[GSoC] Image Selection (#4457)

* Localisation updates from https://translatewiki.net.

* Fixes #4357  After switching to different account, contributions screen shows pictures of previous account (#4421)

* Update UploadMediaDetailFragment.java

* Update LoginActivity.java

Clear CompositeDisposable after logging in successfully. It may help solve the problem of saving the contribution to the previous account

* Revert "Update UploadMediaDetailFragment.java"

This reverts commit b1b4257f20.

Co-authored-by: Obsidian_zero <1198474846@qq.com>

* Remove unnecessary whitespace from a message (#4439)

* Merge v3.0.1 into master (#4446)

* Versioning and changelog for v3.0.0 (#4152)

* Versioning for v3.0.0

* Update changelog.md

* Handled migration 8-9-10 in BookmarksLocationDao (#4154)

* #Fixes #4141
- Handled migrations for BookmarkLocationsDao from 8-9-10

* #Fixes #4141
- Handled migrations for BookmarkLocationsDao from 8-9-10

* Fixes #4179 (#4180)

* Handled null pointer exception in MainActivity->ContributionsFragment#backButtonClicked()
* Updated >ContributionsFragment#backButtonClicked() to handle back press properly

* Fixes #4179 (#4181)

* Handled possible null check on MediaDetails in BookmarkListRootFragment#backPressed()

* Cherrypick for hotfix3.1 (#4205)

* Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list.

Fixes #4159 On Explore Tab, All Available Options on toolbar in media detail view are only targeting the first media in the list.

* fixed bug: App crashes on viewing review in Review Fragment #4132 (#4146)

* fixed bug:app crashes on viewing review in Review Fragment #4135

* Fixed the issue with back button in contribution tab. (#4177)

Co-authored-by: Pratham2305 <Pratham2305@users.noreply.github.com>

* Fixed the issue with back navigation button on toolbar in explore tab. (#4175)

* Fix (#4148) Issues on theme change

* fixed themeChange crashes

* fixed comments

* Overlooked the title bar

Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com>
Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com>
Co-authored-by: Pratham2305 <Pratham2305@users.noreply.github.com>
Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com>

* Fixes #4173 (#4396)

* Fix #4147  Pre-fill desc in Nearby uploads with Wikidata item's label + description (#4390)

* Update query to fetch descriptions

* Make description added to NearbyResultItem

* Make string operations to display description and label in a combined way

* Fix reviews, remove long description from list and swap label and description texts

* Fix repeated information issue

* Fix double information issue

* fix style issues

* Remove douplicated information

* Changes made (#4354)

* Remove nonexistent method

* Fix #4283 IllegalStateException (#4440)

* Fix #4283 IllegalStateException

* Fix flickering issue

* Versioning for v3.0.1

* Update changelog.md

Co-authored-by: Ashish <ashishkumar468@gmail.com>
Co-authored-by: neslihanturan <tur.neslihan@gmail.com>
Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com>
Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com>
Co-authored-by: Pratham2305 <Pratham2305@users.noreply.github.com>
Co-authored-by: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com>
Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com>
Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com>

* Localisation updates from https://translatewiki.net.

* Added basic Fetch

* added permission request

* Folder count rectified

* Loaded thumbnail

* disabled overlay

* Added sha1 function

* Documented the code

* Added a feature for editing coordinates (#4418)

* not

* Place Picker added

* Pick location and API call linked

* minor warnings resolved

* Code conventions followed

* issue fixed

* Wikitext edited properly

* minor modification

* Location Picker added

* Bottom sheet removed

* Location picker fully implemented

* credit added

* credit added

* issues fixed

* issues fixed

* minor issue fixed

* Some build issues occured merging release v3.0 are fixed. One paranthesis issue is solved, a method about UploadService is removed, since we don't use it anymore. (#4451)

* Localisation updates from https://translatewiki.net.

* Fixes 4344 - Duplicate Uploads (#4442)

* Fixes 4344
- Update the retention policy of the Work Manager to ExistingWorkPolicy.APPEND_OR_REPLACE- which would append the new work to the end of existing one. This helps remove the while loop in UploadWorker which was meant to handle the cases where a new worker would be created for retries. The while loop seemed to have race conditions uploading duplicate entries.

* Update states to IN_PROGRESS before uploads are processed

* Image selection added

* Forwarded activity result to upload wizard

* Initialised xmls, made folder and image item.

* xmls done

* xmls completed

* removed unwanted attribute

* Created models, adapters and view models (#4441)

* created models, adapters and view models

* Added Image Fragment

* back button linked

* Documentation and refractor

* spaces

* Butterknife annotation

* DiffUtil

* Added Examples

* Extended Custom selector From Base Activity

* made view model injectable

* Added basic Fetch

* added permission request

* Folder count rectified

* Loaded thumbnail

* disabled overlay

* Added sha1 function

* Documented the code

* Image selection added

* Forwarded activity result to upload wizard

* [GSOC] Added Image Fetch (#4449)

* Added basic Fetch

* added permission request

* Folder count rectified

* Loaded thumbnail

* disabled overlay

* Added sha1 function

* Documented the code

* fixed merge errors

* Documented the remaining function

Co-authored-by: translatewiki.net <l10n-bot@translatewiki.net>
Co-authored-by: obsidian-zero <63155026+obsidian-zero@users.noreply.github.com>
Co-authored-by: Obsidian_zero <1198474846@qq.com>
Co-authored-by: Amir E. Aharoni <amir.aharoni@mail.huji.ac.il>
Co-authored-by: Josephine Lim <josephinelim86@gmail.com>
Co-authored-by: Ashish <ashishkumar468@gmail.com>
Co-authored-by: neslihanturan <tur.neslihan@gmail.com>
Co-authored-by: Pratham Pahariya <54663429+Pratham2305@users.noreply.github.com>
Co-authored-by: Shabir Ahmad <56585337+shabar-shab@users.noreply.github.com>
Co-authored-by: Pratham2305 <Pratham2305@users.noreply.github.com>
Co-authored-by: Madhur Gupta <30932899+madhurgupta10@users.noreply.github.com>
Co-authored-by: Vinayak Aggarwal <56196007+vinayak0505@users.noreply.github.com>
Co-authored-by: Ayan Sarkar <71203077+Ayan-10@users.noreply.github.com>
This commit is contained in:
Aditya-Srivastav 2021-06-17 14:59:27 +05:30 committed by GitHub
parent aed4bc8ebf
commit 746a660028
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
74 changed files with 1397 additions and 155 deletions

View file

@ -1,5 +1,10 @@
# Wikimedia Commons for Android
## v3.0.1
- Pre-fill desc in Nearby uploads with Wikidata item's label + description
- Improved ACRA crash reporting
- Fixed various crashes
## v3.0.0
- Added Structured Data to upload workflow, users can now add depicts
- Added Leaderboard in Achievements screen

View file

@ -39,7 +39,7 @@ dependencies {
implementation 'fr.avianey.com.viewpagerindicator:library:2.4.1.1@aar'
implementation 'com.github.chrisbanes:PhotoView:2.0.0'
implementation 'com.github.pedrovgs:renderers:3.3.3'
implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:8.6.2'
implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:9.1.0'
implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-localization-v8:0.11.0'
implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-scalebar-v9:0.4.0'
implementation 'com.github.deano2390:MaterialShowcaseView:1.2.0'
@ -58,8 +58,8 @@ dependencies {
implementation "com.squareup.okhttp3:okhttp-ws:$OKHTTP_VERSION"
// Logging
implementation 'ch.acra:acra-dialog:5.3.0'
implementation 'ch.acra:acra-mail:5.3.0'
implementation 'ch.acra:acra-dialog:5.8.1-beta11'
implementation 'ch.acra:acra-mail:5.8.1-beta11'
implementation 'org.slf4j:slf4j-api:1.7.25'
api('com.github.tony19:logback-android-classic:1.1.1-6') {
exclude group: 'com.google.android', module: 'android'
@ -153,8 +153,9 @@ android {
defaultConfig {
//applicationId 'fr.free.nrw.commons'
versionCode 876
versionName '3.0.0'
versionCode 1016
versionName '3.0.1'
setProperty("archivesBaseName", "app-commons-v$versionName-" + getBranchName())
minSdkVersion 19

View file

@ -140,7 +140,11 @@
android:name=".review.ReviewActivity"
android:label="@string/title_activity_review" />
<service
<activity
android:name=".LocationPicker.LocationPickerActivity"
android:label="Location Picker" />
<service
android:name=".auth.WikiAccountAuthenticatorService"
android:exported="true"
android:process=":auth">

View file

@ -70,7 +70,8 @@ import timber.log.Timber;
)
@AcraMailSender(
mailTo = "commons-app-android-private@googlegroups.com"
mailTo = "commons-app-android-private@googlegroups.com",
reportAsFile = false
)
@AcraDialog(

View file

@ -0,0 +1,54 @@
package fr.free.nrw.commons.LocationPicker;
import android.app.Activity;
import android.content.Intent;
import androidx.annotation.NonNull;
import com.mapbox.mapboxsdk.camera.CameraPosition;
/**
* Helper class for starting the activity
*/
public final class LocationPicker {
/**
* Getting camera position from the intent using constants
* @param data intent
* @return CameraPosition
*/
public static CameraPosition getCameraPosition(final Intent data) {
return data.getParcelableExtra(LocationPickerConstants.MAP_CAMERA_POSITION);
}
public static class IntentBuilder {
private final Intent intent;
/**
* Creates a new builder that creates an intent to launch the place picker activity.
*/
public IntentBuilder() {
intent = new Intent();
}
/**
* Gets and puts location in intent
* @param position CameraPosition
* @return LocationPicker.IntentBuilder
*/
public LocationPicker.IntentBuilder defaultLocation(
final CameraPosition position) {
intent.putExtra(LocationPickerConstants.MAP_CAMERA_POSITION, position);
return this;
}
/**
* Gets and sets the activity
* @param activity Activity
* @return Intent
*/
public Intent build(final Activity activity) {
intent.setClass(activity, LocationPickerActivity.class);
return intent;
}
}
}

View file

@ -0,0 +1,288 @@
package fr.free.nrw.commons.LocationPicker;
import android.content.Intent;
import android.os.Bundle;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.view.Window;
import android.view.animation.OvershootInterpolator;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.AppCompatTextView;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.mapbox.android.core.permissions.PermissionsManager;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.camera.CameraPosition.Builder;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.location.LocationComponent;
import com.mapbox.mapboxsdk.location.LocationComponentActivationOptions;
import com.mapbox.mapboxsdk.location.modes.CameraMode;
import com.mapbox.mapboxsdk.location.modes.RenderMode;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraIdleListener;
import com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.Style;
import com.mapbox.mapboxsdk.maps.UiSettings;
import fr.free.nrw.commons.R;
import org.jetbrains.annotations.NotNull;
import timber.log.Timber;
/**
* Helps to pick location and return the result with an intent
*/
public class LocationPickerActivity extends AppCompatActivity implements OnMapReadyCallback,
OnCameraMoveStartedListener, OnCameraIdleListener, Observer<CameraPosition> {
/**
* cameraPosition : position of picker
*/
private CameraPosition cameraPosition;
/**
* markerImage : picker image
*/
private ImageView markerImage;
/**
* mapboxMap : map
*/
private MapboxMap mapboxMap;
/**
* mapView : view of the map
*/
private MapView mapView;
/**
* tvAttribution : credit
*/
private AppCompatTextView tvAttribution;
@Override
protected void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
final ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.hide();
}
setContentView(R.layout.activity_location_picker);
if (savedInstanceState == null) {
cameraPosition = getIntent().getParcelableExtra(LocationPickerConstants.MAP_CAMERA_POSITION);
}
final LocationPickerViewModel viewModel = new ViewModelProvider(this)
.get(LocationPickerViewModel.class);
viewModel.getResult().observe(this, this);
bindViews();
addBackButtonListener();
addPlaceSelectedButton();
addCredits();
getToolbarUI();
mapView.onCreate(savedInstanceState);
mapView.getMapAsync(this);
}
/**
* For showing credits
*/
private void addCredits() {
tvAttribution.setText(Html.fromHtml(getString(R.string.map_attribution)));
tvAttribution.setMovementMethod(LinkMovementMethod.getInstance());
}
/**
* Clicking back button destroy locationPickerActivity
*/
private void addBackButtonListener() {
final ImageView backButton = findViewById(R.id.mapbox_place_picker_toolbar_back_button);
backButton.setOnClickListener(view -> finish());
}
/**
* Binds mapView and location picker icon
*/
private void bindViews() {
mapView = findViewById(R.id.map_view);
markerImage = findViewById(R.id.location_picker_image_view_marker);
tvAttribution = findViewById(R.id.tv_attribution);
}
/**
* Binds the listeners
*/
private void bindListeners() {
mapboxMap.addOnCameraMoveStartedListener(
this);
mapboxMap.addOnCameraIdleListener(
this);
}
/**
* Gets toolbar color
*/
private void getToolbarUI() {
final ConstraintLayout toolbar = findViewById(R.id.location_picker_toolbar);
toolbar.setBackgroundColor(getResources().getColor(R.color.primaryColor));
}
/**
* Takes action when map is ready to show
* @param mapboxMap map
*/
@Override
public void onMapReady(final MapboxMap mapboxMap) {
this.mapboxMap = mapboxMap;
mapboxMap.setStyle(Style.MAPBOX_STREETS, style -> {
adjustCameraBasedOnOptions();
bindListeners();
enableLocationComponent(style);
});
}
/**
* move the location to the current media coordinates
*/
private void adjustCameraBasedOnOptions() {
mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
}
/**
* Enables location components
* @param loadedMapStyle Style
*/
@SuppressWarnings( {"MissingPermission"})
private void enableLocationComponent(@NonNull final Style loadedMapStyle) {
final UiSettings uiSettings = mapboxMap.getUiSettings();
uiSettings.setAttributionEnabled(false);
// Check if permissions are enabled and if not request
if (PermissionsManager.areLocationPermissionsGranted(this)) {
// Get an instance of the component
final LocationComponent locationComponent = mapboxMap.getLocationComponent();
// Activate with options
locationComponent.activateLocationComponent(
LocationComponentActivationOptions.builder(this, loadedMapStyle).build());
// Enable to make component visible
locationComponent.setLocationComponentEnabled(true);
// Set the component's camera mode
locationComponent.setCameraMode(CameraMode.NONE);
// Set the component's render mode
locationComponent.setRenderMode(RenderMode.NORMAL);
}
}
/**
* Acts on camera moving
* @param reason int
*/
@Override
public void onCameraMoveStarted(final int reason) {
Timber.v("Map camera has begun moving.");
if (markerImage.getTranslationY() == 0) {
markerImage.animate().translationY(-75)
.setInterpolator(new OvershootInterpolator()).setDuration(250).start();
}
}
/**
* Acts on camera idle
*/
@Override
public void onCameraIdle() {
Timber.v("Map camera is now idling.");
markerImage.animate().translationY(0)
.setInterpolator(new OvershootInterpolator()).setDuration(250).start();
}
/**
* Takes action on camera position
* @param position position of picker
*/
@Override
public void onChanged(@Nullable CameraPosition position) {
if (position == null) {
position = new Builder()
.target(new LatLng(mapboxMap.getCameraPosition().target.getLatitude(),
mapboxMap.getCameraPosition().target.getLongitude()))
.zoom(16).build();
}
cameraPosition = position;
}
/**
* Select the preferable location
*/
private void addPlaceSelectedButton() {
final FloatingActionButton placeSelectedButton = findViewById(R.id.location_chosen_button);
placeSelectedButton.setOnClickListener(view -> placeSelected());
}
/**
* Return the intent with required data
*/
void placeSelected() {
final Intent returningIntent = new Intent();
returningIntent.putExtra(LocationPickerConstants.MAP_CAMERA_POSITION,
mapboxMap.getCameraPosition());
setResult(AppCompatActivity.RESULT_OK, returningIntent);
finish();
}
@Override
protected void onStart() {
super.onStart();
mapView.onStart();
}
@Override
protected void onResume() {
super.onResume();
mapView.onResume();
}
@Override
protected void onPause() {
super.onPause();
mapView.onPause();
}
@Override
protected void onStop() {
super.onStop();
mapView.onStop();
}
@Override
protected void onSaveInstanceState(final @NotNull Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
@Override
protected void onDestroy() {
super.onDestroy();
mapView.onDestroy();
}
@Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}
}

View file

@ -0,0 +1,13 @@
package fr.free.nrw.commons.LocationPicker;
/**
* Constants need for location picking
*/
public final class LocationPickerConstants {
public static final String MAP_CAMERA_POSITION
= "location.picker.cameraPosition";
private LocationPickerConstants() {
}
}

View file

@ -0,0 +1,60 @@
package fr.free.nrw.commons.LocationPicker;
import android.app.Application;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.MutableLiveData;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import org.jetbrains.annotations.NotNull;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import timber.log.Timber;
/**
* Observes live camera position data
*/
public class LocationPickerViewModel extends AndroidViewModel implements Callback<CameraPosition> {
/**
* Wrapping CameraPosition with MutableLiveData
*/
private final MutableLiveData<CameraPosition> result = new MutableLiveData<>();
/**
* Constructor for this class
* @param application Application
*/
public LocationPickerViewModel(@NonNull final Application application) {
super(application);
}
/**
* Responses on camera position changing
* @param call Call<CameraPosition>
* @param response Response<CameraPosition>
*/
@Override
public void onResponse(final @NotNull Call<CameraPosition> call,
final Response<CameraPosition> response) {
if(response.body()==null){
result.setValue(null);
return;
}
result.setValue(response.body());
}
@Override
public void onFailure(final @NotNull Call<CameraPosition> call, final @NotNull Throwable t) {
Timber.e(t);
}
/**
* Gets live CameraPosition
* @return MutableLiveData<CameraPosition>
*/
public MutableLiveData<CameraPosition> getResult() {
return result;
}
}

View file

@ -74,7 +74,7 @@ class Media constructor(
* Gets the coordinates of where the file was created.
* @return file coordinates as a LatLng
*/
val coordinates: LatLng? = null,
var coordinates: LatLng? = null,
val captions: Map<String, String> = emptyMap(),
val descriptions: Map<String, String> = emptyMap(),
val depictionIds: List<String> = emptyList()

View file

@ -1,6 +1,7 @@
package fr.free.nrw.commons.actions
import io.reactivex.Observable
import io.reactivex.Single
import org.wikipedia.csrf.CsrfTokenClient
/**
@ -62,4 +63,15 @@ class PageEditClient(
Observable.just(false)
}
}
/**
* Get whole WikiText of required file
* @param title : Name of the file
* @return Observable<MwQueryResult>
*/
fun getCurrentWikiText(title: String): Single<String?> {
return pageEditInterface.getWikiText(title).map {
it.query()?.pages()?.get(0)?.revisions()?.get(0)?.content()
}
}
}

View file

@ -1,12 +1,11 @@
package fr.free.nrw.commons.actions
import io.reactivex.Observable
import io.reactivex.Single
import org.wikipedia.dataclient.Service
import org.wikipedia.dataclient.mwapi.MwQueryResponse
import org.wikipedia.edit.Edit
import retrofit2.http.Field
import retrofit2.http.FormUrlEncoded
import retrofit2.http.Headers
import retrofit2.http.POST
import retrofit2.http.*
/**
* This interface facilitates wiki commons page editing services to the Networking module
@ -73,4 +72,17 @@ interface PageEditInterface {
@Field("prependtext") prependText: String,
@Field("token") token: String
): Observable<Edit>
/**
* Get wiki text for provided file names
* @param titles : Name of the file
* @return Single<MwQueryResult>
*/
@GET(
Service.MW_API_PREFIX +
"action=query&prop=revisions&rvprop=content|timestamp&rvlimit=1&converttitles="
)
fun getWikiText(
@Query("titles") title: String
): Single<MwQueryResponse?>
}

View file

@ -339,6 +339,7 @@ public class LoginActivity extends AccountAuthenticatorActivity {
// no longer attached to activity!
return;
}
compositeDisposable.clear();
sessionManager.setUserLoggedIn(true);
AppAdapter.get().updateAccount(loginResult);
progressDialog.dismiss();

View file

@ -269,7 +269,7 @@ public class BookmarkLocationsDao {
onUpdate(db, from, to);
return;
}
if (from == 8) {
if (from < 10) {
from++;
onUpdate(db, from, to);
return;

View file

@ -8,7 +8,6 @@ import android.content.Context;
import android.content.Intent;
import androidx.annotation.NonNull;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.customselector.ui.selector.CustomSelectorActivity;
import fr.free.nrw.commons.filepicker.DefaultCallback;
import fr.free.nrw.commons.filepicker.FilePicker;
import fr.free.nrw.commons.filepicker.FilePicker.ImageSource;
@ -63,16 +62,11 @@ public class ContributionController {
* Initiate gallery picker with permission
*/
public void initiateCustomGalleryPickWithPermission(final Activity activity) {
boolean useExtStorage = defaultKvStore.getBoolean("useExternalStorage", true);
Intent intent = new Intent(activity,CustomSelectorActivity.class);
if (!useExtStorage) {
activity.startActivity(intent);
return;
}
setPickerConfiguration(activity,true);
PermissionUtils.checkPermissionsAndPerformAction(activity,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
() -> activity.startActivity(intent),
() -> FilePicker.openCustomSelector(activity, 0),
R.string.storage_permission_title,
R.string.write_storage_permission_rationale);
}

View file

@ -6,6 +6,7 @@ import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
@ -24,6 +25,14 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager.OnBackStackChangedListener;
import androidx.fragment.app.FragmentTransaction;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.notification.Notification;
import fr.free.nrw.commons.notification.NotificationController;
import fr.free.nrw.commons.theme.BaseActivity;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
import butterknife.BindView;
import butterknife.ButterKnife;
import fr.free.nrw.commons.CommonsApplication;
@ -531,6 +540,13 @@ public class ContributionsFragment
presenter.onDetachView();
}
@Override
public void notifyDataSetChanged() {
if (mediaDetailPagerFragment != null) {
mediaDetailPagerFragment.notifyDataSetChanged();
}
}
/**
* Retry upload when it is failed
*
@ -604,12 +620,8 @@ public class ContributionsFragment
return contributionsListFragment.getContributionStateAt(position);
}
public void backButtonClicked() {
if (mediaDetailPagerFragment.isVisible()) {
if(mediaDetailPagerFragment.backButtonClicked()) {
// MediaDetailed handled the backPressed no further action required.
return;
}
public boolean backButtonClicked() {
if (null != mediaDetailPagerFragment && mediaDetailPagerFragment.isVisible()) {
if (store.getBoolean("displayNearbyCardView", true)) {
if (nearbyNotificationCardView.cardViewVisibilityState == NearbyNotificationCardView.CardViewVisibilityState.READY) {
nearbyNotificationCardView.setVisibility(View.VISIBLE);
@ -622,7 +634,9 @@ public class ContributionsFragment
((BaseActivity)getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(false);
((MainActivity)getActivity()).showTabs();
fetchCampaigns();
return true;
}
return false;
}
// Getter for mediaDetailPagerFragment

View file

@ -104,6 +104,8 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
private final int SPAN_COUNT_LANDSCAPE = 3;
private final int SPAN_COUNT_PORTRAIT = 1;
private int contributionsSize;
@Override
public View onCreateView(
@ -154,7 +156,11 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
}
contributionsListPresenter.setup();
contributionsListPresenter.contributionList.observe(this.getViewLifecycleOwner(), adapter::submitList);
contributionsListPresenter.contributionList.observe(this.getViewLifecycleOwner(), list -> {
contributionsSize = list.size();
adapter.submitList(list);
callback.notifyDataSetChanged();
});
rvContributionsList.setAdapter(adapter);
adapter.registerAdapterDataObserver(new AdapterDataObserver() {
@Override
@ -405,10 +411,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
}
public int getTotalMediaCount() {
if(adapter != null) {
return adapter.getItemCount();
}
return 0;
return contributionsSize;
}
/**
@ -435,6 +438,8 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
public interface Callback {
void notifyDataSetChanged();
void retryUpload(Contribution contribution);
void showDetail(int position, boolean isWikipediaButtonDisplayed);

View file

@ -99,7 +99,9 @@ public class MainActivity extends BaseActivity
@Override
public boolean onSupportNavigateUp() {
if (activeFragment == ActiveFragment.CONTRIBUTIONS) {
contributionsFragment.backButtonClicked();
if (!contributionsFragment.backButtonClicked()) {
return false;
}
} else {
onBackPressed();
showTabs();
@ -264,16 +266,10 @@ public class MainActivity extends BaseActivity
@Override
public void onBackPressed() {
if (contributionsFragment != null && activeFragment == ActiveFragment.CONTRIBUTIONS) {
// Meas that contribution fragment is visible
mediaDetailPagerFragment=contributionsFragment.getMediaDetailPagerFragment();
if (mediaDetailPagerFragment ==null) { //means you open the app currently and not open mediaDetailPage fragment
// Means that contribution fragment is visible
if (!contributionsFragment.backButtonClicked()) {//If this one does not wan't to handle
// the back press, let the activity do so
super.onBackPressed();
} else if (mediaDetailPagerFragment!=null) {
if(!mediaDetailPagerFragment.isVisible()){ //means you are at contributions fragement
super.onBackPressed();
} else { //mean you are at mediaDetailPager Fragment
contributionsFragment.backButtonClicked();
}
}
} else if (nearbyParentFragment != null && activeFragment == ActiveFragment.NEARBY) {
// Means that nearby fragment is visible
@ -322,7 +318,7 @@ public class MainActivity extends BaseActivity
} else {
WorkManager.getInstance(getApplicationContext()).enqueueUniqueWork(
UploadWorker.class.getSimpleName(),
ExistingWorkPolicy.KEEP, OneTimeWorkRequest.from(UploadWorker.class));
ExistingWorkPolicy.APPEND_OR_REPLACE, OneTimeWorkRequest.from(UploadWorker.class));
viewUtilWrapper
.showShortToast(getBaseContext(), getString(R.string.limited_connection_disabled));

View file

@ -0,0 +1,197 @@
package fr.free.nrw.commons.coordinates;
import static fr.free.nrw.commons.notification.NotificationHelper.NOTIFICATION_EDIT_COORDINATES;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import fr.free.nrw.commons.BuildConfig;
import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.actions.PageEditClient;
import fr.free.nrw.commons.notification.NotificationHelper;
import fr.free.nrw.commons.utils.ViewUtilWrapper;
import io.reactivex.Observable;
import io.reactivex.Single;
import io.reactivex.schedulers.Schedulers;
import java.util.Objects;
import javax.inject.Inject;
import javax.inject.Named;
import org.apache.commons.lang3.StringUtils;
import timber.log.Timber;
/**
* Helper class for edit and update given coordinates and showing notification about new coordinates
* upgradation
*/
public class CoordinateEditHelper {
/**
* notificationHelper: helps creating notification
*/
private final NotificationHelper notificationHelper;
/**
* * pageEditClient: methods provided by this member posts the edited coordinates
* to the Media wiki api
*/
public final PageEditClient pageEditClient;
/**
* viewUtil: helps to show Toast
*/
private final ViewUtilWrapper viewUtil;
@Inject
public CoordinateEditHelper(final NotificationHelper notificationHelper,
@Named("commons-page-edit") final PageEditClient pageEditClient,
final ViewUtilWrapper viewUtil) {
this.notificationHelper = notificationHelper;
this.pageEditClient = pageEditClient;
this.viewUtil = viewUtil;
}
/**
* Public interface to edit coordinates
* @param context to be added
* @param media to be added
* @param Accuracy to be added
* @return Single<Boolean>
*/
public Single<Boolean> makeCoordinatesEdit(final Context context, final Media media,
final String Latitude, final String Longitude, final String Accuracy) {
viewUtil.showShortToast(context,
context.getString(R.string.coordinates_edit_helper_make_edit_toast));
return addCoordinates(media, Latitude, Longitude, Accuracy)
.flatMapSingle(result -> Single.just(showCoordinatesEditNotification(context, media,
Latitude, Longitude, Accuracy, result)))
.firstOrError();
}
/**
* Replaces new coordinates
* @param media to be added
* @param Latitude to be added
* @param Longitude to be added
* @param Accuracy to be added
* @return Observable<Boolean>
*/
private Observable<Boolean> addCoordinates(final Media media, final String Latitude,
final String Longitude, final String Accuracy) {
Timber.d("thread is coordinates adding %s", Thread.currentThread().getName());
final String summary = "Adding Coordinates";
final StringBuilder buffer = new StringBuilder();
final String wikiText = pageEditClient.getCurrentWikiText(media.getFilename())
.subscribeOn(Schedulers.io())
.blockingGet();
if (Latitude != null) {
buffer.append("\n{{Location|").append(Latitude).append("|").append(Longitude)
.append("|").append(Accuracy).append("}}");
}
final String editedLocation = buffer.toString();
final String appendText = getFormattedWikiText(wikiText, editedLocation);
return pageEditClient.edit(Objects.requireNonNull(media.getFilename())
, appendText, summary);
}
/**
* Helps to get formatted wikitext with upgraded location
* @param wikiText current wikitext
* @param editedLocation new location
* @return String
*/
private String getFormattedWikiText(final String wikiText, final String editedLocation){
if (wikiText.contains("filedesc") && wikiText.contains("Location")) {
final String fromLocationToEnd = wikiText.substring(wikiText.indexOf("{{Location"));
final String firstHalf = wikiText.substring(0, wikiText.indexOf("{{Location"));
final String lastHalf = fromLocationToEnd.substring(
fromLocationToEnd.indexOf("}}") + 2);
final int startOfSecondSection = StringUtils.ordinalIndexOf(wikiText,
"==", 3);
final StringBuilder buffer = new StringBuilder();
if (wikiText.charAt(wikiText.indexOf("{{Location")-1) == '\n') {
buffer.append(editedLocation.substring(1));
} else {
buffer.append(editedLocation);
}
if (startOfSecondSection != -1 && wikiText.charAt(startOfSecondSection-1)!= '\n') {
buffer.append("\n");
}
return firstHalf + buffer + lastHalf;
}
if (wikiText.contains("filedesc") && !wikiText.contains("Location")) {
final int startOfSecondSection = StringUtils.ordinalIndexOf(wikiText,
"==", 3);
if (startOfSecondSection != -1) {
final String firstHalf = wikiText.substring(0, startOfSecondSection);
final String lastHalf = wikiText.substring(startOfSecondSection);
final String buffer = editedLocation.substring(1)
+ "\n";
return firstHalf + buffer + lastHalf;
}
return wikiText + editedLocation;
}
if (!wikiText.contains("filedesc") && !wikiText.contains("Location")) {
return "== {{int:filedesc}} ==" + editedLocation + wikiText;
}
if (!wikiText.contains("filedesc") && wikiText.contains("Location")) {
return "== {{int:filedesc}} ==" + editedLocation + wikiText;
}
return null;
}
/**
* Update coordinates and shows notification about coordinates update
* @param context to be added
* @param media to be added
* @param latitude to be added
* @param longitude to be added
* @param Accuracy to be added
* @param result to be added
* @return boolean
*/
private boolean showCoordinatesEditNotification(final Context context, final Media media,
final String latitude, final String longitude, final String Accuracy,
final boolean result) {
final String message;
String title = context.getString(R.string.coordinates_edit_helper_show_edit_title);
if (result) {
media.setCoordinates(
new fr.free.nrw.commons.location.LatLng(Double.parseDouble(latitude),
Double.parseDouble(longitude),
Float.parseFloat(Accuracy)));
title += ": " + context
.getString(R.string.coordinates_edit_helper_show_edit_title_success);
final StringBuilder coordinatesInMessage = new StringBuilder();
final String mediaCoordinate = String.valueOf(media.getCoordinates());
coordinatesInMessage.append(mediaCoordinate);
message = context.getString(R.string.coordinates_edit_helper_show_edit_message,
coordinatesInMessage.toString());
} else {
title += ": " + context.getString(R.string.coordinates_edit_helper_show_edit_title);
message = context.getString(R.string.coordinates_edit_helper_edit_message_else) ;
}
final String urlForFile = BuildConfig.COMMONS_URL + "/wiki/" + media.getFilename();
final Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(urlForFile));
notificationHelper.showNotification(context, title, message, NOTIFICATION_EDIT_COORDINATES,
browserIntent);
return result;
}
}

View file

@ -11,7 +11,7 @@ import kotlin.collections.ArrayList
import kotlin.collections.LinkedHashMap
/**
* Image Helper object, includes all the static functions required by custom selector
* Image Helper object, includes all the static functions required by custom selector.
*/
object ImageHelper {
@ -49,6 +49,34 @@ object ImageHelper {
return filteredImages
}
/**
* getIndex: Returns the index of image in given list.
*/
fun getIndex(list: ArrayList<Image>, image: Image): Int {
return list.indexOf(image)
}
/**
* Gets the list of indices from the master list.
*/
fun getIndexList(list: ArrayList<Image>, masterList: ArrayList<Image>): ArrayList<Int> {
/**
* TODO
* Can be optimised as masterList is sorted by time.
*/
val indexes = arrayListOf<Int>()
for(image in list) {
val index = getIndex(masterList,image)
if (index == -1) {
continue
}
indexes.add(index)
}
return indexes
}
/**
* Generates the file sha1 from file input stream.
*/

View file

@ -10,6 +10,7 @@ import androidx.constraintlayout.widget.Group
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import fr.free.nrw.commons.customselector.helper.ImageHelper
import fr.free.nrw.commons.customselector.listeners.ImageSelectListener
import fr.free.nrw.commons.customselector.model.Image
@ -26,6 +27,16 @@ class ImageAdapter(
RecyclerViewAdapter<ImageAdapter.ImageViewHolder>(context) {
/**
* ImageSelectedOrUpdated payload class.
*/
class ImageSelectedOrUpdated
/**
* ImageUnselected payload class.
*/
class ImageUnselected
/**
* Currently selected images.
*/
@ -49,18 +60,41 @@ class ImageAdapter(
*/
override fun onBindViewHolder(holder: ImageViewHolder, position: Int) {
val image=images[position]
// todo load image thumbnail, set selected view.
val selectedIndex = ImageHelper.getIndex(selectedImages,image)
val isSelected = selectedIndex != -1
if(isSelected){
holder.itemSelected(selectedIndex+1)
}
else {
holder.itemUnselected();
}
Glide.with(context).load(image.uri).into(holder.image)
holder.itemView.setOnClickListener {
selectOrRemoveImage(image, position)
selectOrRemoveImage(holder, position)
}
}
/**
* Handle click event on an image, update counter on images.
*/
private fun selectOrRemoveImage(image:Image, position:Int){
// todo select the image if not selected and remove it if already selected
private fun selectOrRemoveImage(holder:ImageViewHolder, position:Int){
val clickedIndex = ImageHelper.getIndex(selectedImages,images[position])
if (clickedIndex != -1) {
selectedImages.removeAt(clickedIndex)
notifyItemChanged(position,ImageUnselected())
val indexes = ImageHelper.getIndexList(selectedImages, images)
for (index in indexes) {
notifyItemChanged(index, ImageSelectedOrUpdated())
}
} else {
/**
* TODO
* Show toast on tapping an uploaded item.
*/
selectedImages.add(images[position])
notifyItemChanged(position, ImageSelectedOrUpdated())
}
imageSelectListener.onSelectedImagesChanged(selectedImages)
}
/**
@ -90,9 +124,39 @@ class ImageAdapter(
*/
class ImageViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
val image: ImageView = itemView.findViewById(R.id.image_thumbnail)
val selectedNumber: TextView = itemView.findViewById(R.id.selected_count)
val uploadedGroup: Group = itemView.findViewById(R.id.uploaded_group)
val selectedGroup: Group = itemView.findViewById(R.id.selected_group)
private val selectedNumber: TextView = itemView.findViewById(R.id.selected_count)
private val uploadedGroup: Group = itemView.findViewById(R.id.uploaded_group)
private val selectedGroup: Group = itemView.findViewById(R.id.selected_group)
/**
* Item selected view.
*/
fun itemSelected(index: Int) {
selectedGroup.visibility = View.VISIBLE
selectedNumber.text = index.toString()
}
/**
* Item Unselected view.
*/
fun itemUnselected() {
selectedGroup.visibility = View.GONE
}
/**
* Item Uploaded view.
*/
fun itemUploaded() {
uploadedGroup.visibility = View.VISIBLE
}
/**
* Item Not Uploaded view.
*/
fun itemNotUploaded() {
uploadedGroup.visibility = View.GONE
}
}
/**

View file

@ -1,5 +1,7 @@
package fr.free.nrw.commons.customselector.ui.selector
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.widget.ImageButton
import android.widget.TextView
@ -10,6 +12,7 @@ import fr.free.nrw.commons.customselector.listeners.ImageSelectListener
import fr.free.nrw.commons.customselector.model.Folder
import fr.free.nrw.commons.customselector.model.Image
import fr.free.nrw.commons.theme.BaseActivity
import java.io.File
import javax.inject.Inject
class CustomSelectorActivity : BaseActivity(), FolderClickListener, ImageSelectListener {
@ -73,7 +76,8 @@ class CustomSelectorActivity : BaseActivity(), FolderClickListener, ImageSelectL
val back : ImageButton = findViewById(R.id.back)
back.setOnClickListener { onBackPressed() }
// todo done listener.
val done : ImageButton = findViewById(R.id.done)
done.setOnClickListener { onDone() }
}
/**
@ -91,9 +95,44 @@ class CustomSelectorActivity : BaseActivity(), FolderClickListener, ImageSelectL
* override Selected Images Change, update view model selected images.
*/
override fun onSelectedImagesChanged(selectedImages: ArrayList<Image>) {
viewModel.selectedImages.value = selectedImages
// todo update selected images in view model.
}
/**
* OnDone clicked.
* Get the selected images. Remove any non existent file, forward the data to finish selector.
*/
fun onDone() {
val selectedImages = viewModel.selectedImages.value
if(selectedImages.isNullOrEmpty()) {
finishPickImages(arrayListOf())
return
}
var i = 0
while (i < selectedImages.size) {
val path = selectedImages[i].path
val file = File(path)
if (!file.exists()) {
selectedImages.removeAt(i)
i--
}
i++
}
finishPickImages(selectedImages)
}
/**
* finishPickImages, Load the data to the intent and set result.
* Finish the activity.
*/
private fun finishPickImages(images: ArrayList<Image>) {
val data = Intent()
data.putParcelableArrayListExtra("Images", images)
setResult(Activity.RESULT_OK, data)
finish()
}
/**
* Back pressed.
* Change toolbar title.
@ -106,16 +145,4 @@ class CustomSelectorActivity : BaseActivity(), FolderClickListener, ImageSelectL
}
}
/**
*
* TODO
* Permission check.
* OnDone
* Activity Result.
*
*
*/
}

View file

@ -1,7 +1,6 @@
package fr.free.nrw.commons.customselector.ui.selector
import android.content.Context
import android.util.Log
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import fr.free.nrw.commons.customselector.listeners.ImageLoaderListener
@ -14,10 +13,18 @@ import kotlinx.coroutines.cancel
class CustomSelectorViewModel(var context: Context,var imageFileLoader: ImageFileLoader) : ViewModel() {
/**
* Scope for coroutine task (image fetch).
*/
private val scope = CoroutineScope(Dispatchers.Main)
/**
* Result Live Data
* Stores selected images.
*/
var selectedImages: MutableLiveData<ArrayList<Image>> = MutableLiveData()
/**
* Result Live Data.
*/
val result = MutableLiveData(Result(CallbackStatus.IDLE, arrayListOf()))

View file

@ -13,7 +13,7 @@ import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao;
public class DBOpenHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "commons.db";
private static final int DATABASE_VERSION = 15;
private static final int DATABASE_VERSION = 14;
public static final String CONTRIBUTIONS_TABLE = "contributions";
private final String DROP_TABLE_STATEMENT="DROP TABLE IF EXISTS %s";

View file

@ -110,9 +110,8 @@ public class ExploreListRootFragment extends CommonsDaggerSupportFragment implem
@Override
public void onMediaClicked(int position) {
container.setVisibility(View.VISIBLE);
((ExploreFragment) getParentFragment()).tabLayout.setVisibility(View.GONE);
((ExploreFragment)getParentFragment()).tabLayout.setVisibility(View.GONE);
mediaDetails = new MediaDetailPagerFragment(false, true);
((ExploreFragment) getParentFragment()).setScroll(false);
setFragment(mediaDetails, listFragment);
mediaDetails.showImage(position);
}

View file

@ -10,6 +10,7 @@ public interface Constants {
int FILE_PICKER_IMAGE_IDENTIFICATOR = 0b1101101100; //876
int SOURCE_CHOOSER = 1 << 15;
int PICK_PICTURE_FROM_CUSTOM_SELECTOR = FILE_PICKER_IMAGE_IDENTIFICATOR + (1 << 10);
int PICK_PICTURE_FROM_DOCUMENTS = FILE_PICKER_IMAGE_IDENTIFICATOR + (1 << 11);
int PICK_PICTURE_FROM_GALLERY = FILE_PICKER_IMAGE_IDENTIFICATOR + (1 << 12);
int TAKE_PICTURE = FILE_PICKER_IMAGE_IDENTIFICATOR + (1 << 13);

View file

@ -15,6 +15,8 @@ import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import fr.free.nrw.commons.customselector.model.Image;
import fr.free.nrw.commons.customselector.ui.selector.CustomSelectorActivity;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
@ -51,6 +53,11 @@ public class FilePicker implements Constants {
.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, configuration(context).allowsMultiplePickingInGallery());
}
private static Intent createCustomSelectorIntent(@NonNull Context context, int type) {
storeType(context, type);
return new Intent(context, CustomSelectorActivity.class);
}
private static Intent createCameraForImageIntent(@NonNull Context context, int type) {
storeType(context, type);
@ -97,6 +104,14 @@ public class FilePicker implements Constants {
activity.startActivityForResult(intent, RequestCodes.PICK_PICTURE_FROM_GALLERY);
}
/**
* Opens Custom Selector
*/
public static void openCustomSelector(Activity activity, int type) {
Intent intent = createCustomSelectorIntent(activity, type);
activity.startActivityForResult(intent, RequestCodes.PICK_PICTURE_FROM_CUSTOM_SELECTOR);
}
/**
* Opens the camera app to pick image clicked by user
*/
@ -135,12 +150,15 @@ public class FilePicker implements Constants {
if (requestCode == RequestCodes.PICK_PICTURE_FROM_GALLERY ||
requestCode == RequestCodes.TAKE_PICTURE ||
requestCode == RequestCodes.CAPTURE_VIDEO ||
requestCode == RequestCodes.PICK_PICTURE_FROM_DOCUMENTS) {
requestCode == RequestCodes.PICK_PICTURE_FROM_DOCUMENTS ||
requestCode == RequestCodes.PICK_PICTURE_FROM_CUSTOM_SELECTOR) {
if (resultCode == Activity.RESULT_OK) {
if (requestCode == RequestCodes.PICK_PICTURE_FROM_DOCUMENTS && !isPhoto(data)) {
onPictureReturnedFromDocuments(data, activity, callbacks);
} else if (requestCode == RequestCodes.PICK_PICTURE_FROM_GALLERY && !isPhoto(data)) {
onPictureReturnedFromGallery(data, activity, callbacks);
} else if (requestCode == RequestCodes.PICK_PICTURE_FROM_CUSTOM_SELECTOR) {
onPictureReturnedFromCustomSelector(data, activity, callbacks);
} else if (requestCode == RequestCodes.TAKE_PICTURE) {
onPictureReturnedFromCamera(activity, callbacks);
} else if (requestCode == RequestCodes.CAPTURE_VIDEO) {
@ -197,6 +215,32 @@ public class FilePicker implements Constants {
}
}
private static void onPictureReturnedFromCustomSelector(Intent data, Activity activity, @NonNull FilePicker.Callbacks callbacks) {
try {
List<UploadableFile> files = getFilesFromCustomSelector(data, activity);
callbacks.onImagesPicked(files, ImageSource.CUSTOM_SELECTOR, restoreType(activity));
} catch (Exception e) {
e.printStackTrace();
callbacks.onImagePickerError(e, ImageSource.CUSTOM_SELECTOR, restoreType(activity));
}
}
private static List<UploadableFile> getFilesFromCustomSelector(Intent data, Activity activity) throws IOException, SecurityException {
List<UploadableFile> files = new ArrayList<>();
ArrayList<Image> images = data.getParcelableArrayListExtra("Images");
for(Image image : images) {
Uri uri = image.getUri();
UploadableFile file = PickedFiles.pickedExistingPicture(activity, uri);
files.add(file);
}
if (configuration(activity).shouldCopyPickedImagesToPublicGalleryAppFolder()) {
PickedFiles.copyFilesInSeparateThread(activity, files);
}
return files;
}
private static void onPictureReturnedFromGallery(Intent data, Activity activity, @NonNull FilePicker.Callbacks callbacks) {
try {
List<UploadableFile> files = getFilesFromGalleryPictures(data, activity);
@ -301,7 +345,7 @@ public class FilePicker implements Constants {
public enum ImageSource {
GALLERY, DOCUMENTS, CAMERA_IMAGE, CAMERA_VIDEO
GALLERY, DOCUMENTS, CAMERA_IMAGE, CAMERA_VIDEO, CUSTOM_SELECTOR
}
public interface Callbacks {

View file

@ -2,12 +2,16 @@ package fr.free.nrw.commons.logging;
import android.content.Context;
import android.os.Bundle;
import javax.inject.Inject;
import javax.inject.Singleton;
import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.utils.ConfigUtils;
import fr.free.nrw.commons.utils.DeviceInfoUtil;
import org.acra.data.CrashReportData;
import org.acra.sender.ReportSenderException;
import org.jetbrains.annotations.NotNull;
/**
* Class responsible for sending logs to developers
@ -87,4 +91,15 @@ public class CommonsLogSender extends LogsSender {
return builder.toString();
}
@Override
public boolean requiresForeground() {
return false;
}
@Override
public void send(@NotNull Context context, @NotNull CrashReportData crashReportData,
@NotNull Bundle bundle) throws ReportSenderException {
}
}

View file

@ -1,5 +1,7 @@
package fr.free.nrw.commons.media;
import static android.app.Activity.RESULT_CANCELED;
import static android.app.Activity.RESULT_OK;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static fr.free.nrw.commons.category.CategoryClientKt.CATEGORY_NEEDING_CATEGORIES;
@ -16,11 +18,8 @@ import android.os.Bundle;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnKeyListener;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
@ -50,6 +49,9 @@ import com.facebook.imagepipeline.image.ImageInfo;
import com.facebook.imagepipeline.request.ImageRequest;
import com.jakewharton.rxbinding2.view.RxView;
import com.jakewharton.rxbinding2.widget.RxSearchView;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.geometry.LatLng;
import fr.free.nrw.commons.LocationPicker.LocationPicker;
import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.MediaDataExtractor;
import fr.free.nrw.commons.R;
@ -62,10 +64,11 @@ import fr.free.nrw.commons.category.CategoryEditHelper;
import fr.free.nrw.commons.category.CategoryEditSearchRecyclerViewAdapter;
import fr.free.nrw.commons.category.CategoryEditSearchRecyclerViewAdapter.Callback;
import fr.free.nrw.commons.contributions.ContributionsFragment;
import fr.free.nrw.commons.coordinates.CoordinateEditHelper;
import fr.free.nrw.commons.delete.DeleteHelper;
import fr.free.nrw.commons.delete.ReasonBuilder;
import fr.free.nrw.commons.explore.depictions.WikidataItemDetailsActivity;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
import fr.free.nrw.commons.explore.depictions.WikidataItemDetailsActivity;
import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.nearby.Label;
import fr.free.nrw.commons.ui.widget.HtmlTextView;
@ -77,8 +80,9 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.inject.Named;
import org.apache.commons.lang3.StringUtils;
@ -88,6 +92,7 @@ import timber.log.Timber;
public class MediaDetailFragment extends CommonsDaggerSupportFragment implements Callback,
CategoryEditHelper.Callback {
private static final int REQUEST_CODE = 1001 ;
private boolean editable;
private boolean isCategoryImage;
private MediaDetailPagerFragment.MediaDetailProvider detailProvider;
@ -125,6 +130,8 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
@Inject
CategoryEditHelper categoryEditHelper;
@Inject
CoordinateEditHelper coordinateEditHelper;
@Inject
ViewUtilWrapper viewUtil;
@Inject
CategoryClient categoryClient;
@ -760,6 +767,75 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
}
}
@OnClick(R.id.coordinate_edit)
public void onUpdateCoordinatesClicked(){
goToLocationPickerActivity();
}
/**
* Start location picker activity with a request code and get the coordinates from the activity.
*/
private void goToLocationPickerActivity() {
/*
If location is not provided in media this coordinates will act as a placeholder in
location picker activity
*/
double defaultLatitude = 37.773972;
double defaultLongitude = -122.431297;
if (media.getCoordinates() != null) {
defaultLatitude = media.getCoordinates().getLatitude();
defaultLongitude = media.getCoordinates().getLongitude();
}
startActivityForResult(new LocationPicker.IntentBuilder()
.defaultLocation(new CameraPosition.Builder()
.target(new LatLng(defaultLatitude, defaultLongitude))
.zoom(16).build())
.build(getActivity()), REQUEST_CODE);
}
/**
* Get the coordinates and update the existing coordinates.
* @param requestCode
* @param resultCode
* @param data
*/
@Override
public void onActivityResult(final int requestCode, final int resultCode,
@Nullable final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
assert data != null;
final CameraPosition cameraPosition = LocationPicker.getCameraPosition(data);
if (cameraPosition != null) {
final String latitude = String.valueOf(cameraPosition.target.getLatitude());
final String longitude = String.valueOf(cameraPosition.target.getLongitude());
final String accuracy = String.valueOf(cameraPosition.target.getAltitude());
String currentLatitude = null;
String currentLongitude = null;
if (media.getCoordinates() != null) {
currentLatitude = String.valueOf(media.getCoordinates().getLatitude());
currentLongitude = String.valueOf(media.getCoordinates().getLongitude());
}
if (!latitude.equals(currentLatitude) || !longitude.equals(currentLongitude)) {
updateCoordinates(latitude, longitude, accuracy);
} else if (media.getCoordinates() == null) {
updateCoordinates(latitude, longitude, accuracy);
}
}
} else if (resultCode == RESULT_CANCELED) {
viewUtil.showShortToast(getContext(),
Objects.requireNonNull(getContext())
.getString(R.string.coordinates_picking_unsuccessful));
}
}
@OnClick(R.id.update_categories_button)
public void onUpdateCategoriesClicked() {
updateCategories(categoryEditSearchRecyclerViewAdapter.getNewCategories());
@ -783,6 +859,24 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
}));
}
/**
* Fetched coordinates are replaced with existing coordinates by a POST API call.
* @param Latitude to be added
* @param Longitude to be added
* @param Accuracy to be added
*/
public void updateCoordinates(final String Latitude, final String Longitude,
final String Accuracy) {
compositeDisposable.add(coordinateEditHelper.makeCoordinatesEdit(getContext(), media,
Latitude, Longitude, Accuracy)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(s -> {
Timber.d("Coordinates are added.");
coordinates.setText(prettyCoordinates(media));
}));
}
@SuppressLint("StringFormatInvalid")
@OnClick(R.id.nominateDeletion)
public void onDeleteButtonClicked(){

View file

@ -6,7 +6,6 @@ import android.annotation.SuppressLint;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@ -101,19 +100,13 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
pager.addOnPageChangeListener(this);
adapter = new MediaDetailAdapter(getChildFragmentManager());
if (((BaseActivity) getActivity()).getSupportActionBar() != null) {
((BaseActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
pager.setAdapter(adapter);
if (savedInstanceState != null) {
final int pageNumber = savedInstanceState.getInt("current-page");
pager.setCurrentItem(pageNumber, false);
getActivity().invalidateOptionsMenu();
adapter.notifyDataSetChanged();
}
adapter.notifyDataSetChanged();
if (getActivity() instanceof MainActivity) {
((MainActivity)getActivity()).hideTabs();
}

View file

@ -63,11 +63,27 @@ public class Place implements Parcelable {
if(!StringUtils.isBlank(itemClass)) {
classEntityId = itemClass.replace("http://www.wikidata.org/entity/", "");
}
// Set description when not null and not empty
String description = (item.getDescription().getValue() != null && !item.getDescription().getValue().isEmpty()) ? item.getDescription().getValue() : "";
// When description is "?" but we have a valid label, just use the label. So replace "?" by "" in description
description = (description.equals("?")
&& (item.getLabel().getValue() != null
&& !item.getLabel().getValue().isEmpty()) ? "" : description);
/*
* If we have a valid label
* - If have a valid label add the description at the end of the string with parenthesis
* - If we don't have a valid label, string will include only the description. So add it without paranthesis
*/
description = ((item.getLabel().getValue() != null && !item.getLabel().getValue().isEmpty())
? item.getLabel().getValue()
+ ((description != null && !description.isEmpty())
? " (" + description + ")" : "")
: description);
return new Place(
item.getLabel().getLanguage(),
item.getLabel().getValue(),
Label.fromText(classEntityId), // list
item.getClassLabel().getValue(), // details
description, // description and label of Wikidata item
PlaceUtils.latLngFromPointString(item.getLocation().getValue()),
item.getCommonsCategory().getValue(),
new Sitelinks.Builder()

View file

@ -51,7 +51,8 @@ fun placeAdapterDelegate(
tvDesc.setText(R.string.no_description_found)
tvDesc.visibility = INVISIBLE
} else {
tvDesc.text = descriptionText
// Remove the label and display only texts inside pharentheses (description) since too long
tvDesc.text = descriptionText.substringAfter(tvName.text.toString() + " (").substringBeforeLast(")");
}
distance.text = item.distance
icon.setImageResource(item.label.icon)

View file

@ -1521,7 +1521,12 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
title.setText(selectedPlace.name);
distance.setText(selectedPlace.distance);
description.setText(selectedPlace.getLongDescription());
// Remove label since it is double information
String descriptionText = selectedPlace.getLongDescription()
.replace(selectedPlace.getName() + " (","");
descriptionText = (descriptionText.equals(selectedPlace.getLongDescription()) ? descriptionText : descriptionText.replaceFirst(".$",""));
// Set the short description after we remove place name from long description
description.setText(descriptionText);
fabCamera.setOnClickListener(view -> {
if (fabCamera.isShown()) {

View file

@ -12,6 +12,7 @@ class NearbyResultItem(private val item: ResultTuple?,
@field:SerializedName("commonsCategory") private val commonsCategory: ResultTuple?,
@field:SerializedName("pic") private val pic: ResultTuple?,
@field:SerializedName("destroyed") private val destroyed: ResultTuple?,
@field:SerializedName("description") private val description: ResultTuple?,
@field:SerializedName("endTime") private val endTime: ResultTuple?) {
fun getItem(): ResultTuple {
@ -58,8 +59,11 @@ class NearbyResultItem(private val item: ResultTuple?,
return destroyed ?: ResultTuple()
}
fun getDescription(): ResultTuple {
return description ?: ResultTuple()
}
fun getEndTime(): ResultTuple {
return endTime ?: ResultTuple()
}
}

View file

@ -25,6 +25,7 @@ public class NotificationHelper {
public static final int NOTIFICATION_DELETE = 1;
public static final int NOTIFICATION_EDIT_CATEGORY = 2;
public static final int NOTIFICATION_EDIT_COORDINATES = 3;
private NotificationManager notificationManager;
private NotificationCompat.Builder notificationBuilder;

View file

@ -289,7 +289,7 @@ public class UploadActivity extends BaseActivity implements UploadContract.View,
public void makeUploadRequest() {
WorkManager.getInstance(getApplicationContext()).enqueueUniqueWork(
UploadWorker.class.getSimpleName(),
ExistingWorkPolicy.KEEP, OneTimeWorkRequest.from(UploadWorker.class));
ExistingWorkPolicy.APPEND_OR_REPLACE, OneTimeWorkRequest.from(UploadWorker.class));
}
@Override

View file

@ -7,6 +7,7 @@ import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import com.google.gson.Gson
import com.mapbox.mapboxsdk.plugins.localization.BuildConfig
import dagger.android.ContributesAndroidInjector
import fr.free.nrw.commons.CommonsApplication
@ -146,61 +147,60 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) :
CommonsApplication.NOTIFICATION_CHANNEL_ID_ALL
)!!
withContext(Dispatchers.IO) {
//Doing this so that retry requests do not create new work requests and while a work is
// already running, all the requests should go through this, so kind of a queue
while (contributionDao.getContribution(statesToProcess)
.blockingGet().isNotEmpty()
) {
val queuedContributions = contributionDao.getContribution(statesToProcess)
.blockingGet()
//Showing initial notification for the number of uploads being processed
val queuedContributions = contributionDao.getContribution(statesToProcess)
.blockingGet()
//Showing initial notification for the number of uploads being processed
processingUploads.setContentTitle(appContext.getString(R.string.starting_uploads))
processingUploads.setContentText(
appContext.resources.getQuantityString(
R.plurals.starting_multiple_uploads,
queuedContributions.size,
queuedContributions.size
)
)
notificationManager?.notify(
PROCESSING_UPLOADS_NOTIFICATION_TAG,
PROCESSING_UPLOADS_NOTIFICATION_ID,
processingUploads.build()
Timber.e("Queued Contributions: "+ queuedContributions.size)
processingUploads.setContentTitle(appContext.getString(R.string.starting_uploads))
processingUploads.setContentText(
appContext.resources.getQuantityString(
R.plurals.starting_multiple_uploads,
queuedContributions.size,
queuedContributions.size
)
)
notificationManager?.notify(
PROCESSING_UPLOADS_NOTIFICATION_TAG,
PROCESSING_UPLOADS_NOTIFICATION_ID,
processingUploads.build()
)
queuedContributions.asFlow().map { contribution ->
/**
* If the limited connection mode is on, lets iterate through the queued
* contributions
* and set the state as STATE_QUEUED_LIMITED_CONNECTION_MODE ,
* otherwise proceed with the upload
*/
if(isLimitedConnectionModeEnabled()){
if (contribution.state == Contribution.STATE_QUEUED) {
contribution.state = Contribution.STATE_QUEUED_LIMITED_CONNECTION_MODE
contributionDao.save(contribution)
}
} else {
contribution.transferred = 0
contribution.state = Contribution.STATE_IN_PROGRESS
contributionDao.save(contribution)
uploadContribution(contribution = contribution)
}
}.collect()
//Dismiss the global notification
notificationManager?.cancel(
PROCESSING_UPLOADS_NOTIFICATION_TAG,
PROCESSING_UPLOADS_NOTIFICATION_ID
)
//No need to keep looking if the limited connection mode is on,
//If the user toggles it, the work manager will be started again
if(isLimitedConnectionModeEnabled()){
break;
}
/**
* To avoid race condition when multiple of these workers are working, assign this state
so that the next one does not process these contribution again
*/
queuedContributions.forEach {
it.state=Contribution.STATE_IN_PROGRESS
contributionDao.saveSynchronous(it)
}
queuedContributions.asFlow().map { contribution ->
/**
* If the limited connection mode is on, lets iterate through the queued
* contributions
* and set the state as STATE_QUEUED_LIMITED_CONNECTION_MODE ,
* otherwise proceed with the upload
*/
if (isLimitedConnectionModeEnabled()) {
if (contribution.state == Contribution.STATE_QUEUED) {
contribution.state = Contribution.STATE_QUEUED_LIMITED_CONNECTION_MODE
contributionDao.saveSynchronous(contribution)
}
} else {
contribution.transferred = 0
contribution.state = Contribution.STATE_IN_PROGRESS
contributionDao.saveSynchronous(contribution)
uploadContribution(contribution = contribution)
}
}.collect()
//Dismiss the global notification
notificationManager?.cancel(
PROCESSING_UPLOADS_NOTIFICATION_TAG,
PROCESSING_UPLOADS_NOTIFICATION_ID
)
}
//TODO make this smart, think of handling retries in the future
return Result.success()
@ -307,6 +307,7 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) :
Timber.e(exception)
Timber.e("Upload from stash failed for contribution : $filename")
showFailedNotification(contribution)
contribution.state=Contribution.STATE_FAILED
if (STASH_ERROR_CODES.contains(exception.message)) {
clearChunks(contribution)
}
@ -315,26 +316,28 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) :
StashUploadState.PAUSED -> {
showPausedNotification(contribution)
contribution.state = Contribution.STATE_PAUSED
contributionDao.save(contribution).blockingGet()
contributionDao.saveSynchronous(contribution)
}
else -> {
Timber.e("""upload file to stash failed with status: ${stashUploadResult.state}""")
showFailedNotification(contribution)
contribution.state = Contribution.STATE_FAILED
contribution.chunkInfo = null
contributionDao.save(contribution).blockingAwait()
contributionDao.saveSynchronous(contribution)
}
}
}catch (exception: Exception){
Timber.e(exception)
Timber.e("Stash upload failed for contribution: $filename")
showFailedNotification(contribution)
contribution.state=Contribution.STATE_FAILED
clearChunks(contribution)
}
}
private fun clearChunks(contribution: Contribution) {
contribution.chunkInfo=null
contributionDao.save(contribution).blockingAwait()
contributionDao.saveSynchronous(contribution)
}
/**

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 778 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 474 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".LocationPicker.LocationPickerActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/location_picker_app_bar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<include layout="@layout/toolbar_location_picker"/>
</com.google.android.material.appbar.AppBarLayout>
<include layout="@layout/content_location_picker"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="bottom">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/location_chosen_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:tint="@color/white"
app:backgroundTint="@color/wikimedia_green"
app:elevation="3dp"
app:layout_anchorGravity="top|end"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:srcCompat="@drawable/ic_check_black_24dp"
android:contentDescription="@string/select_location_location_picker" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_attribution"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="@string/map_attribution"
android:textAlignment="center"
android:textSize="10sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<ImageView
android:id="@+id/location_picker_image_view_marker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:elevation="2dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/map_default_map_marker"
android:contentDescription="@string/location_picker_image_view" />
<ImageView
android:id="@+id/location_picker_image_view_shadow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:elevation="1dp"
app:layout_constraintBottom_toBottomOf="@+id/map_view"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/location_picker_image_view_marker"
app:layout_constraintVertical_bias="0.0"
app:srcCompat="@drawable/map_default_map_marker_shadow"
android:contentDescription="@string/location_picker_image_view_shadow" />
<com.mapbox.mapboxsdk.maps.MapView
android:id="@+id/map_view"
android:layout_width="0dp"
android:layout_height="0dp"
app:mapbox_uiLogo="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</com.mapbox.mapboxsdk.maps.MapView>
<include layout="@layout/bottom_container_location_picker"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -249,6 +249,16 @@
android:drawablePadding="@dimen/tiny_gap"
android:drawableStart="?attr/iconMap24"
tools:text="Coordinates link" />
<Button
android:id="@+id/coordinate_edit"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginTop="@dimen/standard_gap"
android:layout_marginBottom="@dimen/standard_gap"
android:layout_gravity="end"
android:background="@drawable/ic_baseline_edit_24" />
</LinearLayout>
<LinearLayout

View file

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/location_picker_toolbar"
android:layout_width="match_parent"
android:layout_height="78dp"
tools:background="@color/primaryColor">
<TextView
android:id="@+id/location_picker_toolbar_primary_text_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="72dp"
android:text="@string/choose_a_location"
android:textColor="@color/white"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@+id/location_picker_toolbar_secondary_text_view"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"/>
<TextView
android:id="@+id/location_picker_toolbar_secondary_text_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="72dp"
android:text="@string/pan_and_zoom_to_adjust"
android:textColor="@color/white"
android:textSize="14sp"
android:textStyle="normal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/location_picker_toolbar_primary_text_view"/>
<ImageView
android:id="@+id/mapbox_place_picker_toolbar_back_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:contentDescription="@string/exit_location_picker"
android:tint="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_arrow_back_white"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -265,6 +265,7 @@
<string name="upload_problem_different_geolocation">تم التقاط هذه الصورة في موقع مختلف.</string>
<string name="upload_problem_fbmd">من فضلك فقط ارفع الصور التي التقطها بنفسك. لا ترفع الصور التي وجدتها على حسابات الآخرين في فيسبوك.</string>
<string name="upload_problem_do_you_continue">هل ما زلت تريد رفع هذه الصورة؟</string>
<string name="upload_connection_error_alert_title">خطأ في الاتصال</string>
<string name="upload_problem_image">تم العثور على مشاكل في الصورة</string>
<string name="internet_downloaded">يُرجَى فقط رفع الصور التي التقطتها بنفسك، لا ترفع الصور التي قمت بتنزيلها من الإنترنت.</string>
<string name="use_external_storage" fuzzy="true">استخدم تخزينا خارجيا</string>

View file

@ -246,6 +246,8 @@
<string name="upload_problem_different_geolocation">Dieses Bild wurde an einem anderen Ort aufgenommen.</string>
<string name="upload_problem_fbmd">Lade bitte nur Bilder hoch, die du selber erstellt hast. Lade keine Bilder hoch, die du auf Facebook-Konten anderer Personen gefunden hast.</string>
<string name="upload_problem_do_you_continue">Möchtest du immer noch dieses Bild hochladen?</string>
<string name="upload_connection_error_alert_title">Verbindungsfehler</string>
<string name="upload_connection_error_alert_detail">Der Hochlade-Vorgang erfordert einen aktiven Internetzugang. Bitte prüfe deine Netzwerkverbindung.</string>
<string name="upload_problem_image">Im Bild gefundene Probleme</string>
<string name="internet_downloaded">Lade bitte nur Bilder hoch, die du selber aufgenommen hast. Lade keine Bilder hoch, die du aus dem Internet heruntergeladen hast.</string>
<string name="use_external_storage">Speichern von In-App-Aufnahmen</string>
@ -498,6 +500,12 @@
</plurals>
<string name="category_edit_helper_edit_message_else">Konnte keine Kategorien hinzufügen.</string>
<string name="category_edit_button_text">Kategorien aktualisieren</string>
<string name="coordinates_edit_helper_make_edit_toast">Versuche, die Koordinaten zu aktualisieren.</string>
<string name="coordinates_edit_helper_show_edit_title">Koordinaten aktualisieren</string>
<string name="coordinates_edit_helper_show_edit_title_success">Erfolgreich</string>
<string name="coordinates_edit_helper_show_edit_message">Koordinaten %1$s werden hinzugefügt.</string>
<string name="coordinates_edit_helper_edit_message_else">Koordinaten konnten nicht hinzugefügt werden.</string>
<string name="coordinates_picking_unsuccessful">Es können keine Koordinaten abgerufen werden.</string>
<string name="share_image_via">Bild teilen via</string>
<string name="no_achievements_yet">Du hast noch keine Beiträge geleistet</string>
<string name="account_created">Benutzerkonto erstellt!</string>
@ -574,8 +582,21 @@
<string name="quality_images_info">Qualitätsbilder sind Diagramme oder Fotografien, die bestimmte Qualitätsstandards erfüllen (die meist technischer Natur sind) und für Wikimedia-Projekte wertvoll sind</string>
<string name="resuming_upload">Hochladen fortsetzen …</string>
<string name="pausing_upload">Hochladen pausieren …</string>
<string name="limited_connection_explanation">Du hast den eingeschränkten Verbindungsmodus aktiviert. Alle Uploads sind angehalten und werden fortgesetzt, sobald du diesen Modus deaktivierst.</string>
<string name="limited_connection_is_on">Der begrenzte Verbindungsmodus ist aktiv.</string>
<string name="media_details_tooltip">Bitte schreibe eine kurze Bildunterschrift, die sagt, was dein Bild zeigt. Sage in der Beschreibung, was das Bild interessant oder typisch oder selten macht, und erkläree den Kontext, sichtbar oder nicht. Verwende möglichst genaue Begriffe.</string>
<string name="depicts_tooltip">Bitte finde und wähle alle Benennungen, die dieses Bild darstellt. Sei so spezifisch wie möglich. Wenn das Bild mehrere Begriffe darstellt, wähle sie alle in angemessener Weise aus. Wähle keine generischen Begriffe, wenn spezifischere Begriffe verfügbar sind.</string>
<string name="categories_tooltip">Bitte wähle die entsprechenden Kategorien aus. Im Gegensatz zu den Darstellungen sind die Kategorien nur in Englisch.</string>
<string name="license_tooltip">Commons macht deine Bilder wiederverwendbar und von jedem adaptierbar. Möchtest du auf alle Rechte verzichten? Möchtest du als Urheber genannt werden? Möchtest du, dass Bearbeitungen die gleiche Lizenz verwenden?</string>
<string name="depicts_step_title">Stellt dar</string>
<string name="license_step_title">Medienlizenz</string>
<string name="media_detail_step_title">Mediendetails</string>
<string name="menu_view_category_page">Kategorieseite anzeigen</string>
<string name="app_ui_language">Benutzeroberflächensprache</string>
<string name="read_help_link">Mehr lesen</string>
<string name="choose_a_location">Wähle einen Ort</string>
<string name="pan_and_zoom_to_adjust">Schwenken und Zoomen zum Einstellen</string>
<string name="exit_location_picker">Beenden der Ortswahl</string>
<string name="select_location_location_picker">Ort auswählen</string>
<string name="location_picker_image_view">Ortauswahl Bildansicht</string>
</resources>

View file

@ -10,6 +10,7 @@
* Gomoko
* Happy13241
* Jean-Frédéric
* JenyxGym
* KATRINE1992
* Melissadeba95
* Metroitendo
@ -252,6 +253,8 @@
<string name="upload_problem_different_geolocation">Cette image a été prise à un emplacement différent.</string>
<string name="upload_problem_fbmd">Veuillez ne téléverser que des images que vous avez prises vous-même. Ne téléversez aucune image prise sur les comptes Facebook dautres personnes.</string>
<string name="upload_problem_do_you_continue">Voulez-vous tout de même téléverser cette image?</string>
<string name="upload_connection_error_alert_title">Erreur de connexion</string>
<string name="upload_connection_error_alert_detail">Le processus de téléchargement nécessite un accès internet\n actif. Veuillez vérifier votre connexion internet.</string>
<string name="upload_problem_image">Problèmes trouvés dans limage</string>
<string name="internet_downloaded">Veuillez ne téléverser que des images que vous avez prises vous-même. Ne téléversez aucune image que vous avez téléchargée depuis Internet.</string>
<string name="use_external_storage">Enregistrer les prises de vue dans lapplication</string>

View file

@ -35,6 +35,8 @@
<string name="provider_contributions">Mi incargamentos</string>
<string name="menu_share">Divider</string>
<string name="write_storage_permission_rationale">Permission necessari: Scriber sur immagazinage externe. Le app non pote acceder a tu camera/galeria sin isto.</string>
<string name="upload_connection_error_alert_title">Error de connexion</string>
<string name="upload_connection_error_alert_detail">Le processo de incargamento require un accesso a internet active. Per favor verifica tu connexion al rete.</string>
<string name="use_external_storage">Salveguardar photos prendite in app</string>
<string name="use_external_storage_summary">Salveguardar le photos prendite con le camera del application sur le immagazinage de tu apparato</string>
<string name="nearby_location_not_available">\"A proximitate\" poterea non functionar, perque le position geographic non es disponibile.</string>

View file

@ -3,6 +3,7 @@
* Arifin.wijaya
* Daud I.F. Argana
* Farras
* Gombang
* Hidayatsrf
* Ilham151096
* Iwan Novirion
@ -21,10 +22,10 @@
<item quantity="other">(%1$d)</item>
</plurals>
<string name="starting_uploads">Memulai Unggahan</string>
<plurals name="starting_multiple_uploads" fuzzy="true">
<item quantity="other">Memulai %1$d unggahan</item>
<plurals name="starting_multiple_uploads">
<item quantity="other">Memroses %1$d unggahan</item>
</plurals>
<plurals name="multiple_uploads_title" fuzzy="true">
<plurals name="multiple_uploads_title">
<item quantity="other">%1$d unggahan</item>
</plurals>
<plurals name="share_license_summary">
@ -61,7 +62,7 @@
<string name="uploading_queued">Unggahan dalam antrian (mode sambungan terbatas diaktifkan)</string>
<string name="upload_completed_notification_title">%1$s terunggah!</string>
<string name="upload_completed_notification_text">Tekan untuk melihat unggahan Anda</string>
<string name="upload_progress_notification_title_start" fuzzy="true">Memulai unggahan %1$s</string>
<string name="upload_progress_notification_title_start">Mengunggah berkas: %1$s</string>
<string name="upload_progress_notification_title_in_progress">%1$s terunggah!</string>
<string name="upload_progress_notification_title_finishing">Menyelesaikan pengunggahan %1$s</string>
<string name="upload_failed_notification_title">Mengunggah %1$s gagal</string>
@ -229,6 +230,8 @@
<string name="upload_problem_different_geolocation">Gambar diambil di lokasi berbeda.</string>
<string name="upload_problem_fbmd">Tolong hanya unggah gambar-gambar yang Anda ambil sendiri. Jangan unggah gambar yang Anda temukan di akun Facebook orang lain.</string>
<string name="upload_problem_do_you_continue">Apakah Anda masih ingin mengunggah gambar ini?</string>
<string name="upload_connection_error_alert_title">Kesalahan koneksi</string>
<string name="upload_connection_error_alert_detail">Proses pengunggahan memerlukan akses internet aktif. Mohon periksa sambungan jaringan Anda.</string>
<string name="upload_problem_image">Masalah dalam gambar ditemukan</string>
<string name="internet_downloaded">Unggah hanya gambar yang sudah Anda potret sendiri. Jangan unggah gambar yang Anda ambil dari Internet.</string>
<string name="use_external_storage">Simpan foto dalam aplikasi</string>
@ -418,8 +421,8 @@
<string name="review_category_explanation">Gambar ini ada dalam kategori %1$s.</string>
<string name="review_spam_report_question">Di luar cakupan karena</string>
<string name="review_c_violation_report_question">Pelanggaran hak cipta karena</string>
<string name="review_thanks_yes_button_text" fuzzy="true">Ya, mengapa tidak</string>
<string name="review_thanks_no_button_text" fuzzy="true">Gambar selanjutnya</string>
<string name="review_thanks_yes_button_text">Gambar selanjutnya</string>
<string name="review_thanks_no_button_text">Ya, mengapa tidak</string>
<string name="skip_image_explanation">Menekan tombol ini akan memberikan Anda gambar lain yang baru diunggah dari Wikimedia Commons</string>
<string name="review_image_explanation">Anda dapat meninjau gambar dan meningkatkan kualitas Wikimedia Commons.\n Empat parameter peninjauan adalah: \n - Apakah gambar ini di dalam cakupan? \n - Apakah gambar ini mengikut aturan hak cipta? \n - Apakah gambar ini dikategorikan dengan benar? \n - Jika semuanya berjalan lancar Anda juga bisa berterima kasih kepada kontributor.</string>
<string name="no_image">Tidak ada gambar digunakan</string>
@ -435,6 +438,8 @@
<string name="images_featured_explanation">Gambar pilihan adalah gambar dari fotografer dan ilustrator berkemampuan tinggi yang komunitas Wikimedia Commons telah pilih sebagai salah satu gambar dengan kualitas tertinggi di situs ini.</string>
<string name="images_via_nearby_explanation">Gambar Diunggah via Tempat sekitar adalah gambar yang diunggah dengan menemukan tempat di peta.</string>
<string name="thanks_received_explanation">Fitur ini memungkinkan penyunting mengirimkan sebuah pemberitahuan Terima kasih kepada pengguna yang membuat suntingan yang berguna dengan menggunakan tautan terima kasih kecil di halaman riwayat atau \'\'diff\'\'.</string>
<string name="copy_image_caption_description">Salin ke media berikutnya</string>
<string name="copied_successfully">Berhasil disalin</string>
<string name="welcome_do_upload_content_description">Contoh gambar yang sebaiknya diunggah ke Commons</string>
<string name="welcome_dont_upload_content_description">Contoh gambar yang sebaiknya tidak diunggah</string>
<string name="skip_image">Lewati gambar ini</string>
@ -573,4 +578,7 @@
<string name="media_detail_step_title">Detail Media</string>
<string name="menu_view_category_page">Lihat halaman kategori</string>
<string name="menu_view_item_page">Lihat halaman butir</string>
<string name="app_ui_language">Bahasa antarmuka pengguna aplikasi</string>
<string name="remove">Menghapus takarir dan deskripsi</string>
<string name="read_help_link">Baca lebih lanjut</string>
</resources>

View file

@ -257,6 +257,8 @@
<string name="upload_problem_different_geolocation">התמונה צולמה במקום אחר.</string>
<string name="upload_problem_fbmd">נא להעלות רק תמונות שהעלית בעצמך. לא להעלות תמונות שמצאת בחשבונות של כל מיני אנשים בפייסבוק.</string>
<string name="upload_problem_do_you_continue">האם ברצונך עדיין להעלות את התמונה הזאת?</string>
<string name="upload_connection_error_alert_title">שגיאת חיבור</string>
<string name="upload_connection_error_alert_detail">תהליך ההעלאה דורש גישה פעילה לאינטרנט. נא לבדוק את חיבור הרשת שלך.</string>
<string name="upload_problem_image">נמצאו בעיות בתמונה</string>
<string name="internet_downloaded">נא להעלות רק תמונות שצילמת בעצמך. לא להעלות תמונות שהורדת מהאינטרנט.</string>
<string name="use_external_storage">שמירת צילומים מתוך היישומון</string>
@ -511,6 +513,12 @@
</plurals>
<string name="category_edit_helper_edit_message_else">לא ניתן להוסיף קטגוריות.</string>
<string name="category_edit_button_text">עדכון קטגוריות</string>
<string name="coordinates_edit_helper_make_edit_toast">מנסים לעדכן נקודות ציון.</string>
<string name="coordinates_edit_helper_show_edit_title">עדכון נקודות ציון</string>
<string name="coordinates_edit_helper_show_edit_title_success">זה עבד</string>
<string name="coordinates_edit_helper_show_edit_message">נקודות הציון %1$s נוספות.</string>
<string name="coordinates_edit_helper_edit_message_else">לא היה אפשר להוסיף נקודות ציון.</string>
<string name="coordinates_picking_unsuccessful">לא ניתן לעדכן נקודות ציון.</string>
<string name="share_image_via">שיתוף תמונה דרך</string>
<string name="no_achievements_yet">לא תרמת שום דבר עדיין</string>
<string name="account_created">חשבון נוצר!</string>
@ -608,4 +616,9 @@
<string name="app_ui_language">שפת ממשק המשתמש של היישום</string>
<string name="remove">הסרת כיתוב ותיאור</string>
<string name="read_help_link">מידע נוסף</string>
<string name="choose_a_location">נא לבחור מיקום</string>
<string name="pan_and_zoom_to_adjust">לצדד וקרב כדי לכוונן</string>
<string name="exit_location_picker">יציאה מבורר המיקום</string>
<string name="select_location_location_picker">נא לבחור מיקום</string>
<string name="location_picker_image_view">תצוגת תמונה של בורר המיקום</string>
</resources>

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Authors:
* Bennylin
* Diki Ananta
* N219
* NoiX180
@ -81,7 +82,7 @@
<string name="contributions_subtitle_zero" fuzzy="true">Durung ana unggahan</string>
<string name="categories_not_found">Ora ana kategori kang cocog karo %1$s</string>
<string name="categories_skip_explanation">Wuwuh kategori supaya gambar-gambaré panjenengan gampang tinemu ing Wikimedia Commons.\nWiwit nulis saperlu muwuhi kategori.</string>
<string name="categories_activity_title">Kategori</string>
<string name="categories_activity_title">Katégori</string>
<string name="title_activity_settings">Setèlan</string>
<string name="title_activity_signup">Ndhaftar</string>
<string name="title_activity_featured_images">Gambar Pilihan</string>
@ -115,7 +116,7 @@
<string name="tutorial_4_text">Contoné unggahan:</string>
<string name="welcome_final_text">Panjenengan sumerep, ta?</string>
<string name="welcome_final_button_text">Iya!</string>
<string name="detail_panel_cats_label">Kategori</string>
<string name="detail_panel_cats_label">Katégori</string>
<string name="detail_panel_cats_loading">Ngamot…</string>
<string name="detail_panel_cats_none">Ora ana sing dipilih</string>
<string name="detail_description_empty">Tanpa katerangan</string>
@ -163,7 +164,7 @@
<string name="navigation_item_notification">Pambiwara</string>
<string name="no_description_found">dhèksripsi ora tinemu</string>
<string name="nearby_info_menu_commons_article">Kaca barkas Commons</string>
<string name="nearby_info_menu_wikidata_article">Wiji Wikidhata</string>
<string name="nearby_info_menu_wikidata_article">Wiji Wikidata</string>
<string name="nearby_info_menu_wikipedia_article">Artikel Wikipédia</string>
<string name="use_external_storage" fuzzy="true">Anggo panyimpenan njaba</string>
<string name="login_to_your_account">Mlebu log ing akun panjenengan</string>

View file

@ -29,7 +29,7 @@
<string name="uploading_started">ಅಪ್ಲೋಡ್ ಪ್ರಾರಂಭವಾಗಿದೆ!</string>
<string name="upload_completed_notification_title">%1$s ಅಪ್ಲೋಡ್ ಆಗಿದೆ!</string>
<string name="upload_completed_notification_text">ನಿಮ್ಮ ನಕಲೇರಿಕೆಯನ್ನು ನೋಡಲು ಮೆಲ್ಲಗೆ ತಟ್ಟಿ</string>
<string name="upload_progress_notification_title_start" fuzzy="true">ನಕಲೇರಿಕೆಯ%1$s ಪ್ರಾರಂಭ</string>
<string name="upload_progress_notification_title_start">ಕಡತ ಅಪ್‌ಲೋಡ್ ಆಗುತ್ತಿದೆ: %s</string>
<string name="upload_progress_notification_title_in_progress">%1$s ನಕಲೇರಿಕೆ ಆಗಿದೆ!</string>
<string name="upload_progress_notification_title_finishing">%1$s ಅಪ್ಲೋಡ್ ಮಾಡುವಿಕೆಯನ್ನು ಪೂರ್ಣಗೊಳಿಸಲಾಗುತ್ತಿದೆ</string>
<string name="upload_failed_notification_title">%1$s ಅಪ್ಲೋಡ್ ಮಾಡುವಿಕೆ ವಿಫಲವಾಗಿದೆ</string>
@ -44,6 +44,7 @@
<string name="menu_nearby">ಹತ್ತಿರದ</string>
<string name="provider_contributions">ನನ್ನ ನಕಲೇರಿಕೆಗಳು</string>
<string name="menu_share">ಹಂಚು</string>
<string name="menu_view_file_page">ಕಡತದ ಪುಟ ವೀಕ್ಷಿಸಿ</string>
<string name="share_title_hint">ತಲೆಬರಹ (ಕಡ್ಡಾಯ)</string>
<string name="share_description_hint">ವಿವರ</string>
<string name="share_caption_hint">ತಲೆಬರಹ</string>
@ -63,11 +64,13 @@
<string name="contributions_subtitle_zero">(ಇನ್ನೂ ಯಾವುದೇ ಅಪ್‌ಲೋಡ್‌ಗಳಿಲ್ಲ)</string>
<string name="categories_activity_title">ವರ್ಗಗಳು</string>
<string name="title_activity_settings">ವ್ಯವಸ್ಥೆಗಳು</string>
<string name="title_activity_signup">ಸೈನ್ ಅಪ್</string>
<string name="title_activity_category_details">ವರ್ಗ</string>
<string name="menu_about">ಕುರಿತು</string>
<string name="about_privacy_policy">ಗೌಪ್ಯತಾ ನೀತಿ</string>
<string name="about_credits">ಮನ್ನಣೆಗಳು</string>
<string name="title_activity_about">ಕುರಿತು</string>
<string name="provider_categories">ಇತ್ತೀಚೆಗೆ ಬಳಸಲಾದ ವರ್ಗಗಳು</string>
<string name="menu_retry_upload">ಪುನಃ ಪ್ರಯತ್ನಿಸಿ</string>
<string name="menu_cancel_upload">ರದ್ದುಮಾಡಿ</string>
<string name="menu_download">ನಕಲಿಳಿಸಿ</string>
@ -89,7 +92,9 @@
<string name="detail_panel_cats_loading">ತುಂಬಿಸಲಾಗುತ್ತಿದೆ….</string>
<string name="detail_caption_empty">ತಲೆಬರಹ ಇಲ್ಲ</string>
<string name="detail_description_empty">ವಿವರ ಇಲ್ಲ</string>
<string name="detail_discussion_empty">ಯಾವುದೇ ಚರ್ಚೆ ಇಲ್ಲ</string>
<string name="detail_license_empty">ಗೊತ್ತಿಲ್ಲದ ಪರವಾನಗಿ</string>
<string name="menu_refresh">ಪುನಶ್ಚೇತನಗೊಳಿಸು</string>
<string name="ok">ಸರಿ</string>
<string name="warning">ಎಚ್ಚರಿಕೆ</string>
<string name="upload">ಅಪ್‌ಲೋಡ್</string>
@ -97,10 +102,13 @@
<string name="no">ಇಲ್ಲ</string>
<string name="media_detail_caption">ತಲೆಬರಹ</string>
<string name="media_detail_title">ಶೀರ್ಷಿಕೆ</string>
<string name="media_detail_discussion">ಚರ್ಚೆ</string>
<string name="media_detail_author">ಕರ್ತೃ</string>
<string name="media_detail_license">ಪರವಾನಗಿ</string>
<string name="commons_website">ಕಾಮನ್ಸ್ ಜಾಲತಾಣ</string>
<string name="commons_facebook">ಕಾಮನ್ಸ್ ಫೇಸ್ಬುಕ್ ಪುಟ</string>
<string name="welcome_image_mount_zao">ಮೌಂಟ್ ಜಾವ್</string>
<string name="welcome_image_llamas">ಲಾಮಾಗಳು</string>
<string name="welcome_image_tulip">ಟ್ಯೂಲಿಪ್</string>
<string name="welcome_image_sydney_opera_house">ಸಿಡ್ನಿ ಒಪೆರಾ ಹೌಸ್</string>
<string name="cancel">ರದ್ದುಮಾಡಿ</string>
@ -114,6 +122,7 @@
<string name="navigation_item_logout">ಲಾಗ್ ಔಟ್</string>
<string name="navigation_item_notification">ಅಧಿಸೂಚನೆಗಳು</string>
<string name="no_description_found">ಯಾವುದೇ ವಿವರಣೆ ಸಿಗಲಿಲ್ಲ</string>
<string name="nearby_info_menu_commons_article">ಕಾಮನ್ಸ್ ಕಡತದ ಪುಟ</string>
<string name="nearby_info_menu_wikidata_article">ವಿಕಿಡೇಟಾ ವಸ್ತು</string>
<string name="nearby_info_menu_wikipedia_article">ವಿಕಿಪೀಡಿಯ ಲೇಖನ</string>
<string name="upload_problem_image_dark">ಚಿತ್ರವು ಬಹಳ ಕಪ್ಪಾಗಿದೆ.</string>
@ -134,9 +143,13 @@
<string name="retry">ಪುನಃ ಪ್ರಯತ್ನಿಸಿ</string>
<string name="no_images_found">ಯಾವುದೇ ಚಿತ್ರಗಳು ಸಿಗಲಿಲ್ಲ!</string>
<string name="block_notification_title">ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ</string>
<string name="appwidget_img">ದಿನದ ಚಿತ್ರ</string>
<string name="app_widget_heading">ದಿನದ ಚಿತ್ರ</string>
<string name="menu_search_button">ಹುಡುಕು</string>
<string name="search_commons">ಕಾಮನ್ಸ್ ಹುಡುಕಿ</string>
<string name="title_activity_search">ಹುಡುಕು</string>
<string name="search_recent_header">ಇತ್ತೀಚೆಗಿನ ಹುಡುಕಾಟಗಳು:</string>
<string name="search_tab_title_media">ಮೀಡಿಯಾ</string>
<string name="search_tab_title_categories">ವರ್ಗಗಳು</string>
<string name="search_tab_title_depictions">ವಸ್ತುಗಳು</string>
<string name="question">ಪ್ರಶ್ನೆ</string>

View file

@ -227,6 +227,8 @@
<string name="upload_problem_different_geolocation">Оваа слика е направена на друго место.</string>
<string name="upload_problem_fbmd">Подигајте само слики кои самите сте ги направиле. Не подигајте слики пронајдени на туѓи профили на Фејсбук.</string>
<string name="upload_problem_do_you_continue">Дали сепак сакате да ја подигнете?</string>
<string name="upload_connection_error_alert_title">Грешка при поврзувањето</string>
<string name="upload_connection_error_alert_detail">Постапката за подигање бара да сте поврзани\n со семрежјето. Проверете си ја врската.</string>
<string name="upload_problem_image">Пронајдени проблеми во сликата</string>
<string name="internet_downloaded">Подигајте само слики кои самите сте ги направиле. Не подигајте слики што сте ги нашле некаде на семрежјето.</string>
<string name="use_external_storage">Зачувај слики направени во прилогот</string>
@ -479,6 +481,12 @@
</plurals>
<string name="category_edit_helper_edit_message_else">Не можев да ги додадам категориите.</string>
<string name="category_edit_button_text">Поднови категории</string>
<string name="coordinates_edit_helper_make_edit_toast">Се обидувам да ги подоновам координатите.</string>
<string name="coordinates_edit_helper_show_edit_title">Поднова на координати</string>
<string name="coordinates_edit_helper_show_edit_title_success">Успеа</string>
<string name="coordinates_edit_helper_show_edit_message">Координатите %1$s се додадени.</string>
<string name="coordinates_edit_helper_edit_message_else">Не можев да додадам координати.</string>
<string name="coordinates_picking_unsuccessful">Не можев да добијам координати.</string>
<string name="share_image_via">Сподели ја сликата преку</string>
<string name="no_achievements_yet">Засега немате придонеси</string>
<string name="account_created">Сметката е создадена!</string>
@ -576,4 +584,10 @@
<string name="app_ui_language">Јазик на прилогот</string>
<string name="remove">Острани толкување и опис</string>
<string name="read_help_link">Прочитајте повеќе</string>
<string name="choose_a_location">Изберете местоположба</string>
<string name="pan_and_zoom_to_adjust">Доведете го местото на картата и приближете го за да прилагодите</string>
<string name="exit_location_picker">Излези од избирачот на местоположба</string>
<string name="select_location_location_picker">Изберете местоположба</string>
<string name="location_picker_image_view">Поглед на слика за избирање местоположба</string>
<string name="location_picker_image_view_shadow">Сенчест поглед на слика за избирање местоположба</string>
</resources>

View file

@ -5,6 +5,7 @@
* CiaPan
* DeRudySoulStorm
* InternerowyGołąb
* Kareyac
* Krottyianock
* Matma Rex
* Mazab IZW
@ -505,6 +506,7 @@
<string name="category_edit_helper_show_edit_title_success">Powodzenie</string>
<string name="category_edit_helper_edit_message_else">Nie udało się dodać kategorii.</string>
<string name="category_edit_button_text">Aktualizuj kategorie</string>
<string name="coordinates_edit_helper_show_edit_title_success">Powodzenie</string>
<string name="share_image_via">Udostępnij obraz przez</string>
<string name="no_achievements_yet">Nie wykonałeś jeszcze żadnej edycji</string>
<string name="account_created">Konto zostało utworzone!</string>

View file

@ -225,6 +225,8 @@
<string name="upload_problem_different_geolocation">Sa fòto a l\'é stàita fàita ant un pòst diferent.</string>
<string name="upload_problem_fbmd">Për piasì, ch\'a caria mach dle fòto che a l\'ha fàit chiel-midem. Ch\'a caria pa dle fòto che a l\'ha trovà an sij cont Facebook d\'àutra gent.</string>
<string name="upload_problem_do_you_continue">Veul-lo istess carié sta plancia?</string>
<string name="upload_connection_error_alert_title">Eror ëd conession</string>
<string name="upload_connection_error_alert_detail">Ël process ëd cariament a l\'ha damanca ëd n\'acess ativ a l\'aragnà. Për piasì, ch\'a verìfica sò colegament a la rej.</string>
<string name="upload_problem_image">Trovà un problema con la plancia</string>
<string name="internet_downloaded">Për piasì, ch\'a caria mach dle fòto ch\'a l\'ha fàit chiel-midem. Ch\'a caria pa dle fòto che a l\'ha dëscarià da \'ns l\'aragnà.</string>
<string name="use_external_storage">Argistré le plance d\'aplicassion</string>
@ -477,6 +479,12 @@
</plurals>
<string name="category_edit_helper_edit_message_else">Impossìbil gionté dle categorìe.</string>
<string name="category_edit_button_text">Agiorné le categorìe</string>
<string name="coordinates_edit_helper_make_edit_toast">Tentativ d\'agiorné le coordinà.</string>
<string name="coordinates_edit_helper_show_edit_title">Agiornament ëd le coordinà</string>
<string name="coordinates_edit_helper_show_edit_title_success">Sucess</string>
<string name="coordinates_edit_helper_show_edit_message">Giontà la coordinà %1$s.</string>
<string name="coordinates_edit_helper_edit_message_else">Impossìbil gionté lé coordinà.</string>
<string name="coordinates_picking_unsuccessful">Impossìbil oten-e le coordinà.</string>
<string name="share_image_via">Partagé la plancia via</string>
<string name="no_achievements_yet">A l\'ha pa ancor fàit ëd contribussion</string>
<string name="account_created">Cont creà!</string>
@ -574,4 +582,10 @@
<string name="app_ui_language">Lenga d\'antërfassa utent ëd l\'aplicassion</string>
<string name="remove">A gava na legenda e na descrission</string>
<string name="read_help_link">Lese ëd pi</string>
<string name="choose_a_location">Serne na locassion</string>
<string name="pan_and_zoom_to_adjust">Slarghé e strenze për buté bin</string>
<string name="exit_location_picker">Seurte dal selessionador ëd locassion</string>
<string name="select_location_location_picker">Selessioné na locassion</string>
<string name="location_picker_image_view">Vista dla plancia dël selessionador ëd locassion</string>
<string name="location_picker_image_view_shadow">Locassion ëd picker_image_view_shadow</string>
</resources>

View file

@ -241,6 +241,8 @@
<string name="upload_problem_different_geolocation">Esta foto foi tirada em um local diferente.</string>
<string name="upload_problem_fbmd">Por favor, apenas envie fotos que você tirou sozinho. Não faça upload de fotos que você encontrou nas contas do Facebook de outras pessoas.</string>
<string name="upload_problem_do_you_continue">Você ainda quer enviar esta foto?</string>
<string name="upload_connection_error_alert_title">Erro de conexão</string>
<string name="upload_connection_error_alert_detail">O processo de carregamento requer acesso a internet\n ativa. Verifique sua conexão de rede.</string>
<string name="upload_problem_image">Problemas encontrados na imagem</string>
<string name="internet_downloaded">Por favor, só envie fotos que você tirou. Não faça upload de fotos baixadas da Internet.</string>
<string name="use_external_storage">Salvar fotos no aplicativo</string>

View file

@ -269,6 +269,8 @@
<string name="upload_problem_different_geolocation">Это изображение было сделано в другом месте.</string>
<string name="upload_problem_fbmd">Пожалуйста, загружайте только те изображения, которые были сделаны вами. Не загружайте изображений, которые вы нашли в Фейсбуке.</string>
<string name="upload_problem_do_you_continue">Всё равно желаете загрузить это изображение?</string>
<string name="upload_connection_error_alert_title">Ошибка соединения</string>
<string name="upload_connection_error_alert_detail">Для загрузки требуется доступ в Интернет. Пожалуйста, проверьте ваше сетевое соединение.</string>
<string name="upload_problem_image">Проблемы, найденные в изображении</string>
<string name="internet_downloaded">Пожалуйста, загружайте только те изображения, которые были сделаны вами. Не загружайте изображений, которые вы скачали из Интернета.</string>
<string name="use_external_storage">Сохранять сделанные в приложении снимки</string>

View file

@ -246,6 +246,8 @@
<string name="upload_problem_different_geolocation">Tento obrázok bol zhotovený na inom mieste.</string>
<string name="upload_problem_fbmd">Nahrávajte, prosím, len obrázky, ktoré ste sami zhotovili. Nenahrávajte obrázky, ktoré ste našli na Facebookovom profile niekoho iného.</string>
<string name="upload_problem_do_you_continue">Naozaj chcete nahrať tento obrázok?</string>
<string name="upload_connection_error_alert_title">Chyba pripojenia</string>
<string name="upload_connection_error_alert_detail">Proces nahrávania vyžaduje aktívny internetový prístup. Prosím skontrolujte svoje internetové pripojenie.</string>
<string name="upload_problem_image">Na obrázku boli nájdené nejaké problémy</string>
<string name="internet_downloaded">Nahrávajte, prosím, len obrázky, ktoré ste sami zhotovili. Nenahrávajte obrázky, ktoré ste stiahli z internetu.</string>
<string name="use_external_storage">Použiť externé úložisko</string>

View file

@ -232,6 +232,8 @@
<string name="upload_problem_different_geolocation">Bilden togs på en annan plats.</string>
<string name="upload_problem_fbmd">Var god ladda bara upp bilder som du själv har tagit. Ladda inte upp bilder som du har hittat på andra personers Facebook-konton.</string>
<string name="upload_problem_do_you_continue">Vill du fortfarande ladda upp denna bild?</string>
<string name="upload_connection_error_alert_title">Anslutningsfel</string>
<string name="upload_connection_error_alert_detail">Uppladdningsprocessen kräver aktiv internetåtkomst. Kontrollera din nätverksanslutning.</string>
<string name="upload_problem_image">Problem hittades i bilden</string>
<string name="internet_downloaded">Var god ladda bara upp bilder som du själv har tagit. Ladda inte upp bilder som du har laddat ned från Internet.</string>
<string name="use_external_storage">Spara bilder i appen</string>

View file

@ -247,6 +247,8 @@
<string name="upload_problem_different_geolocation">Bu resim farklı bir yerde çekilmiş.</string>
<string name="upload_problem_fbmd">Lütfen yalnızca kendi çektiğiniz resimleri yükleyin. Başkalarının Facebook hesaplarında bulduğunuz resimleri yüklemeyin.</string>
<string name="upload_problem_do_you_continue">Hâlâ bu resmi yüklemek istiyor musunuz?</string>
<string name="upload_connection_error_alert_title">Bağlantı Hatası</string>
<string name="upload_connection_error_alert_detail">Yükleme işlemi etkin internet erişimine ihtiyaç duyar.\n Lütfen ağ bağlantınızı kontrol edin.</string>
<string name="upload_problem_image">Resimde bulunan sorunlar</string>
<string name="internet_downloaded">Lütfen yalnızca kendi çektiğiniz resimleri yükleyin. İnternetten indirdiğiniz resimleri yüklemeyin.</string>
<string name="use_external_storage">Uygulama içi çekimleri kaydedin</string>

View file

@ -228,8 +228,7 @@
<string name="upload_problem_do_you_continue">Do you still want to upload this picture?</string>
<string name="upload_connection_error_alert_title">Connection Error</string>
<string name="upload_connection_error_alert_detail">The upload process requires active internet
access. Please check your network connection.</string>
<string name="upload_connection_error_alert_detail">The upload process requires active internet access. Please check your network connection.</string>
<string name="upload_problem_image">Problems found in image</string>
<string name="internet_downloaded">Please only upload pictures that you have taken by yourself. Don\'t upload pictures that you have downloaded from the Internet.</string>
@ -514,6 +513,13 @@ Upload your first media by tapping on the add button.</string>
<string name="category_edit_helper_edit_message_else">Could not add categories.</string>
<string name="category_edit_button_text">Update categories</string>
<string name="coordinates_edit_helper_make_edit_toast">Trying to update coordinates.</string>
<string name="coordinates_edit_helper_show_edit_title">Coordinates update</string>
<string name="coordinates_edit_helper_show_edit_title_success">Success</string>
<string name="coordinates_edit_helper_show_edit_message">Coordinates %1$s are added.</string>
<string name="coordinates_edit_helper_edit_message_else">Could not add coordinates.</string>
<string name="coordinates_picking_unsuccessful">Unable to get coordinates.</string>
<string name="share_image_via">Share image via</string>
<string name="no_achievements_yet">You haven\'t made any contributions yet</string>
<string name="account_created">Account created!</string>
@ -616,6 +622,12 @@ Upload your first media by tapping on the add button.</string>
<string name="app_ui_language">App user interface language</string>
<string name="remove">Removes a caption and description</string>
<string name="read_help_link">Read more</string>
<string name="choose_a_location">Choose a location</string>
<string name="pan_and_zoom_to_adjust">Pan and zoom to adjust</string>
<string name="exit_location_picker">Exit location picker</string>
<string name="select_location_location_picker">Select location</string>
<string name="location_picker_image_view">Location picker image view</string>
<string name="location_picker_image_view_shadow">Location picker_image_view_shadow</string>
<string name="custom_selector_title">Custom Selector</string>
<string name="custom_selector_empty_text">No Images</string>

View file

@ -2,6 +2,7 @@ SELECT
(SAMPLE(?location) as ?location)
?item
(SAMPLE(COALESCE(?itemLabelPreferredLanguage, ?itemLabelAnyLanguage)) as ?label)
(SAMPLE(COALESCE(?itemDescriptionPreferredLanguage, ?itemDescriptionAnyLanguage, "?")) as ?description)
(SAMPLE(?classId) as ?class)
(SAMPLE(COALESCE(?classLabelPreferredLanguage, ?classLabelAnyLanguage, "?")) as ?classLabel)
(SAMPLE(COALESCE(?icon0, ?icon1)) as ?icon)
@ -23,6 +24,10 @@ SELECT
OPTIONAL {?item rdfs:label ?itemLabelPreferredLanguage. FILTER (lang(?itemLabelPreferredLanguage) = "${LANG}")}
OPTIONAL {?item rdfs:label ?itemLabelAnyLanguage}
# Get the description in the preferred language of the user, or any other language if no description is available in that language.
OPTIONAL {?item schema:description ?itemDescriptionPreferredLanguage. FILTER (lang(?itemDescriptionPreferredLanguage) = "${LANG}")}
OPTIONAL {?item schema:description ?itemDescriptionAnyLanguage }
# Get Commons category (P373)
OPTIONAL { ?item wdt:P373 ?commonsCategory. }