diff --git a/app/src/main/java/com/mapbox/mapboxsdk/plugins/testapp/activity/places/PickerLauncherActivity.kt b/app/src/main/java/com/mapbox/mapboxsdk/plugins/testapp/activity/places/PickerLauncherActivity.kt
index a9fd6343f..29db5ea4a 100644
--- a/app/src/main/java/com/mapbox/mapboxsdk/plugins/testapp/activity/places/PickerLauncherActivity.kt
+++ b/app/src/main/java/com/mapbox/mapboxsdk/plugins/testapp/activity/places/PickerLauncherActivity.kt
@@ -28,6 +28,13 @@ class PickerLauncherActivity : AppCompatActivity() {
else getString(R.string.reverse_geocoding_disabled)
}
+ userLocationSwitch.text = getString(R.string.user_location_button_disabled)
+ userLocationSwitch.setOnCheckedChangeListener { compoundButton, checked ->
+ userLocationSwitch.text = if (checked)
+ getString(R.string.user_location_button_enabled)
+ else getString(R.string.user_location_button_disabled)
+ }
+
fabLocationPicker.setOnClickListener { _ ->
Mapbox.getAccessToken()?.let {
startActivityForResult(
@@ -35,6 +42,7 @@ class PickerLauncherActivity : AppCompatActivity() {
.accessToken(it)
.placeOptions(PlacePickerOptions.builder()
.includeReverseGeocode(reverseGeocodingSwitch.isChecked)
+ .includeDeviceLocationButton(userLocationSwitch.isChecked)
.statingCameraPosition(CameraPosition.Builder()
.target(LatLng(40.7544, -73.9862))
.zoom(16.0)
diff --git a/app/src/main/res/layout/activity_picker_launcher.xml b/app/src/main/res/layout/activity_picker_launcher.xml
index 4df51c4f7..e53dde63d 100644
--- a/app/src/main/res/layout/activity_picker_launcher.xml
+++ b/app/src/main/res/layout/activity_picker_launcher.xml
@@ -21,6 +21,21 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView"/>
+
+
Example shows how to launch the Place Picker using the Floating action button and receiving a result in onActivityResult.
Reverse geocoding enabled
Reverse geocoding disabled
+ User location button enabled
+ User location button disabled
Min zoom: %1$d
diff --git a/plugin-places/src/main/java/com/mapbox/mapboxsdk/plugins/places/picker/model/PlacePickerOptions.java b/plugin-places/src/main/java/com/mapbox/mapboxsdk/plugins/places/picker/model/PlacePickerOptions.java
index ba5188a64..0090ac2bc 100644
--- a/plugin-places/src/main/java/com/mapbox/mapboxsdk/plugins/places/picker/model/PlacePickerOptions.java
+++ b/plugin-places/src/main/java/com/mapbox/mapboxsdk/plugins/places/picker/model/PlacePickerOptions.java
@@ -35,9 +35,12 @@ public abstract class PlacePickerOptions implements BasePlaceOptions, Parcelable
public abstract boolean includeReverseGeocode();
+ public abstract boolean includeDeviceLocationButton();
+
public static Builder builder() {
return new AutoValue_PlacePickerOptions.Builder()
- .includeReverseGeocode(true);
+ .includeReverseGeocode(true)
+ .includeDeviceLocationButton(false);
}
@AutoValue.Builder
@@ -59,6 +62,10 @@ public Builder geocodingTypes(@NonNull @GeocodingTypeCriteria String... geocodin
public abstract Builder statingCameraPosition(@NonNull CameraPosition cameraPosition);
/**
+ * Determine whether to include a bottom sheet in the PlacePickerActivity to display
+ * geocoding information associated with coordinates at the center of the map. A new
+ * geocoding call is made every time the map is moved when true is passed through
+ * includeReverseGeocode().
*
* @param includeReverseGeocode whether or not to make a reverse geocoding call to
* retrieve and display information associated with
@@ -68,6 +75,18 @@ public Builder geocodingTypes(@NonNull @GeocodingTypeCriteria String... geocodin
*/
public abstract Builder includeReverseGeocode(boolean includeReverseGeocode);
+ /**
+ * Determine whether an Android-system Floating Action Button is included in
+ * the PlacePickerActivity UI. Clicking on this Floating Action Button will
+ * move the map camera to the device's location. False is the default if
+ * this method isn't used in building the PlacePickerOptions object.
+ *
+ * @param includeDeviceLocationButton
+ *
+ * @return this builder instance for chaining options together
+ */
+ public abstract Builder includeDeviceLocationButton(boolean includeDeviceLocationButton);
+
public abstract PlacePickerOptions build();
}
}
diff --git a/plugin-places/src/main/java/com/mapbox/mapboxsdk/plugins/places/picker/ui/PlacePickerActivity.java b/plugin-places/src/main/java/com/mapbox/mapboxsdk/plugins/places/picker/ui/PlacePickerActivity.java
index 5d5b40385..5c5f2b03c 100644
--- a/plugin-places/src/main/java/com/mapbox/mapboxsdk/plugins/places/picker/ui/PlacePickerActivity.java
+++ b/plugin-places/src/main/java/com/mapbox/mapboxsdk/plugins/places/picker/ui/PlacePickerActivity.java
@@ -3,6 +3,7 @@
import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders;
import android.content.Intent;
+import android.location.Location;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@@ -15,12 +16,20 @@
import android.view.Window;
import android.view.animation.OvershootInterpolator;
import android.widget.ImageView;
+import android.widget.Toast;
import com.google.gson.JsonObject;
+import com.mapbox.android.core.permissions.PermissionsListener;
+import com.mapbox.android.core.permissions.PermissionsManager;
import com.mapbox.api.geocoding.v5.models.CarmenFeature;
import com.mapbox.geojson.Point;
+import com.mapbox.mapboxsdk.camera.CameraPosition;
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.OnMapReadyCallback;
@@ -33,6 +42,7 @@
import com.mapbox.mapboxsdk.plugins.places.picker.model.PlacePickerOptions;
import com.mapbox.mapboxsdk.plugins.places.picker.viewmodel.PlacePickerViewModel;
+import java.util.List;
import java.util.Locale;
import timber.log.Timber;
@@ -46,8 +56,10 @@
* @since 0.2.0
*/
public class PlacePickerActivity extends AppCompatActivity implements OnMapReadyCallback,
- MapboxMap.OnCameraMoveStartedListener, MapboxMap.OnCameraIdleListener, Observer {
+ MapboxMap.OnCameraMoveStartedListener, MapboxMap.OnCameraIdleListener, Observer,
+ PermissionsListener {
+ private PermissionsManager permissionsManager;
CurrentPlaceSelectionBottomSheet bottomSheet;
CarmenFeature carmenFeature;
private PlacePickerViewModel viewModel;
@@ -56,6 +68,7 @@ public class PlacePickerActivity extends AppCompatActivity implements OnMapReady
private MapboxMap mapboxMap;
private String accessToken;
private MapView mapView;
+ private FloatingActionButton userLocationButton;
private boolean includeReverseGeocode;
@Override
@@ -105,6 +118,12 @@ private void bindViews() {
mapView = findViewById(R.id.map_view);
bottomSheet = findViewById(R.id.mapbox_plugins_picker_bottom_sheet);
markerImage = findViewById(R.id.mapbox_plugins_image_view_marker);
+ userLocationButton = findViewById(R.id.user_location_button);
+ }
+
+ private void bindListeners() {
+ PlacePickerActivity.this.mapboxMap.addOnCameraMoveStartedListener(PlacePickerActivity.this);
+ PlacePickerActivity.this.mapboxMap.addOnCameraIdleListener(PlacePickerActivity.this);
}
private void customizeViews() {
@@ -123,25 +142,85 @@ public void onMapReady(final MapboxMap mapboxMap) {
mapboxMap.setStyle(Style.MAPBOX_STREETS, new Style.OnStyleLoaded() {
@Override
public void onStyleLoaded(@NonNull Style style) {
- if (options != null) {
- if (options.startingBounds() != null) {
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(options.startingBounds(), 0));
- } else if (options.statingCameraPosition() != null) {
- mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(options.statingCameraPosition()));
- }
- }
-
+ adjustCameraBasedOnOptions();
if (includeReverseGeocode) {
- // Initialize with the markers current location information.
+ // Initialize with the marker's current coordinates.
makeReverseGeocodingSearch();
}
+ bindListeners();
- PlacePickerActivity.this.mapboxMap.addOnCameraMoveStartedListener(PlacePickerActivity.this);
- PlacePickerActivity.this.mapboxMap.addOnCameraIdleListener(PlacePickerActivity.this);
+ if (options != null && options.includeDeviceLocationButton()) {
+ enableLocationComponent(style);
+ } else {
+ userLocationButton.hide();
+ }
}
});
}
+ private void adjustCameraBasedOnOptions() {
+ if (options != null) {
+ if (options.startingBounds() != null) {
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(options.startingBounds(), 0));
+ } else if (options.statingCameraPosition() != null) {
+ mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(options.statingCameraPosition()));
+ }
+ }
+ }
+
+ @SuppressWarnings( {"MissingPermission"})
+ private void enableLocationComponent(@NonNull Style loadedMapStyle) {
+ // Check if permissions are enabled and if not request
+ if (PermissionsManager.areLocationPermissionsGranted(this)) {
+
+ // Get an instance of the component
+ 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);
+
+ addUserLocationButton();
+ } else {
+ permissionsManager = new PermissionsManager(this);
+ permissionsManager.requestLocationPermissions(this);
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ permissionsManager.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ }
+
+ @Override
+ public void onExplanationNeeded(List permissionsToExplain) {
+ Toast.makeText(this, R.string.mapbox_plugins_place_picker_user_location_permission_explanation,
+ Toast.LENGTH_LONG).show();
+ }
+
+ @Override
+ public void onPermissionResult(boolean granted) {
+ if (granted) {
+ mapboxMap.getStyle(new Style.OnStyleLoaded() {
+ @Override
+ public void onStyleLoaded(@NonNull Style style) {
+ if (options != null && options.includeDeviceLocationButton()) {
+ enableLocationComponent(style);
+ }
+ }
+ });
+ }
+ }
+
@Override
public void onCameraMoveStarted(int reason) {
Timber.v("Map camera has begun moving.");
@@ -185,8 +264,8 @@ private void makeReverseGeocodingSearch() {
LatLng latLng = mapboxMap.getCameraPosition().target;
if (latLng != null) {
viewModel.reverseGeocode(
- Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude()),
- accessToken, options
+ Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude()),
+ accessToken, options
);
}
}
@@ -207,6 +286,32 @@ public void onClick(View view) {
});
}
+ /**
+ * Bind the device location Floating Action Button to this activity's UI and move the
+ * map camera if the button's clicked.
+ */
+ private void addUserLocationButton() {
+ userLocationButton.show();
+ userLocationButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (mapboxMap.getLocationComponent().getLastKnownLocation() != null) {
+ Location lastKnownLocation = mapboxMap.getLocationComponent().getLastKnownLocation();
+ mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(
+ new CameraPosition.Builder()
+ .target(new LatLng(lastKnownLocation.getLatitude(),
+ lastKnownLocation.getLongitude()))
+ .zoom(17.5)
+ .build()
+ ),1400);
+ } else {
+ Toast.makeText(PlacePickerActivity.this,
+ getString(R.string.mapbox_plugins_place_picker_user_location_not_found), Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
+ }
+
void placeSelected() {
Intent returningIntent = new Intent();
if (includeReverseGeocode) {
diff --git a/plugin-places/src/main/res/drawable/mapbox_plugins_ic_user_location.xml b/plugin-places/src/main/res/drawable/mapbox_plugins_ic_user_location.xml
new file mode 100644
index 000000000..a7d704ab0
--- /dev/null
+++ b/plugin-places/src/main/res/drawable/mapbox_plugins_ic_user_location.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/plugin-places/src/main/res/layout/mapbox_view_bottom_sheet_container.xml b/plugin-places/src/main/res/layout/mapbox_view_bottom_sheet_container.xml
index cf351e059..345604a26 100644
--- a/plugin-places/src/main/res/layout/mapbox_view_bottom_sheet_container.xml
+++ b/plugin-places/src/main/res/layout/mapbox_view_bottom_sheet_container.xml
@@ -1,66 +1,67 @@
+ 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">
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
+
-
+
-
+
-
+
-
+
-
+
\ No newline at end of file
diff --git a/plugin-places/src/main/res/values/strings.xml b/plugin-places/src/main/res/values/strings.xml
index a332e3e14..261b126a8 100644
--- a/plugin-places/src/main/res/values/strings.xml
+++ b/plugin-places/src/main/res/values/strings.xml
@@ -17,4 +17,8 @@
Clear Search Query
Offline icon
+
+ This app needs location permissions in order to find the device location.
+ Location permissions not granted.
+ Location can\'t be found.