Skip to content

Commit 20f74a9

Browse files
authored
Added a feature for editing coordinates (commons-app#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
1 parent 15e66f5 commit 20f74a9

33 files changed

+924
-12
lines changed

app/build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ dependencies {
3939
implementation 'fr.avianey.com.viewpagerindicator:library:2.4.1.1@aar'
4040
implementation 'com.github.chrisbanes:PhotoView:2.0.0'
4141
implementation 'com.github.pedrovgs:renderers:3.3.3'
42-
implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:8.6.2'
42+
implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:9.1.0'
4343
implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-localization-v8:0.11.0'
4444
implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-scalebar-v9:0.4.0'
4545
implementation 'com.github.deano2390:MaterialShowcaseView:1.2.0'

app/src/main/AndroidManifest.xml

+5-1
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,11 @@
137137
android:name=".review.ReviewActivity"
138138
android:label="@string/title_activity_review" />
139139

140-
<service
140+
<activity
141+
android:name=".LocationPicker.LocationPickerActivity"
142+
android:label="Location Picker" />
143+
144+
<service
141145
android:name=".auth.WikiAccountAuthenticatorService"
142146
android:exported="true"
143147
android:process=":auth">
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package fr.free.nrw.commons.LocationPicker;
2+
3+
import android.app.Activity;
4+
import android.content.Intent;
5+
import androidx.annotation.NonNull;
6+
import com.mapbox.mapboxsdk.camera.CameraPosition;
7+
8+
/**
9+
* Helper class for starting the activity
10+
*/
11+
public final class LocationPicker {
12+
13+
/**
14+
* Getting camera position from the intent using constants
15+
* @param data intent
16+
* @return CameraPosition
17+
*/
18+
public static CameraPosition getCameraPosition(final Intent data) {
19+
return data.getParcelableExtra(LocationPickerConstants.MAP_CAMERA_POSITION);
20+
}
21+
22+
public static class IntentBuilder {
23+
24+
private final Intent intent;
25+
26+
/**
27+
* Creates a new builder that creates an intent to launch the place picker activity.
28+
*/
29+
public IntentBuilder() {
30+
intent = new Intent();
31+
}
32+
33+
/**
34+
* Gets and puts location in intent
35+
* @param position CameraPosition
36+
* @return LocationPicker.IntentBuilder
37+
*/
38+
public LocationPicker.IntentBuilder defaultLocation(
39+
final CameraPosition position) {
40+
intent.putExtra(LocationPickerConstants.MAP_CAMERA_POSITION, position);
41+
return this;
42+
}
43+
44+
/**
45+
* Gets and sets the activity
46+
* @param activity Activity
47+
* @return Intent
48+
*/
49+
public Intent build(final Activity activity) {
50+
intent.setClass(activity, LocationPickerActivity.class);
51+
return intent;
52+
}
53+
}
54+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
package fr.free.nrw.commons.LocationPicker;
2+
3+
import android.content.Intent;
4+
import android.os.Bundle;
5+
import android.text.Html;
6+
import android.text.method.LinkMovementMethod;
7+
import android.view.Window;
8+
import android.view.animation.OvershootInterpolator;
9+
import android.widget.ImageView;
10+
import androidx.annotation.NonNull;
11+
import androidx.annotation.Nullable;
12+
import androidx.appcompat.app.ActionBar;
13+
import androidx.appcompat.app.AppCompatActivity;
14+
import androidx.appcompat.widget.AppCompatTextView;
15+
import androidx.constraintlayout.widget.ConstraintLayout;
16+
import androidx.lifecycle.Observer;
17+
import androidx.lifecycle.ViewModelProvider;
18+
import com.google.android.material.floatingactionbutton.FloatingActionButton;
19+
import com.mapbox.android.core.permissions.PermissionsManager;
20+
import com.mapbox.mapboxsdk.camera.CameraPosition;
21+
import com.mapbox.mapboxsdk.camera.CameraPosition.Builder;
22+
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
23+
import com.mapbox.mapboxsdk.geometry.LatLng;
24+
import com.mapbox.mapboxsdk.location.LocationComponent;
25+
import com.mapbox.mapboxsdk.location.LocationComponentActivationOptions;
26+
import com.mapbox.mapboxsdk.location.modes.CameraMode;
27+
import com.mapbox.mapboxsdk.location.modes.RenderMode;
28+
import com.mapbox.mapboxsdk.maps.MapView;
29+
import com.mapbox.mapboxsdk.maps.MapboxMap;
30+
import com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraIdleListener;
31+
import com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener;
32+
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
33+
import com.mapbox.mapboxsdk.maps.Style;
34+
import com.mapbox.mapboxsdk.maps.UiSettings;
35+
import fr.free.nrw.commons.R;
36+
import org.jetbrains.annotations.NotNull;
37+
import timber.log.Timber;
38+
39+
/**
40+
* Helps to pick location and return the result with an intent
41+
*/
42+
public class LocationPickerActivity extends AppCompatActivity implements OnMapReadyCallback,
43+
OnCameraMoveStartedListener, OnCameraIdleListener, Observer<CameraPosition> {
44+
45+
/**
46+
* cameraPosition : position of picker
47+
*/
48+
private CameraPosition cameraPosition;
49+
/**
50+
* markerImage : picker image
51+
*/
52+
private ImageView markerImage;
53+
/**
54+
* mapboxMap : map
55+
*/
56+
private MapboxMap mapboxMap;
57+
/**
58+
* mapView : view of the map
59+
*/
60+
private MapView mapView;
61+
/**
62+
* tvAttribution : credit
63+
*/
64+
private AppCompatTextView tvAttribution;
65+
66+
@Override
67+
protected void onCreate(@Nullable final Bundle savedInstanceState) {
68+
super.onCreate(savedInstanceState);
69+
70+
getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
71+
final ActionBar actionBar = getSupportActionBar();
72+
if (actionBar != null) {
73+
actionBar.hide();
74+
}
75+
setContentView(R.layout.activity_location_picker);
76+
77+
if (savedInstanceState == null) {
78+
cameraPosition = getIntent().getParcelableExtra(LocationPickerConstants.MAP_CAMERA_POSITION);
79+
}
80+
81+
final LocationPickerViewModel viewModel = new ViewModelProvider(this)
82+
.get(LocationPickerViewModel.class);
83+
viewModel.getResult().observe(this, this);
84+
85+
bindViews();
86+
addBackButtonListener();
87+
addPlaceSelectedButton();
88+
addCredits();
89+
getToolbarUI();
90+
91+
mapView.onCreate(savedInstanceState);
92+
mapView.getMapAsync(this);
93+
}
94+
95+
/**
96+
* For showing credits
97+
*/
98+
private void addCredits() {
99+
tvAttribution.setText(Html.fromHtml(getString(R.string.map_attribution)));
100+
tvAttribution.setMovementMethod(LinkMovementMethod.getInstance());
101+
}
102+
103+
/**
104+
* Clicking back button destroy locationPickerActivity
105+
*/
106+
private void addBackButtonListener() {
107+
final ImageView backButton = findViewById(R.id.mapbox_place_picker_toolbar_back_button);
108+
backButton.setOnClickListener(view -> finish());
109+
}
110+
111+
/**
112+
* Binds mapView and location picker icon
113+
*/
114+
private void bindViews() {
115+
mapView = findViewById(R.id.map_view);
116+
markerImage = findViewById(R.id.location_picker_image_view_marker);
117+
tvAttribution = findViewById(R.id.tv_attribution);
118+
}
119+
120+
/**
121+
* Binds the listeners
122+
*/
123+
private void bindListeners() {
124+
mapboxMap.addOnCameraMoveStartedListener(
125+
this);
126+
mapboxMap.addOnCameraIdleListener(
127+
this);
128+
}
129+
130+
/**
131+
* Gets toolbar color
132+
*/
133+
private void getToolbarUI() {
134+
final ConstraintLayout toolbar = findViewById(R.id.location_picker_toolbar);
135+
toolbar.setBackgroundColor(getResources().getColor(R.color.primaryColor));
136+
}
137+
138+
/**
139+
* Takes action when map is ready to show
140+
* @param mapboxMap map
141+
*/
142+
@Override
143+
public void onMapReady(final MapboxMap mapboxMap) {
144+
this.mapboxMap = mapboxMap;
145+
mapboxMap.setStyle(Style.MAPBOX_STREETS, style -> {
146+
adjustCameraBasedOnOptions();
147+
bindListeners();
148+
enableLocationComponent(style);
149+
});
150+
}
151+
152+
/**
153+
* move the location to the current media coordinates
154+
*/
155+
private void adjustCameraBasedOnOptions() {
156+
mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
157+
}
158+
159+
/**
160+
* Enables location components
161+
* @param loadedMapStyle Style
162+
*/
163+
@SuppressWarnings( {"MissingPermission"})
164+
private void enableLocationComponent(@NonNull final Style loadedMapStyle) {
165+
final UiSettings uiSettings = mapboxMap.getUiSettings();
166+
uiSettings.setAttributionEnabled(false);
167+
168+
// Check if permissions are enabled and if not request
169+
if (PermissionsManager.areLocationPermissionsGranted(this)) {
170+
171+
// Get an instance of the component
172+
final LocationComponent locationComponent = mapboxMap.getLocationComponent();
173+
174+
// Activate with options
175+
locationComponent.activateLocationComponent(
176+
LocationComponentActivationOptions.builder(this, loadedMapStyle).build());
177+
178+
// Enable to make component visible
179+
locationComponent.setLocationComponentEnabled(true);
180+
181+
// Set the component's camera mode
182+
locationComponent.setCameraMode(CameraMode.NONE);
183+
184+
// Set the component's render mode
185+
locationComponent.setRenderMode(RenderMode.NORMAL);
186+
187+
}
188+
}
189+
190+
/**
191+
* Acts on camera moving
192+
* @param reason int
193+
*/
194+
@Override
195+
public void onCameraMoveStarted(final int reason) {
196+
Timber.v("Map camera has begun moving.");
197+
if (markerImage.getTranslationY() == 0) {
198+
markerImage.animate().translationY(-75)
199+
.setInterpolator(new OvershootInterpolator()).setDuration(250).start();
200+
}
201+
}
202+
203+
/**
204+
* Acts on camera idle
205+
*/
206+
@Override
207+
public void onCameraIdle() {
208+
Timber.v("Map camera is now idling.");
209+
markerImage.animate().translationY(0)
210+
.setInterpolator(new OvershootInterpolator()).setDuration(250).start();
211+
}
212+
213+
/**
214+
* Takes action on camera position
215+
* @param position position of picker
216+
*/
217+
@Override
218+
public void onChanged(@Nullable CameraPosition position) {
219+
if (position == null) {
220+
position = new Builder()
221+
.target(new LatLng(mapboxMap.getCameraPosition().target.getLatitude(),
222+
mapboxMap.getCameraPosition().target.getLongitude()))
223+
.zoom(16).build();
224+
}
225+
cameraPosition = position;
226+
}
227+
228+
/**
229+
* Select the preferable location
230+
*/
231+
private void addPlaceSelectedButton() {
232+
final FloatingActionButton placeSelectedButton = findViewById(R.id.location_chosen_button);
233+
placeSelectedButton.setOnClickListener(view -> placeSelected());
234+
}
235+
236+
/**
237+
* Return the intent with required data
238+
*/
239+
void placeSelected() {
240+
final Intent returningIntent = new Intent();
241+
returningIntent.putExtra(LocationPickerConstants.MAP_CAMERA_POSITION,
242+
mapboxMap.getCameraPosition());
243+
setResult(AppCompatActivity.RESULT_OK, returningIntent);
244+
finish();
245+
}
246+
247+
@Override
248+
protected void onStart() {
249+
super.onStart();
250+
mapView.onStart();
251+
}
252+
253+
@Override
254+
protected void onResume() {
255+
super.onResume();
256+
mapView.onResume();
257+
}
258+
259+
@Override
260+
protected void onPause() {
261+
super.onPause();
262+
mapView.onPause();
263+
}
264+
265+
@Override
266+
protected void onStop() {
267+
super.onStop();
268+
mapView.onStop();
269+
}
270+
271+
@Override
272+
protected void onSaveInstanceState(final @NotNull Bundle outState) {
273+
super.onSaveInstanceState(outState);
274+
mapView.onSaveInstanceState(outState);
275+
}
276+
277+
@Override
278+
protected void onDestroy() {
279+
super.onDestroy();
280+
mapView.onDestroy();
281+
}
282+
283+
@Override
284+
public void onLowMemory() {
285+
super.onLowMemory();
286+
mapView.onLowMemory();
287+
}
288+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package fr.free.nrw.commons.LocationPicker;
2+
3+
/**
4+
* Constants need for location picking
5+
*/
6+
public final class LocationPickerConstants {
7+
8+
public static final String MAP_CAMERA_POSITION
9+
= "location.picker.cameraPosition";
10+
11+
private LocationPickerConstants() {
12+
}
13+
}

0 commit comments

Comments
 (0)