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
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -137,7 +137,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">
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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() {
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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?>
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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(){
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
BIN
app/src/main/res/drawable-hdpi/map_default_map_marker.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
app/src/main/res/drawable-hdpi/map_default_map_marker_shadow.png
Normal file
|
After Width: | Height: | Size: 778 B |
BIN
app/src/main/res/drawable-hdpi/mapbox_logo_icon.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
app/src/main/res/drawable-mdpi/map_default_map_marker.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
app/src/main/res/drawable-mdpi/map_default_map_marker_shadow.png
Normal file
|
After Width: | Height: | Size: 474 B |
BIN
app/src/main/res/drawable-mdpi/mapbox_logo_icon.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
app/src/main/res/drawable-xhdpi/map_default_map_marker.png
Normal file
|
After Width: | Height: | Size: 6 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
BIN
app/src/main/res/drawable-xhdpi/mapbox_logo_icon.png
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
BIN
app/src/main/res/drawable-xxhdpi/map_default_map_marker.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
BIN
app/src/main/res/drawable-xxhdpi/mapbox_logo_icon.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/map_default_map_marker.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/mapbox_logo_icon.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
20
app/src/main/res/layout/activity_location_picker.xml
Normal 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>
|
||||
35
app/src/main/res/layout/bottom_container_location_picker.xml
Normal 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>
|
||||
49
app/src/main/res/layout/content_location_picker.xml
Normal 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>
|
||||
|
|
@ -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
|
||||
|
|
|
|||
50
app/src/main/res/layout/toolbar_location_picker.xml
Normal 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>
|
||||
|
|
@ -513,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>
|
||||
|
|
@ -615,5 +622,11 @@ 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>
|
||||
|
||||
</resources>
|
||||
|
|
|
|||