From e9a2c32527fd9c50397a6fab392da83145076f4f Mon Sep 17 00:00:00 2001 From: Ayan Sarkar <71203077+Ayan-10@users.noreply.github.com> Date: Tue, 29 Jun 2021 14:32:20 +0530 Subject: [PATCH] Added option to show and modify location while uploading (#4475) * initial commit * Everything done * minor modification * minor modification * Issues fixed * minor modifications * issue fixed * Issues fixed --- .../LocationPicker/LocationPicker.java | 24 ++- .../LocationPickerActivity.java | 141 ++++++++++++++++-- .../LocationPickerConstants.java | 4 + .../commons/media/MediaDetailFragment.java | 1 + .../UploadMediaDetailFragment.java | 79 +++++++++- .../bottom_container_location_picker.xml | 58 ++++++- app/src/main/res/values/strings.xml | 7 +- 7 files changed, 282 insertions(+), 32 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/LocationPicker/LocationPicker.java b/app/src/main/java/fr/free/nrw/commons/LocationPicker/LocationPicker.java index d23b370f2..696dc9810 100644 --- a/app/src/main/java/fr/free/nrw/commons/LocationPicker/LocationPicker.java +++ b/app/src/main/java/fr/free/nrw/commons/LocationPicker/LocationPicker.java @@ -2,7 +2,6 @@ package fr.free.nrw.commons.LocationPicker; import android.app.Activity; import android.content.Intent; -import androidx.annotation.NonNull; import com.mapbox.mapboxsdk.camera.CameraPosition; /** @@ -33,25 +32,34 @@ public final class LocationPicker { /** * 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; + intent.putExtra(LocationPickerConstants.MAP_CAMERA_POSITION, position); + return this; + } + + /** + * Gets and puts activity name in intent + * @param activity activity key + * @return LocationPicker.IntentBuilder + */ + public LocationPicker.IntentBuilder activityKey( + final String activity) { + intent.putExtra(LocationPickerConstants.ACTIVITY_KEY, activity); + 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; + public Intent build(final Activity activity) { + intent.setClass(activity, LocationPickerActivity.class); + return intent; } } } diff --git a/app/src/main/java/fr/free/nrw/commons/LocationPicker/LocationPickerActivity.java b/app/src/main/java/fr/free/nrw/commons/LocationPicker/LocationPickerActivity.java index 1ae7e06bc..03bd678af 100644 --- a/app/src/main/java/fr/free/nrw/commons/LocationPicker/LocationPickerActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/LocationPicker/LocationPickerActivity.java @@ -1,12 +1,23 @@ package fr.free.nrw.commons.LocationPicker; +import static com.mapbox.mapboxsdk.style.layers.Property.NONE; +import static com.mapbox.mapboxsdk.style.layers.Property.VISIBLE; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAllowOverlap; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconIgnorePlacement; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility; + import android.content.Intent; +import android.graphics.BitmapFactory; import android.os.Bundle; import android.text.Html; import android.text.method.LinkMovementMethod; +import android.view.View; import android.view.Window; import android.view.animation.OvershootInterpolator; +import android.widget.Button; import android.widget.ImageView; +import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; @@ -17,6 +28,7 @@ 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.geojson.Point; import com.mapbox.mapboxsdk.camera.CameraPosition; import com.mapbox.mapboxsdk.camera.CameraPosition.Builder; import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; @@ -32,7 +44,11 @@ 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 com.mapbox.mapboxsdk.style.layers.Layer; +import com.mapbox.mapboxsdk.style.layers.SymbolLayer; +import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; import fr.free.nrw.commons.R; +import fr.free.nrw.commons.Utils; import org.jetbrains.annotations.NotNull; import timber.log.Timber; @@ -42,6 +58,10 @@ import timber.log.Timber; public class LocationPickerActivity extends AppCompatActivity implements OnMapReadyCallback, OnCameraMoveStartedListener, OnCameraIdleListener, Observer { + /** + * DROPPED_MARKER_LAYER_ID : id for layer + */ + private static final String DROPPED_MARKER_LAYER_ID = "DROPPED_MARKER_LAYER_ID"; /** * cameraPosition : position of picker */ @@ -62,6 +82,38 @@ public class LocationPickerActivity extends AppCompatActivity implements OnMapRe * tvAttribution : credit */ private AppCompatTextView tvAttribution; + /** + * activity : activity key + */ + private String activity; + /** + * modifyLocationButton : button for start editing location + */ + Button modifyLocationButton; + /** + * showInMapButton : button for showing in map + */ + TextView showInMapButton; + /** + * placeSelectedButton : fab for selecting location + */ + FloatingActionButton placeSelectedButton; + /** + * droppedMarkerLayer : Layer for static screen + */ + private Layer droppedMarkerLayer; + /** + * shadow : imageview of shadow + */ + private ImageView shadow; + /** + * largeToolbarText : textView of shadow + */ + private TextView largeToolbarText; + /** + * smallToolbarText : textView of shadow + */ + private TextView smallToolbarText; @Override protected void onCreate(@Nullable final Bundle savedInstanceState) { @@ -77,6 +129,7 @@ public class LocationPickerActivity extends AppCompatActivity implements OnMapRe if (savedInstanceState == null) { cameraPosition = getIntent() .getParcelableExtra(LocationPickerConstants.MAP_CAMERA_POSITION); + activity = getIntent().getStringExtra(LocationPickerConstants.ACTIVITY_KEY); } final LocationPickerViewModel viewModel = new ViewModelProvider(this) @@ -89,6 +142,15 @@ public class LocationPickerActivity extends AppCompatActivity implements OnMapRe addCredits(); getToolbarUI(); + if (activity.equals("UploadActivity")) { + placeSelectedButton.setVisibility(View.GONE); + modifyLocationButton.setVisibility(View.VISIBLE); + showInMapButton.setVisibility(View.VISIBLE); + largeToolbarText.setText(getResources().getString(R.string.image_location)); + smallToolbarText.setText(getResources(). + getString(R.string.check_whether_location_is_correct)); + } + mapView.onCreate(savedInstanceState); mapView.getMapAsync(this); } @@ -116,6 +178,10 @@ public class LocationPickerActivity extends AppCompatActivity implements OnMapRe mapView = findViewById(R.id.map_view); markerImage = findViewById(R.id.location_picker_image_view_marker); tvAttribution = findViewById(R.id.tv_attribution); + modifyLocationButton = findViewById(R.id.modify_location); + showInMapButton = findViewById(R.id.show_in_map); + showInMapButton.setText(getResources().getString(R.string.show_in_map_app).toUpperCase()); + shadow = findViewById(R.id.location_picker_image_view_shadow); } /** @@ -133,24 +199,84 @@ public class LocationPickerActivity extends AppCompatActivity implements OnMapRe */ private void getToolbarUI() { final ConstraintLayout toolbar = findViewById(R.id.location_picker_toolbar); + largeToolbarText = findViewById(R.id.location_picker_toolbar_primary_text_view); + smallToolbarText = findViewById(R.id.location_picker_toolbar_secondary_text_view); 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); + + if (modifyLocationButton.getVisibility() == View.VISIBLE) { + initDroppedMarker(style); + adjustCameraBasedOnOptions(); + enableLocationComponent(style); + if (style.getLayer(DROPPED_MARKER_LAYER_ID) != null) { + final GeoJsonSource source = style.getSourceAs("dropped-marker-source-id"); + if (source != null) { + source.setGeoJson(Point.fromLngLat(cameraPosition.target.getLongitude(), + cameraPosition.target.getLatitude())); + } + droppedMarkerLayer = style.getLayer(DROPPED_MARKER_LAYER_ID); + if (droppedMarkerLayer != null) { + droppedMarkerLayer.setProperties(visibility(VISIBLE)); + markerImage.setVisibility(View.GONE); + shadow.setVisibility(View.GONE); + } + } + } else { + adjustCameraBasedOnOptions(); + enableLocationComponent(style); + bindListeners(); + } + modifyLocationButton.setOnClickListener(v -> { + placeSelectedButton.setVisibility(View.VISIBLE); + modifyLocationButton.setVisibility(View.GONE); + showInMapButton.setVisibility(View.GONE); + droppedMarkerLayer.setProperties(visibility(NONE)); + markerImage.setVisibility(View.VISIBLE); + shadow.setVisibility(View.VISIBLE); + largeToolbarText.setText(getResources().getString(R.string.choose_a_location)); + smallToolbarText.setText(getResources().getString(R.string.pan_and_zoom_to_adjust)); + bindListeners(); + }); + + showInMapButton.setOnClickListener(v -> showInMap()); }); } + /** + * Show the location in map app + */ + public void showInMap(){ + Utils.handleGeoCoordinates(this, + new fr.free.nrw.commons.location.LatLng(cameraPosition.target.getLatitude(), + cameraPosition.target.getLongitude(), 0.0f)); + } + + /** + * Initialize Dropped Marker and layer without showing + * @param loadedMapStyle style + */ + private void initDroppedMarker(@NonNull final Style loadedMapStyle) { + // Add the marker image to map + loadedMapStyle.addImage("dropped-icon-image", BitmapFactory.decodeResource( + getResources(), R.drawable.map_default_map_marker)); + loadedMapStyle.addSource(new GeoJsonSource("dropped-marker-source-id")); + loadedMapStyle.addLayer(new SymbolLayer(DROPPED_MARKER_LAYER_ID, + "dropped-marker-source-id").withProperties( + iconImage("dropped-icon-image"), + visibility(NONE), + iconAllowOverlap(true), + iconIgnorePlacement(true) + )); + } /** * move the location to the current media coordinates */ @@ -160,10 +286,9 @@ public class LocationPickerActivity extends AppCompatActivity implements OnMapRe /** * Enables location components - * * @param loadedMapStyle Style */ - @SuppressWarnings({"MissingPermission"}) + @SuppressWarnings( {"MissingPermission"}) private void enableLocationComponent(@NonNull final Style loadedMapStyle) { final UiSettings uiSettings = mapboxMap.getUiSettings(); uiSettings.setAttributionEnabled(false); @@ -192,7 +317,6 @@ public class LocationPickerActivity extends AppCompatActivity implements OnMapRe /** * Acts on camera moving - * * @param reason int */ @Override @@ -216,7 +340,6 @@ public class LocationPickerActivity extends AppCompatActivity implements OnMapRe /** * Takes action on camera position - * * @param position position of picker */ @Override @@ -234,7 +357,7 @@ public class LocationPickerActivity extends AppCompatActivity implements OnMapRe * Select the preferable location */ private void addPlaceSelectedButton() { - final FloatingActionButton placeSelectedButton = findViewById(R.id.location_chosen_button); + placeSelectedButton = findViewById(R.id.location_chosen_button); placeSelectedButton.setOnClickListener(view -> placeSelected()); } diff --git a/app/src/main/java/fr/free/nrw/commons/LocationPicker/LocationPickerConstants.java b/app/src/main/java/fr/free/nrw/commons/LocationPicker/LocationPickerConstants.java index 780ddd4c5..eb27e496c 100644 --- a/app/src/main/java/fr/free/nrw/commons/LocationPicker/LocationPickerConstants.java +++ b/app/src/main/java/fr/free/nrw/commons/LocationPicker/LocationPickerConstants.java @@ -5,9 +5,13 @@ package fr.free.nrw.commons.LocationPicker; */ public final class LocationPickerConstants { + public static final String ACTIVITY_KEY + = "location.picker.activity"; + public static final String MAP_CAMERA_POSITION = "location.picker.cameraPosition"; + private LocationPickerConstants() { } } diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java index 1ed80696c..d913ff149 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java @@ -815,6 +815,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements .defaultLocation(new CameraPosition.Builder() .target(new LatLng(defaultLatitude, defaultLongitude)) .zoom(16).build()) + .activityKey("MediaActivity") .build(getActivity()), REQUEST_CODE); } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.java b/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.java index 818962c2e..b73553f5e 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.java @@ -1,8 +1,10 @@ package fr.free.nrw.commons.upload.mediaDetails; +import static android.app.Activity.RESULT_OK; import static fr.free.nrw.commons.utils.ImageUtils.getErrorMessageForResult; import android.annotation.SuppressLint; +import android.content.Intent; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -23,19 +25,19 @@ import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; import com.github.chrisbanes.photoview.PhotoView; +import com.mapbox.mapboxsdk.camera.CameraPosition; +import fr.free.nrw.commons.LocationPicker.LocationPicker; import fr.free.nrw.commons.R; -import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.filepicker.UploadableFile; import fr.free.nrw.commons.kvstore.JsonKvStore; -import fr.free.nrw.commons.location.LatLng; import fr.free.nrw.commons.nearby.Place; import fr.free.nrw.commons.settings.Prefs; import fr.free.nrw.commons.upload.ImageCoordinates; import fr.free.nrw.commons.upload.SimilarImageDialogFragment; import fr.free.nrw.commons.upload.UploadBaseFragment; +import fr.free.nrw.commons.upload.UploadItem; import fr.free.nrw.commons.upload.UploadMediaDetail; import fr.free.nrw.commons.upload.UploadMediaDetailAdapter; -import fr.free.nrw.commons.upload.UploadItem; import fr.free.nrw.commons.utils.DialogUtil; import fr.free.nrw.commons.utils.ImageUtils; import fr.free.nrw.commons.utils.ViewUtil; @@ -49,6 +51,7 @@ import timber.log.Timber; public class UploadMediaDetailFragment extends UploadBaseFragment implements UploadMediaDetailsContract.View, UploadMediaDetailAdapter.EventListener { + private static final int REQUEST_CODE = 1211; @BindView(R.id.tv_title) TextView tvTitle; @BindView(R.id.ib_map) @@ -94,7 +97,10 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements */ private Place nearbyPlace; private UploadItem uploadItem; - + /** + * editableUploadItem : Storing the upload item before going to update the coordinates + */ + private UploadItem editableUploadItem; private UploadMediaDetailFragmentCallback callback; @@ -378,10 +384,67 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements } @Override - public void showExternalMap(UploadItem uploadItem) { - Utils.handleGeoCoordinates(getContext(), - new LatLng(uploadItem.getGpsCoords().getDecLatitude(), - uploadItem.getGpsCoords().getDecLongitude(), 0.0f)); + public void showExternalMap(final UploadItem uploadItem) { + goToLocationPickerActivity(uploadItem); + } + + /** + * Start Location picker activity. Show the location first then user can modify it by clicking + * modify location button. + * @param uploadItem current upload item + */ + private void goToLocationPickerActivity(final UploadItem uploadItem) { + + editableUploadItem = uploadItem; + startActivityForResult(new LocationPicker.IntentBuilder() + .defaultLocation(new CameraPosition.Builder() + .target(new com.mapbox.mapboxsdk.geometry.LatLng(uploadItem.getGpsCoords() + .getDecLatitude(), + uploadItem.getGpsCoords().getDecLongitude())) + .zoom(16).build()) + .activityKey("UploadActivity") + .build(getActivity()), REQUEST_CODE); + } + + /** + * Get the coordinates and update the existing coordinates. + * @param requestCode code of request + * @param resultCode code of result + * @param data intent + */ + @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()); + + editLocation(latitude, longitude); + } + } + } + + /** + * Update the old coordinates with new one + * @param latitude new latitude + * @param longitude new longitude + */ + public void editLocation(final String latitude, final String longitude){ + + editableUploadItem.getGpsCoords().setDecLatitude(Double.parseDouble(latitude)); + editableUploadItem.getGpsCoords().setDecLongitude(Double.parseDouble(longitude)); + editableUploadItem.getGpsCoords().setDecimalCoords(latitude+"|"+longitude); + editableUploadItem.getGpsCoords().setImageCoordsExists(true); + Toast.makeText(getContext(), "Location Updated", Toast.LENGTH_LONG).show(); + } @Override diff --git a/app/src/main/res/layout/bottom_container_location_picker.xml b/app/src/main/res/layout/bottom_container_location_picker.xml index 95c0a2431..98a8216c5 100644 --- a/app/src/main/res/layout/bottom_container_location_picker.xml +++ b/app/src/main/res/layout/bottom_container_location_picker.xml @@ -1,6 +1,5 @@ - + app:srcCompat="@drawable/ic_check_black_24dp" /> + + +