From cf006ba85070bb401b193305e41ef991d3ea9b8d Mon Sep 17 00:00:00 2001 From: langsmith Date: Wed, 19 Jun 2019 17:11:39 -0700 Subject: [PATCH] adding user location button to PlacePickerActivity --- .../activity/places/PickerLauncherActivity.kt | 8 ++ .../res/layout/activity_picker_launcher.xml | 15 ++ app/src/main/res/values/strings.xml | 2 + .../picker/model/PlacePickerOptions.java | 21 ++- .../places/picker/ui/PlacePickerActivity.java | 133 ++++++++++++++++-- .../mapbox_plugins_ic_user_location.xml | 9 ++ .../mapbox_view_bottom_sheet_container.xml | 111 +++++++-------- plugin-places/src/main/res/values/strings.xml | 4 + 8 files changed, 233 insertions(+), 70 deletions(-) create mode 100644 plugin-places/src/main/res/drawable/mapbox_plugins_ic_user_location.xml 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.