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
This commit is contained in:
Ayan Sarkar 2021-06-13 09:06:47 +05:30 committed by GitHub
parent 15e66f5278
commit 20f74a90c4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 924 additions and 12 deletions

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'

View file

@ -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">

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

@ -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

@ -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

@ -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;

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

@ -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>