From 907612e93d8a2b156d4604fda348707ccb347836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Paczos?= Date: Wed, 5 Sep 2018 14:30:36 +0200 Subject: [android] updated naming scheme and packages structure for LocationLayerPlugin, now called LocationComponent --- .../location/CameraCompassBearingAnimator.java | 24 + .../location/CameraGpsBearingAnimator.java | 23 + .../mapboxsdk/location/CameraLatLngAnimator.java | 25 + .../mapbox/mapboxsdk/location/CompassEngine.java | 65 + .../mapbox/mapboxsdk/location/CompassListener.java | 33 + .../mapbox/mapboxsdk/location/LatLngEvaluator.java | 19 + .../mapboxsdk/location/LayerAccuracyAnimator.java | 24 + .../mapboxsdk/location/LayerBitmapProvider.java | 33 + .../location/LayerCompassBearingAnimator.java | 24 + .../mapboxsdk/location/LayerFeatureProvider.java | 26 + .../location/LayerGpsBearingAnimator.java | 23 + .../mapboxsdk/location/LayerLatLngAnimator.java | 25 + .../mapboxsdk/location/LayerSourceProvider.java | 107 ++ .../location/LocationAnimatorCoordinator.java | 375 +++++ .../location/LocationCameraController.java | 263 ++++ .../mapboxsdk/location/LocationComponent.java | 1019 +++++++++++++ .../location/LocationComponentCompassEngine.java | 267 ++++ .../location/LocationComponentConstants.java | 66 + .../location/LocationComponentOptions.java | 1561 ++++++++++++++++++++ .../location/LocationLayerController.java | 394 +++++ .../mapbox/mapboxsdk/location/MapboxAnimator.java | 92 ++ .../location/MapboxCameraAnimatorAdapter.java | 38 + .../mapboxsdk/location/MapboxFloatAnimator.java | 17 + .../mapboxsdk/location/MapboxLatLngAnimator.java | 19 + .../location/OnCameraMoveInvalidateListener.java | 7 + .../location/OnCameraTrackingChangedListener.java | 21 + .../location/OnLocationComponentClickListener.java | 11 + .../OnLocationComponentLongClickListener.java | 11 + .../location/OnLocationStaleListener.java | 13 + .../mapboxsdk/location/StaleStateManager.java | 80 + .../mapbox/mapboxsdk/location/TiltAnimator.java | 27 + .../java/com/mapbox/mapboxsdk/location/Utils.java | 102 ++ .../mapbox/mapboxsdk/location/ZoomAnimator.java | 29 + .../mapboxsdk/location/modes/CameraMode.java | 66 + .../mapboxsdk/location/modes/RenderMode.java | 45 + .../mapbox/mapboxsdk/location/package-info.java | 4 + .../java/com/mapbox/mapboxsdk/maps/MapView.java | 6 +- .../java/com/mapbox/mapboxsdk/maps/MapboxMap.java | 28 +- .../CameraCompassBearingAnimator.java | 24 - .../locationlayer/CameraGpsBearingAnimator.java | 23 - .../locationlayer/CameraLatLngAnimator.java | 25 - .../plugins/locationlayer/CompassEngine.java | 62 - .../plugins/locationlayer/CompassListener.java | 33 - .../plugins/locationlayer/LatLngEvaluator.java | 19 - .../locationlayer/LayerAccuracyAnimator.java | 24 - .../plugins/locationlayer/LayerBitmapProvider.java | 33 - .../locationlayer/LayerCompassBearingAnimator.java | 24 - .../locationlayer/LayerFeatureProvider.java | 26 - .../locationlayer/LayerGpsBearingAnimator.java | 23 - .../plugins/locationlayer/LayerLatLngAnimator.java | 25 - .../plugins/locationlayer/LayerSourceProvider.java | 107 -- .../plugins/locationlayer/LocationLayer.java | 394 ----- .../plugins/locationlayer/LocationLayerCamera.java | 263 ---- .../locationlayer/LocationLayerCompassEngine.java | 267 ---- .../locationlayer/LocationLayerConstants.java | 66 - .../locationlayer/LocationLayerOptions.java | 1561 -------------------- .../plugins/locationlayer/LocationLayerPlugin.java | 1017 ------------- .../locationlayer/MapboxCameraAnimatorAdapter.java | 38 - .../OnCameraMoveInvalidateListener.java | 7 - .../OnCameraTrackingChangedListener.java | 21 - .../OnLocationLayerClickListener.java | 11 - .../OnLocationLayerLongClickListener.java | 11 - .../locationlayer/OnLocationStaleListener.java | 13 - .../plugins/locationlayer/PluginAnimator.java | 92 -- .../locationlayer/PluginAnimatorCoordinator.java | 375 ----- .../plugins/locationlayer/PluginFloatAnimator.java | 17 - .../locationlayer/PluginLatLngAnimator.java | 19 - .../plugins/locationlayer/StaleStateManager.java | 80 - .../plugins/locationlayer/TiltAnimator.java | 27 - .../mapboxsdk/plugins/locationlayer/Utils.java | 102 -- .../plugins/locationlayer/ZoomAnimator.java | 29 - .../plugins/locationlayer/modes/CameraMode.java | 66 - .../plugins/locationlayer/modes/RenderMode.java | 45 - .../plugins/locationlayer/package-info.java | 4 - 74 files changed, 4995 insertions(+), 4990 deletions(-) create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/CameraCompassBearingAnimator.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/CameraGpsBearingAnimator.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/CameraLatLngAnimator.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/CompassEngine.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/CompassListener.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LatLngEvaluator.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerAccuracyAnimator.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerBitmapProvider.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerCompassBearingAnimator.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerFeatureProvider.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerGpsBearingAnimator.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerLatLngAnimator.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerSourceProvider.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinator.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationCameraController.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentCompassEngine.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentConstants.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentOptions.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationLayerController.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimator.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxCameraAnimatorAdapter.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxFloatAnimator.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxLatLngAnimator.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnCameraMoveInvalidateListener.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnCameraTrackingChangedListener.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationComponentClickListener.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationComponentLongClickListener.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationStaleListener.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/StaleStateManager.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/TiltAnimator.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/Utils.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/ZoomAnimator.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/modes/CameraMode.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/modes/RenderMode.java create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/CameraCompassBearingAnimator.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/CameraGpsBearingAnimator.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/CameraLatLngAnimator.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/CompassEngine.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/CompassListener.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LatLngEvaluator.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerAccuracyAnimator.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerBitmapProvider.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerCompassBearingAnimator.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerFeatureProvider.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerGpsBearingAnimator.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerLatLngAnimator.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerSourceProvider.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayer.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerCamera.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerCompassEngine.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerConstants.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerOptions.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerPlugin.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/MapboxCameraAnimatorAdapter.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/OnCameraMoveInvalidateListener.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/OnCameraTrackingChangedListener.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/OnLocationLayerClickListener.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/OnLocationLayerLongClickListener.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/OnLocationStaleListener.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginAnimator.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginAnimatorCoordinator.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginFloatAnimator.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginLatLngAnimator.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/StaleStateManager.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/TiltAnimator.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/Utils.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/ZoomAnimator.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/modes/CameraMode.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/modes/RenderMode.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/package-info.java (limited to 'platform/android/MapboxGLAndroidSDK/src/main/java') diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/CameraCompassBearingAnimator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/CameraCompassBearingAnimator.java new file mode 100644 index 0000000000..ea1817ab5e --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/CameraCompassBearingAnimator.java @@ -0,0 +1,24 @@ +package com.mapbox.mapboxsdk.location; + +import android.animation.ValueAnimator; + +import java.util.List; + +class CameraCompassBearingAnimator extends MapboxFloatAnimator { + CameraCompassBearingAnimator(Float previous, Float target, + List updateListeners) { + super(previous, target, updateListeners); + } + + @Override + int provideAnimatorType() { + return ANIMATOR_CAMERA_COMPASS_BEARING; + } + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + for (OnCameraAnimationsValuesChangeListener listener : updateListeners) { + listener.onNewCompassBearingValue((Float) animation.getAnimatedValue()); + } + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/CameraGpsBearingAnimator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/CameraGpsBearingAnimator.java new file mode 100644 index 0000000000..f46cf805ff --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/CameraGpsBearingAnimator.java @@ -0,0 +1,23 @@ +package com.mapbox.mapboxsdk.location; + +import android.animation.ValueAnimator; + +import java.util.List; + +class CameraGpsBearingAnimator extends MapboxFloatAnimator { + CameraGpsBearingAnimator(Float previous, Float target, List updateListeners) { + super(previous, target, updateListeners); + } + + @Override + int provideAnimatorType() { + return ANIMATOR_CAMERA_GPS_BEARING; + } + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + for (OnCameraAnimationsValuesChangeListener listener : updateListeners) { + listener.onNewGpsBearingValue((Float) animation.getAnimatedValue()); + } + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/CameraLatLngAnimator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/CameraLatLngAnimator.java new file mode 100644 index 0000000000..533abfc335 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/CameraLatLngAnimator.java @@ -0,0 +1,25 @@ +package com.mapbox.mapboxsdk.location; + +import android.animation.ValueAnimator; + +import com.mapbox.mapboxsdk.geometry.LatLng; + +import java.util.List; + +class CameraLatLngAnimator extends MapboxLatLngAnimator { + CameraLatLngAnimator(LatLng previous, LatLng target, List updateListeners) { + super(previous, target, updateListeners); + } + + @Override + int provideAnimatorType() { + return ANIMATOR_CAMERA_LATLNG; + } + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + for (OnCameraAnimationsValuesChangeListener listener : updateListeners) { + listener.onNewLatLngValue((LatLng) animation.getAnimatedValue()); + } + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/CompassEngine.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/CompassEngine.java new file mode 100644 index 0000000000..3691bdc0ea --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/CompassEngine.java @@ -0,0 +1,65 @@ +package com.mapbox.mapboxsdk.location; + +import android.support.annotation.NonNull; + +import com.mapbox.mapboxsdk.location.modes.CameraMode; +import com.mapbox.mapboxsdk.location.modes.RenderMode; + +/** + * Interface defining the source of compass heading data that is + * consumed by the {@link LocationComponent} when in compass related + * {@link RenderMode} or + * {@link CameraMode}s. + */ +public interface CompassEngine { + + /** + * Adds a {@link CompassListener} that can be used to + * receive heading and state changes. + * + * @param compassListener to be added + */ + void addCompassListener(@NonNull CompassListener compassListener); + + /** + * Removes a {@link CompassListener} that can be used to + * receive heading and state changes. + * + * @param compassListener to be removed + */ + void removeCompassListener(@NonNull CompassListener compassListener); + + /** + * Returns the last heading value produced and pushed via + * a compass listener. + * + * @return last heading value + */ + float getLastHeading(); + + /** + * Provides the last know accuracy status from the sensor manager. + *

+ * An integer value which is identical to the {@code SensorManager} class constants: + *

    + *
  • {@link android.hardware.SensorManager#SENSOR_STATUS_NO_CONTACT}
  • + *
  • {@link android.hardware.SensorManager#SENSOR_STATUS_UNRELIABLE}
  • + *
  • {@link android.hardware.SensorManager#SENSOR_STATUS_ACCURACY_LOW}
  • + *
  • {@link android.hardware.SensorManager#SENSOR_STATUS_ACCURACY_MEDIUM}
  • + *
  • {@link android.hardware.SensorManager#SENSOR_STATUS_ACCURACY_HIGH}
  • + *
+ * + * @return last accuracy status + */ + int getLastAccuracySensorStatus(); + + /** + * Lifecycle method that can be used for adding or releasing resources. + */ + void onStart(); + + /** + * Lifecycle method that can be used for adding or releasing resources. + */ + void onStop(); +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/CompassListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/CompassListener.java new file mode 100644 index 0000000000..3e5eb7f258 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/CompassListener.java @@ -0,0 +1,33 @@ +package com.mapbox.mapboxsdk.location; + +/** + * Callbacks related to the compass + */ +public interface CompassListener { + + /** + * Callback's invoked when a new compass update occurs. You can listen into the compass updates + * using {@link LocationComponent#addCompassListener(CompassListener)} and implementing these + * callbacks. Note that this interface is also used internally to to update the UI chevron/arrow. + * + * @param userHeading the new compass heading + */ + void onCompassChanged(float userHeading); + + /** + * This gets invoked when the compass accuracy status changes from one value to another. It + * provides an integer value which is identical to the {@code SensorManager} class constants: + *
    + *
  • {@link android.hardware.SensorManager#SENSOR_STATUS_NO_CONTACT}
  • + *
  • {@link android.hardware.SensorManager#SENSOR_STATUS_UNRELIABLE}
  • + *
  • {@link android.hardware.SensorManager#SENSOR_STATUS_ACCURACY_LOW}
  • + *
  • {@link android.hardware.SensorManager#SENSOR_STATUS_ACCURACY_MEDIUM}
  • + *
  • {@link android.hardware.SensorManager#SENSOR_STATUS_ACCURACY_HIGH}
  • + *
+ * + * @param compassStatus the new accuracy of this sensor, one of + * {@code SensorManager.SENSOR_STATUS_*} + */ + void onCompassAccuracyChange(int compassStatus); +} + diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LatLngEvaluator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LatLngEvaluator.java new file mode 100644 index 0000000000..fa966641fd --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LatLngEvaluator.java @@ -0,0 +1,19 @@ +package com.mapbox.mapboxsdk.location; + +import android.animation.TypeEvaluator; + +import com.mapbox.mapboxsdk.geometry.LatLng; + +class LatLngEvaluator implements TypeEvaluator { + + private final LatLng latLng = new LatLng(); + + @Override + public LatLng evaluate(float fraction, LatLng startValue, LatLng endValue) { + latLng.setLatitude(startValue.getLatitude() + + ((endValue.getLatitude() - startValue.getLatitude()) * fraction)); + latLng.setLongitude(startValue.getLongitude() + + ((endValue.getLongitude() - startValue.getLongitude()) * fraction)); + return latLng; + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerAccuracyAnimator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerAccuracyAnimator.java new file mode 100644 index 0000000000..e893f0fee9 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerAccuracyAnimator.java @@ -0,0 +1,24 @@ +package com.mapbox.mapboxsdk.location; + +import android.animation.ValueAnimator; + +import java.util.List; + +class LayerAccuracyAnimator extends MapboxFloatAnimator { + + LayerAccuracyAnimator(Float previous, Float target, List updateListeners) { + super(previous, target, updateListeners); + } + + @Override + int provideAnimatorType() { + return ANIMATOR_LAYER_ACCURACY; + } + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + for (OnLayerAnimationsValuesChangeListener listener : updateListeners) { + listener.onNewAccuracyRadiusValue((Float) animation.getAnimatedValue()); + } + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerBitmapProvider.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerBitmapProvider.java new file mode 100644 index 0000000000..6c9f063c18 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerBitmapProvider.java @@ -0,0 +1,33 @@ +package com.mapbox.mapboxsdk.location; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.support.annotation.ColorInt; +import android.support.annotation.DrawableRes; +import android.support.v4.content.ContextCompat; + +import com.mapbox.mapboxsdk.R; + +import static com.mapbox.mapboxsdk.location.Utils.generateShadow; +import static com.mapbox.mapboxsdk.location.Utils.getBitmapFromDrawable; +import static com.mapbox.mapboxsdk.location.Utils.getDrawable; + +class LayerBitmapProvider { + + private final Context context; + + LayerBitmapProvider(Context context) { + this.context = context; + } + + Bitmap generateBitmap(@DrawableRes int drawableRes, @ColorInt Integer tintColor) { + Drawable drawable = getDrawable(context, drawableRes, tintColor); + return getBitmapFromDrawable(drawable); + } + + Bitmap generateShadowBitmap(LocationComponentOptions options) { + Drawable shadowDrawable = ContextCompat.getDrawable(context, R.drawable.mapbox_user_icon_shadow); + return generateShadow(shadowDrawable, options.elevation()); + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerCompassBearingAnimator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerCompassBearingAnimator.java new file mode 100644 index 0000000000..e75eaca2b5 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerCompassBearingAnimator.java @@ -0,0 +1,24 @@ +package com.mapbox.mapboxsdk.location; + +import android.animation.ValueAnimator; + +import java.util.List; + +class LayerCompassBearingAnimator extends MapboxFloatAnimator { + LayerCompassBearingAnimator(Float previous, + Float target, List updateListeners) { + super(previous, target, updateListeners); + } + + @Override + int provideAnimatorType() { + return ANIMATOR_LAYER_COMPASS_BEARING; + } + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + for (OnLayerAnimationsValuesChangeListener listener : updateListeners) { + listener.onNewCompassBearingValue((Float) animation.getAnimatedValue()); + } + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerFeatureProvider.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerFeatureProvider.java new file mode 100644 index 0000000000..0be38dc4db --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerFeatureProvider.java @@ -0,0 +1,26 @@ +package com.mapbox.mapboxsdk.location; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.Point; + +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_COMPASS_BEARING; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_GPS_BEARING; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_LOCATION_STALE; + +class LayerFeatureProvider { + + @NonNull + Feature generateLocationFeature(@Nullable Feature locationFeature, LocationComponentOptions options) { + if (locationFeature != null) { + return locationFeature; + } + locationFeature = Feature.fromGeometry(Point.fromLngLat(0.0, 0.0)); + locationFeature.addNumberProperty(PROPERTY_GPS_BEARING, 0f); + locationFeature.addNumberProperty(PROPERTY_COMPASS_BEARING, 0f); + locationFeature.addBooleanProperty(PROPERTY_LOCATION_STALE, options.enableStaleState()); + return locationFeature; + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerGpsBearingAnimator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerGpsBearingAnimator.java new file mode 100644 index 0000000000..75cea13750 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerGpsBearingAnimator.java @@ -0,0 +1,23 @@ +package com.mapbox.mapboxsdk.location; + +import android.animation.ValueAnimator; + +import java.util.List; + +class LayerGpsBearingAnimator extends MapboxFloatAnimator { + LayerGpsBearingAnimator(Float previous, Float target, List updateListeners) { + super(previous, target, updateListeners); + } + + @Override + int provideAnimatorType() { + return ANIMATOR_LAYER_GPS_BEARING; + } + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + for (OnLayerAnimationsValuesChangeListener listener : updateListeners) { + listener.onNewGpsBearingValue((Float) animation.getAnimatedValue()); + } + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerLatLngAnimator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerLatLngAnimator.java new file mode 100644 index 0000000000..f4dc2861cf --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerLatLngAnimator.java @@ -0,0 +1,25 @@ +package com.mapbox.mapboxsdk.location; + +import android.animation.ValueAnimator; + +import com.mapbox.mapboxsdk.geometry.LatLng; + +import java.util.List; + +class LayerLatLngAnimator extends MapboxLatLngAnimator { + LayerLatLngAnimator(LatLng previous, LatLng target, List updateListeners) { + super(previous, target, updateListeners); + } + + @Override + int provideAnimatorType() { + return ANIMATOR_LAYER_LATLNG; + } + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + for (OnLayerAnimationsValuesChangeListener listener : updateListeners) { + listener.onNewLatLngValue((LatLng) animation.getAnimatedValue()); + } + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerSourceProvider.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerSourceProvider.java new file mode 100644 index 0000000000..d6198a7da8 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerSourceProvider.java @@ -0,0 +1,107 @@ +package com.mapbox.mapboxsdk.location; + +import com.mapbox.geojson.Feature; +import com.mapbox.mapboxsdk.style.layers.CircleLayer; +import com.mapbox.mapboxsdk.style.layers.Layer; +import com.mapbox.mapboxsdk.style.layers.Property; +import com.mapbox.mapboxsdk.style.layers.SymbolLayer; +import com.mapbox.mapboxsdk.style.sources.GeoJsonOptions; +import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; + +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.ACCURACY_LAYER; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.BACKGROUND_LAYER; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.BEARING_LAYER; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.FOREGROUND_LAYER; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.LOCATION_SOURCE; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_ACCURACY_ALPHA; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_ACCURACY_COLOR; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_ACCURACY_RADIUS; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_BACKGROUND_ICON; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_BACKGROUND_STALE_ICON; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_BEARING_ICON; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_COMPASS_BEARING; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_FOREGROUND_ICON; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_FOREGROUND_ICON_OFFSET; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_FOREGROUND_STALE_ICON; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_GPS_BEARING; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_LOCATION_STALE; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_SHADOW_ICON_OFFSET; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.SHADOW_ICON; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.SHADOW_LAYER; +import static com.mapbox.mapboxsdk.style.expressions.Expression.get; +import static com.mapbox.mapboxsdk.style.expressions.Expression.literal; +import static com.mapbox.mapboxsdk.style.expressions.Expression.match; +import static com.mapbox.mapboxsdk.style.expressions.Expression.stop; +import static com.mapbox.mapboxsdk.style.expressions.Expression.switchCase; +import static com.mapbox.mapboxsdk.style.layers.Property.ICON_ROTATION_ALIGNMENT_MAP; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleColor; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleOpacity; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circlePitchAlignment; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleRadius; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleStrokeColor; +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.iconOffset; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconRotate; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconRotationAlignment; + +class LayerSourceProvider { + + private static final String EMPTY_STRING = ""; + + GeoJsonSource generateSource(Feature locationFeature) { + return new GeoJsonSource( + LOCATION_SOURCE, + locationFeature, + new GeoJsonOptions().withMaxZoom(16) + ); + } + + Layer generateLayer(String layerId) { + SymbolLayer layer = new SymbolLayer(layerId, LOCATION_SOURCE); + layer.setProperties( + iconAllowOverlap(true), + iconIgnorePlacement(true), + iconRotationAlignment(ICON_ROTATION_ALIGNMENT_MAP), + iconRotate( + match(literal(layerId), literal(0f), + stop(FOREGROUND_LAYER, get(PROPERTY_GPS_BEARING)), + stop(BACKGROUND_LAYER, get(PROPERTY_GPS_BEARING)), + stop(SHADOW_LAYER, get(PROPERTY_GPS_BEARING)), + stop(BEARING_LAYER, get(PROPERTY_COMPASS_BEARING)) + ) + ), + iconImage( + match(literal(layerId), literal(EMPTY_STRING), + stop(FOREGROUND_LAYER, switchCase( + get(PROPERTY_LOCATION_STALE), get(PROPERTY_FOREGROUND_STALE_ICON), + get(PROPERTY_FOREGROUND_ICON))), + stop(BACKGROUND_LAYER, switchCase( + get(PROPERTY_LOCATION_STALE), get(PROPERTY_BACKGROUND_STALE_ICON), + get(PROPERTY_BACKGROUND_ICON))), + stop(SHADOW_LAYER, literal(SHADOW_ICON)), + stop(BEARING_LAYER, get(PROPERTY_BEARING_ICON)) + ) + ), + iconOffset( + match(literal(layerId), literal(new Float[] {0f, 0f}), + stop(literal(FOREGROUND_LAYER), get(PROPERTY_FOREGROUND_ICON_OFFSET)), + stop(literal(SHADOW_LAYER), get(PROPERTY_SHADOW_ICON_OFFSET)) + ) + ) + ); + return layer; + } + + Layer generateAccuracyLayer() { + return new CircleLayer(ACCURACY_LAYER, LOCATION_SOURCE) + .withProperties( + circleRadius(get(PROPERTY_ACCURACY_RADIUS)), + circleColor(get(PROPERTY_ACCURACY_COLOR)), + circleOpacity(get(PROPERTY_ACCURACY_ALPHA)), + circleStrokeColor(get(PROPERTY_ACCURACY_COLOR)), + circlePitchAlignment(Property.CIRCLE_PITCH_ALIGNMENT_MAP) + ); + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinator.java new file mode 100644 index 0000000000..67a9727f5c --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinator.java @@ -0,0 +1,375 @@ +package com.mapbox.mapboxsdk.location; + +import android.animation.Animator; +import android.animation.AnimatorSet; +import android.annotation.SuppressLint; +import android.location.Location; +import android.os.SystemClock; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.view.animation.LinearInterpolator; + +import com.mapbox.mapboxsdk.camera.CameraPosition; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapboxMap; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.ACCURACY_RADIUS_ANIMATION_DURATION; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.COMPASS_UPDATE_RATE_MS; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.MAX_ANIMATION_DURATION_MS; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.TRANSITION_ANIMATION_DURATION_MS; +import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_CAMERA_COMPASS_BEARING; +import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_CAMERA_GPS_BEARING; +import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_CAMERA_LATLNG; +import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_LAYER_ACCURACY; +import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_LAYER_COMPASS_BEARING; +import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_LAYER_GPS_BEARING; +import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_LAYER_LATLNG; +import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_TILT; +import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_ZOOM; + +final class LocationAnimatorCoordinator { + + @SuppressLint("UseSparseArrays") + final Map animatorMap = new HashMap<>(); + + final List layerListeners = new ArrayList<>(); + final List cameraListeners = new ArrayList<>(); + + private Location previousLocation; + private float previousAccuracyRadius = -1; + private float previousCompassBearing = -1; + private long locationUpdateTimestamp = -1; + + void addLayerListener(MapboxAnimator.OnLayerAnimationsValuesChangeListener listener) { + layerListeners.add(listener); + } + + void removeLayerListener(MapboxAnimator.OnLayerAnimationsValuesChangeListener listener) { + layerListeners.remove(listener); + } + + void addCameraListener(MapboxAnimator.OnCameraAnimationsValuesChangeListener listener) { + cameraListeners.add(listener); + } + + void removeCameraListener(MapboxAnimator.OnCameraAnimationsValuesChangeListener listener) { + cameraListeners.remove(listener); + } + + void feedNewLocation(@NonNull Location newLocation, @NonNull CameraPosition currentCameraPosition, + boolean isGpsNorth) { + if (previousLocation == null) { + previousLocation = newLocation; + locationUpdateTimestamp = SystemClock.elapsedRealtime() - TRANSITION_ANIMATION_DURATION_MS; + } + + LatLng previousLayerLatLng = getPreviousLayerLatLng(); + float previousLayerBearing = getPreviousLayerGpsBearing(); + LatLng previousCameraLatLng = currentCameraPosition.target; + float previousCameraBearing = (float) currentCameraPosition.bearing; + + LatLng targetLatLng = new LatLng(newLocation); + float targetLayerBearing = newLocation.getBearing(); + float targetCameraBearing = newLocation.getBearing(); + targetCameraBearing = checkGpsNorth(isGpsNorth, targetCameraBearing); + + updateLayerAnimators(previousLayerLatLng, targetLatLng, previousLayerBearing, targetLayerBearing); + updateCameraAnimators(previousCameraLatLng, previousCameraBearing, targetLatLng, targetCameraBearing); + + playLocationAnimators(getAnimationDuration()); + + previousLocation = newLocation; + } + + void feedNewCompassBearing(float targetCompassBearing, @NonNull CameraPosition currentCameraPosition) { + if (previousCompassBearing < 0) { + previousCompassBearing = targetCompassBearing; + } + + float previousLayerBearing = getPreviousLayerCompassBearing(); + float previousCameraBearing = (float) currentCameraPosition.bearing; + + updateCompassAnimators(targetCompassBearing, previousLayerBearing, previousCameraBearing); + playCompassAnimators(COMPASS_UPDATE_RATE_MS); + + previousCompassBearing = targetCompassBearing; + } + + void feedNewAccuracyRadius(float targetAccuracyRadius, boolean noAnimation) { + if (previousAccuracyRadius < 0) { + previousAccuracyRadius = targetAccuracyRadius; + } + + float previousAccuracyRadius = getPreviousAccuracyRadius(); + updateAccuracyAnimators(targetAccuracyRadius, previousAccuracyRadius); + playAccuracyAnimator(noAnimation ? 0 : ACCURACY_RADIUS_ANIMATION_DURATION); + + this.previousAccuracyRadius = targetAccuracyRadius; + } + + void feedNewZoomLevel(double targetZoomLevel, @NonNull CameraPosition currentCameraPosition, long animationDuration, + @Nullable MapboxMap.CancelableCallback callback) { + updateZoomAnimator((float) targetZoomLevel, (float) currentCameraPosition.zoom, callback); + playZoomAnimator(animationDuration); + } + + void feedNewTilt(double targetTilt, @NonNull CameraPosition currentCameraPosition, long animationDuration, + @Nullable MapboxMap.CancelableCallback callback) { + updateTiltAnimator((float) targetTilt, (float) currentCameraPosition.tilt, callback); + playTiltAnimator(animationDuration); + } + + private LatLng getPreviousLayerLatLng() { + LatLng previousLatLng; + MapboxAnimator latLngAnimator = animatorMap.get(ANIMATOR_LAYER_LATLNG); + if (latLngAnimator != null) { + previousLatLng = (LatLng) latLngAnimator.getAnimatedValue(); + } else { + previousLatLng = new LatLng(previousLocation); + } + return previousLatLng; + } + + private float getPreviousLayerGpsBearing() { + LayerGpsBearingAnimator animator = (LayerGpsBearingAnimator) animatorMap.get(ANIMATOR_LAYER_GPS_BEARING); + float previousBearing; + if (animator != null) { + previousBearing = (float) animator.getAnimatedValue(); + } else { + previousBearing = previousLocation.getBearing(); + } + return previousBearing; + } + + private float getPreviousLayerCompassBearing() { + LayerCompassBearingAnimator animator = + (LayerCompassBearingAnimator) animatorMap.get(ANIMATOR_LAYER_COMPASS_BEARING); + + float previousBearing; + if (animator != null) { + previousBearing = (float) animator.getAnimatedValue(); + } else { + previousBearing = previousCompassBearing; + } + return previousBearing; + } + + private float getPreviousAccuracyRadius() { + LayerAccuracyAnimator animator = (LayerAccuracyAnimator) animatorMap.get(ANIMATOR_LAYER_ACCURACY); + float previousRadius; + if (animator != null) { + previousRadius = (float) animator.getAnimatedValue(); + } else { + previousRadius = previousAccuracyRadius; + } + return previousRadius; + } + + private void updateLayerAnimators(LatLng previousLatLng, LatLng targetLatLng, + float previousBearing, float targetBearing) { + createNewAnimator(ANIMATOR_LAYER_LATLNG, new LayerLatLngAnimator(previousLatLng, targetLatLng, layerListeners)); + + float normalizedLayerBearing = Utils.shortestRotation(targetBearing, previousBearing); + createNewAnimator(ANIMATOR_LAYER_GPS_BEARING, + new LayerGpsBearingAnimator(previousBearing, normalizedLayerBearing, layerListeners)); + } + + private void updateCameraAnimators(LatLng previousCameraLatLng, float previousCameraBearing, + LatLng targetLatLng, float targetBearing) { + createNewAnimator(ANIMATOR_CAMERA_LATLNG, + new CameraLatLngAnimator(previousCameraLatLng, targetLatLng, cameraListeners)); + + float normalizedCameraBearing = Utils.shortestRotation(targetBearing, previousCameraBearing); + createNewAnimator(ANIMATOR_CAMERA_GPS_BEARING, + new CameraGpsBearingAnimator(previousCameraBearing, normalizedCameraBearing, cameraListeners)); + } + + private void updateCompassAnimators(float targetCompassBearing, float previousLayerBearing, + float previousCameraBearing) { + float normalizedLayerBearing = Utils.shortestRotation(targetCompassBearing, previousLayerBearing); + createNewAnimator(ANIMATOR_LAYER_COMPASS_BEARING, + new LayerCompassBearingAnimator(previousLayerBearing, normalizedLayerBearing, layerListeners)); + + float normalizedCameraBearing = Utils.shortestRotation(targetCompassBearing, previousCameraBearing); + createNewAnimator(ANIMATOR_CAMERA_COMPASS_BEARING, + new CameraCompassBearingAnimator(previousCameraBearing, normalizedCameraBearing, cameraListeners)); + } + + private void updateAccuracyAnimators(float targetAccuracyRadius, float previousAccuracyRadius) { + createNewAnimator(ANIMATOR_LAYER_ACCURACY, + new LayerAccuracyAnimator(previousAccuracyRadius, targetAccuracyRadius, layerListeners)); + } + + private void updateZoomAnimator(float targetZoomLevel, float previousZoomLevel, + @Nullable MapboxMap.CancelableCallback cancelableCallback) { + createNewAnimator(ANIMATOR_ZOOM, + new ZoomAnimator(previousZoomLevel, targetZoomLevel, cameraListeners, cancelableCallback)); + } + + private void updateTiltAnimator(float targetTilt, float previousTiltLevel, + @Nullable MapboxMap.CancelableCallback cancelableCallback) { + createNewAnimator(ANIMATOR_TILT, + new TiltAnimator(previousTiltLevel, targetTilt, cameraListeners, cancelableCallback)); + } + + private long getAnimationDuration() { + long previousUpdateTimeStamp = locationUpdateTimestamp; + locationUpdateTimestamp = SystemClock.elapsedRealtime(); + + long animationDuration; + if (previousUpdateTimeStamp == 0) { + animationDuration = 0; + } else { + animationDuration = (long) ((locationUpdateTimestamp - previousUpdateTimeStamp) * 1.1f) + /*make animation slightly longer*/; + } + + animationDuration = Math.min(animationDuration, MAX_ANIMATION_DURATION_MS); + + return animationDuration; + } + + private float checkGpsNorth(boolean isGpsNorth, float targetCameraBearing) { + if (isGpsNorth) { + targetCameraBearing = 0; + } + return targetCameraBearing; + } + + private void playLocationAnimators(long duration) { + List locationAnimators = new ArrayList<>(); + locationAnimators.add(animatorMap.get(ANIMATOR_LAYER_LATLNG)); + locationAnimators.add(animatorMap.get(ANIMATOR_LAYER_GPS_BEARING)); + locationAnimators.add(animatorMap.get(ANIMATOR_CAMERA_LATLNG)); + locationAnimators.add(animatorMap.get(ANIMATOR_CAMERA_GPS_BEARING)); + AnimatorSet locationAnimatorSet = new AnimatorSet(); + locationAnimatorSet.playTogether(locationAnimators); + locationAnimatorSet.setInterpolator(new LinearInterpolator()); + locationAnimatorSet.setDuration(duration); + locationAnimatorSet.start(); + } + + private void playCompassAnimators(long duration) { + List compassAnimators = new ArrayList<>(); + compassAnimators.add(animatorMap.get(ANIMATOR_LAYER_COMPASS_BEARING)); + compassAnimators.add(animatorMap.get(ANIMATOR_CAMERA_COMPASS_BEARING)); + AnimatorSet compassAnimatorSet = new AnimatorSet(); + compassAnimatorSet.playTogether(compassAnimators); + compassAnimatorSet.setDuration(duration); + compassAnimatorSet.start(); + } + + private void playAccuracyAnimator(long duration) { + MapboxAnimator animator = animatorMap.get(ANIMATOR_LAYER_ACCURACY); + animator.setDuration(duration); + animator.start(); + } + + private void playZoomAnimator(long duration) { + MapboxAnimator animator = animatorMap.get(ANIMATOR_ZOOM); + animator.setDuration(duration); + animator.start(); + } + + private void playTiltAnimator(long duration) { + MapboxAnimator animator = animatorMap.get(ANIMATOR_TILT); + animator.setDuration(duration); + animator.start(); + } + + private void playCameraLocationAnimators(long duration) { + List locationAnimators = new ArrayList<>(); + locationAnimators.add(animatorMap.get(ANIMATOR_CAMERA_LATLNG)); + locationAnimators.add(animatorMap.get(ANIMATOR_CAMERA_GPS_BEARING)); + AnimatorSet locationAnimatorSet = new AnimatorSet(); + locationAnimatorSet.playTogether(locationAnimators); + locationAnimatorSet.setInterpolator(new LinearInterpolator()); + locationAnimatorSet.setDuration(duration); + locationAnimatorSet.start(); + } + + void resetAllCameraAnimations(CameraPosition currentCameraPosition, boolean isGpsNorth) { + resetCameraCompassAnimation(currentCameraPosition); + resetCameraLocationAnimations(currentCameraPosition, isGpsNorth); + playCameraLocationAnimators(TRANSITION_ANIMATION_DURATION_MS); + } + + private void resetCameraLocationAnimations(CameraPosition currentCameraPosition, boolean isGpsNorth) { + resetCameraLatLngAnimation(currentCameraPosition); + resetCameraGpsBearingAnimation(currentCameraPosition, isGpsNorth); + } + + private void resetCameraLatLngAnimation(CameraPosition currentCameraPosition) { + CameraLatLngAnimator animator = (CameraLatLngAnimator) animatorMap.get(ANIMATOR_CAMERA_LATLNG); + if (animator == null) { + return; + } + + LatLng currentTarget = animator.getTarget(); + LatLng previousCameraTarget = currentCameraPosition.target; + createNewAnimator(ANIMATOR_CAMERA_LATLNG, + new CameraLatLngAnimator(previousCameraTarget, currentTarget, cameraListeners)); + } + + private void resetCameraGpsBearingAnimation(CameraPosition currentCameraPosition, boolean isGpsNorth) { + CameraGpsBearingAnimator animator = (CameraGpsBearingAnimator) animatorMap.get(ANIMATOR_CAMERA_GPS_BEARING); + if (animator == null) { + return; + } + + float currentTargetBearing = animator.getTarget(); + currentTargetBearing = checkGpsNorth(isGpsNorth, currentTargetBearing); + float previousCameraBearing = (float) currentCameraPosition.bearing; + float normalizedCameraBearing = Utils.shortestRotation(currentTargetBearing, previousCameraBearing); + createNewAnimator(ANIMATOR_CAMERA_GPS_BEARING, + new CameraGpsBearingAnimator(previousCameraBearing, normalizedCameraBearing, cameraListeners)); + } + + private void resetCameraCompassAnimation(CameraPosition currentCameraPosition) { + CameraCompassBearingAnimator animator = + (CameraCompassBearingAnimator) animatorMap.get(ANIMATOR_CAMERA_COMPASS_BEARING); + if (animator == null) { + return; + } + + float currentTargetBearing = animator.getTarget(); + float previousCameraBearing = (float) currentCameraPosition.bearing; + float normalizedCameraBearing = Utils.shortestRotation(currentTargetBearing, previousCameraBearing); + createNewAnimator(ANIMATOR_CAMERA_COMPASS_BEARING, + new CameraCompassBearingAnimator(previousCameraBearing, normalizedCameraBearing, cameraListeners)); + } + + private void createNewAnimator(@MapboxAnimator.Type int animatorType, MapboxAnimator animator) { + cancelAnimator(animatorType); + animatorMap.put(animatorType, animator); + } + + void cancelZoomAnimation() { + cancelAnimator(ANIMATOR_ZOOM); + } + + void cancelTiltAnimation() { + cancelAnimator(ANIMATOR_TILT); + } + + void cancelAllAnimations() { + for (@MapboxAnimator.Type int animatorType : animatorMap.keySet()) { + cancelAnimator(animatorType); + } + } + + private void cancelAnimator(@MapboxAnimator.Type int animatorType) { + MapboxAnimator animator = animatorMap.get(animatorType); + if (animator != null) { + animator.cancel(); + animator.removeAllUpdateListeners(); + animator.removeAllListeners(); + animatorMap.put(animatorType, null); + } + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationCameraController.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationCameraController.java new file mode 100644 index 0000000000..5b2209179e --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationCameraController.java @@ -0,0 +1,263 @@ +package com.mapbox.mapboxsdk.location; + +import android.content.Context; +import android.graphics.PointF; +import android.view.MotionEvent; + +import com.mapbox.android.gestures.AndroidGesturesManager; +import com.mapbox.android.gestures.MoveGestureDetector; +import com.mapbox.android.gestures.RotateGestureDetector; +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.location.modes.CameraMode; + +import java.util.List; +import java.util.Set; + +final class LocationCameraController implements MapboxAnimator.OnCameraAnimationsValuesChangeListener { + + @CameraMode.Mode + private int cameraMode; + + private final MapboxMap mapboxMap; + private final OnCameraTrackingChangedListener internalCameraTrackingChangedListener; + private LocationComponentOptions options; + private boolean adjustFocalPoint; + + private final MoveGestureDetector moveGestureDetector; + private final OnCameraMoveInvalidateListener onCameraMoveInvalidateListener; + + LocationCameraController( + Context context, + MapboxMap mapboxMap, + OnCameraTrackingChangedListener internalCameraTrackingChangedListener, + LocationComponentOptions options, + OnCameraMoveInvalidateListener onCameraMoveInvalidateListener) { + this.mapboxMap = mapboxMap; + mapboxMap.setGesturesManager( + new LocationGesturesManager(context), true, true); + moveGestureDetector = mapboxMap.getGesturesManager().getMoveGestureDetector(); + mapboxMap.addOnMoveListener(onMoveListener); + mapboxMap.addOnRotateListener(onRotateListener); + mapboxMap.addOnFlingListener(onFlingListener); + + this.internalCameraTrackingChangedListener = internalCameraTrackingChangedListener; + this.onCameraMoveInvalidateListener = onCameraMoveInvalidateListener; + initializeOptions(options); + } + + // Package private for testing purposes + LocationCameraController(MapboxMap mapboxMap, + MoveGestureDetector moveGestureDetector, + OnCameraTrackingChangedListener internalCameraTrackingChangedListener, + OnCameraMoveInvalidateListener onCameraMoveInvalidateListener) { + this.mapboxMap = mapboxMap; + this.moveGestureDetector = moveGestureDetector; + this.internalCameraTrackingChangedListener = internalCameraTrackingChangedListener; + this.onCameraMoveInvalidateListener = onCameraMoveInvalidateListener; + } + + void initializeOptions(LocationComponentOptions options) { + this.options = options; + } + + void setCameraMode(@CameraMode.Mode int cameraMode) { + final boolean wasTracking = isLocationTracking(); + this.cameraMode = cameraMode; + mapboxMap.cancelTransitions(); + adjustGesturesThresholds(); + notifyCameraTrackingChangeListener(wasTracking); + } + + int getCameraMode() { + return cameraMode; + } + + private void setBearing(float bearing) { + mapboxMap.moveCamera(CameraUpdateFactory.bearingTo(bearing)); + onCameraMoveInvalidateListener.onInvalidateCameraMove(); + } + + private void setLatLng(LatLng latLng) { + mapboxMap.moveCamera(CameraUpdateFactory.newLatLng(latLng)); + onCameraMoveInvalidateListener.onInvalidateCameraMove(); + } + + private void setZoom(float zoom) { + mapboxMap.moveCamera(CameraUpdateFactory.zoomTo(zoom)); + onCameraMoveInvalidateListener.onInvalidateCameraMove(); + } + + private void setTilt(float tilt) { + mapboxMap.moveCamera(CameraUpdateFactory.tiltTo(tilt)); + onCameraMoveInvalidateListener.onInvalidateCameraMove(); + } + + @Override + public void onNewLatLngValue(LatLng latLng) { + if (cameraMode == CameraMode.TRACKING + || cameraMode == CameraMode.TRACKING_COMPASS + || cameraMode == CameraMode.TRACKING_GPS + || cameraMode == CameraMode.TRACKING_GPS_NORTH) { + setLatLng(latLng); + + if (adjustFocalPoint) { + PointF focalPoint = mapboxMap.getProjection().toScreenLocation(latLng); + mapboxMap.getUiSettings().setFocalPoint(focalPoint); + adjustFocalPoint = false; + } + } + } + + @Override + public void onNewGpsBearingValue(float gpsBearing) { + boolean trackingNorth = cameraMode == CameraMode.TRACKING_GPS_NORTH + && mapboxMap.getCameraPosition().bearing != 0; + + if (cameraMode == CameraMode.TRACKING_GPS + || cameraMode == CameraMode.NONE_GPS + || trackingNorth) { + setBearing(gpsBearing); + } + } + + @Override + public void onNewCompassBearingValue(float compassBearing) { + if (cameraMode == CameraMode.TRACKING_COMPASS + || cameraMode == CameraMode.NONE_COMPASS) { + setBearing(compassBearing); + } + } + + @Override + public void onNewZoomValue(float zoom) { + setZoom(zoom); + } + + @Override + public void onNewTiltValue(float tilt) { + setTilt(tilt); + } + + private void adjustGesturesThresholds() { + if (isLocationTracking()) { + adjustFocalPoint = true; + moveGestureDetector.setMoveThreshold(options.trackingInitialMoveThreshold()); + } else { + moveGestureDetector.setMoveThreshold(0f); + } + } + + private boolean isLocationTracking() { + return cameraMode == CameraMode.TRACKING + || cameraMode == CameraMode.TRACKING_COMPASS + || cameraMode == CameraMode.TRACKING_GPS + || cameraMode == CameraMode.TRACKING_GPS_NORTH; + } + + private boolean isBearingTracking() { + return cameraMode == CameraMode.NONE_COMPASS + || cameraMode == CameraMode.TRACKING_COMPASS + || cameraMode == CameraMode.NONE_GPS + || cameraMode == CameraMode.TRACKING_GPS + || cameraMode == CameraMode.TRACKING_GPS_NORTH; + } + + private void notifyCameraTrackingChangeListener(boolean wasTracking) { + internalCameraTrackingChangedListener.onCameraTrackingChanged(cameraMode); + if (wasTracking && !isLocationTracking()) { + mapboxMap.getUiSettings().setFocalPoint(null); + internalCameraTrackingChangedListener.onCameraTrackingDismissed(); + } + } + + private MapboxMap.OnMoveListener onMoveListener = new MapboxMap.OnMoveListener() { + private boolean interrupt; + + @Override + public void onMoveBegin(MoveGestureDetector detector) { + if (detector.getPointersCount() > 1 + && detector.getMoveThreshold() != options.trackingMultiFingerMoveThreshold() + && isLocationTracking()) { + detector.setMoveThreshold(options.trackingMultiFingerMoveThreshold()); + interrupt = true; + } + } + + @Override + public void onMove(MoveGestureDetector detector) { + if (interrupt) { + detector.interrupt(); + return; + } + + setCameraMode(CameraMode.NONE); + } + + @Override + public void onMoveEnd(MoveGestureDetector detector) { + if (!interrupt && isLocationTracking()) { + moveGestureDetector.setMoveThreshold(options.trackingInitialMoveThreshold()); + } + interrupt = false; + } + }; + + private MapboxMap.OnRotateListener onRotateListener = new MapboxMap.OnRotateListener() { + @Override + public void onRotateBegin(RotateGestureDetector detector) { + if (isBearingTracking()) { + setCameraMode(CameraMode.NONE); + } + } + + @Override + public void onRotate(RotateGestureDetector detector) { + // no implementation + } + + @Override + public void onRotateEnd(RotateGestureDetector detector) { + // no implementation + } + }; + + private MapboxMap.OnFlingListener onFlingListener = new MapboxMap.OnFlingListener() { + @Override + public void onFling() { + setCameraMode(CameraMode.NONE); + } + }; + + private class LocationGesturesManager extends AndroidGesturesManager { + + public LocationGesturesManager(Context context) { + super(context); + } + + public LocationGesturesManager(Context context, boolean applyDefaultThresholds) { + super(context, applyDefaultThresholds); + } + + public LocationGesturesManager(Context context, Set[] exclusiveGestures) { + super(context, exclusiveGestures); + } + + public LocationGesturesManager(Context context, List> exclusiveGestures, + boolean applyDefaultThresholds) { + super(context, exclusiveGestures, applyDefaultThresholds); + } + + @Override + public boolean onTouchEvent(MotionEvent motionEvent) { + if (motionEvent != null) { + int action = motionEvent.getActionMasked(); + if (action == MotionEvent.ACTION_UP) { + adjustGesturesThresholds(); + } + } + return super.onTouchEvent(motionEvent); + } + } +} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java new file mode 100644 index 0000000000..4b0860998f --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java @@ -0,0 +1,1019 @@ +package com.mapbox.mapboxsdk.location; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.hardware.SensorManager; +import android.location.Location; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.RequiresPermission; +import android.support.annotation.StyleRes; +import android.support.v7.app.AppCompatDelegate; +import android.view.WindowManager; + +import com.mapbox.android.core.location.LocationEngine; +import com.mapbox.android.core.location.LocationEngineListener; +import com.mapbox.android.core.location.LocationEnginePriority; +import com.mapbox.android.core.location.LocationEngineProvider; +import com.mapbox.mapboxsdk.R; +import com.mapbox.mapboxsdk.camera.CameraPosition; +import com.mapbox.mapboxsdk.camera.CameraUpdate; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.location.modes.CameraMode; +import com.mapbox.mapboxsdk.location.modes.RenderMode; +import com.mapbox.mapboxsdk.log.Logger; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraIdleListener; +import com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveListener; +import com.mapbox.mapboxsdk.maps.MapboxMap.OnMapClickListener; + +import java.util.concurrent.CopyOnWriteArrayList; + +import static android.Manifest.permission.ACCESS_COARSE_LOCATION; +import static android.Manifest.permission.ACCESS_FINE_LOCATION; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.DEFAULT_TRACKING_TILT_ANIM_DURATION; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.DEFAULT_TRACKING_ZOOM_ANIM_DURATION; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.STATE_LOCATION_CAMERA_MODE; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.STATE_LOCATION_ENABLED; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.STATE_LOCATION_LAST_LOCATION; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.STATE_LOCATION_OPTIONS; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.STATE_LOCATION_RENDER_MODE; + +/** + * The Location layer plugin provides location awareness to your mobile application. Enabling this + * plugin provides a contextual experience to your users by showing an icon representing the users + * current location. A few different modes are offered to provide the right context to your users at + * the correct time. {@link RenderMode#NORMAL} simply shows the users location on the map + * represented as a dot. {@link RenderMode#COMPASS} mode allows you to display an arrow icon + * (by default) that points in the direction the device is pointing in. + * {@link RenderMode#GPS} can be used in conjunction with our Navigation SDK to + * display a larger icon (customized with {@link LocationComponentOptions#gpsDrawable()}) we call the user puck. + *

+ * This plugin also offers the ability to set a map camera behavior for tracking the user + * location. These different {@link CameraMode}s will track, stop tracking the location based on the + * mode set with {@link LocationComponent#setCameraMode(int)}. + *

+ * Lastly, {@link LocationComponent#setLocationComponentEnabled(boolean)} can be used + * to disable the Location Layer but keep the instance around till the activity is destroyed. + *

+ * Using this plugin requires you to request permission beforehand manually or using + * {@link com.mapbox.android.core.permissions.PermissionsManager}. Either + * {@code ACCESS_COARSE_LOCATION} or {@code ACCESS_FINE_LOCATION} permissions can be requested and + * this plugin work as expected. + *

+ * When instantiating the plugin for the first time, the map's max/min zoom levels will be set to + * {@link LocationComponentOptions#MAX_ZOOM_DEFAULT} and {@link LocationComponentOptions#MIN_ZOOM_DEFAULT} respectively. + * You can adjust the zoom range with {@link LocationComponentOptions#maxZoom()} and + * {@link LocationComponentOptions#minZoom()}. + *

+ * When an activity, or a fragment, that contains the plugin is destroyed and recreated, + * the plugin will restore its state, which is: + *
+ * - If the plugin was enabled, last location will be displayed. + * You still need to activate the plugin, or just provide the {@link LocationEngine}. + *
+ * - {@link CameraMode} and {@link RenderMode} will be restored. + *
+ * - {@link LocationComponentOptions} will be restored. + */ +public final class LocationComponent { + private static final String TAG = "Mbgl-LocationComponent"; + + private final MapboxMap mapboxMap; + private LocationComponentOptions options; + private LocationEngine locationEngine; + private CompassEngine compassEngine; + private boolean usingInternalLocationEngine; + + private LocationLayerController locationLayerController; + private LocationCameraController locationCameraController; + + private LocationAnimatorCoordinator locationAnimatorCoordinator; + + /** + * Holds last location which is being returned in the {@link #getLastKnownLocation()} + * when there is no {@link #locationEngine} set or when the last location returned by the engine is null. + */ + private Location lastLocation; + private CameraPosition lastCameraPosition; + + /** + * Indicates that the plugin is enabled and should be displaying location if Mapbox components are available and + * the lifecycle is in a resumed state. + */ + private boolean isEnabled; + + /** + * Indicated that plugin's lifecycle {@link #onStart()} method has been called or the plugin is initialized.. + * This allows Mapbox components enter started state and display data, and adds state safety for methods like + * {@link #setLocationComponentEnabled(boolean)} + *

+ * Initialized in a started state because the plugin can be instantiated after lifecycle's onStart() and + * the developer might not register the lifecycle observer but call lifecycle methods manually instead. + */ + private boolean isComponentStarted; + + /** + * Indicates if Mapbox components are ready to be interacted with. This can differ from {@link #isComponentStarted} + * if the Mapbox style is being reloaded. + */ + private boolean isLayerReady; + + private StaleStateManager staleStateManager; + private final CopyOnWriteArrayList onLocationStaleListeners + = new CopyOnWriteArrayList<>(); + private final CopyOnWriteArrayList onLocationComponentClickListeners + = new CopyOnWriteArrayList<>(); + private final CopyOnWriteArrayList onLocationComponentLongClickListeners + = new CopyOnWriteArrayList<>(); + private final CopyOnWriteArrayList onCameraTrackingChangedListeners + = new CopyOnWriteArrayList<>(); + + /** + * Construct a LocationComponent. In order to display location, + * the location layer has to be activated with {@link LocationComponent#activateLocationComponent(Context)}, + * or one of the overloads. + * + * @param mapboxMap the MapboxMap to apply the LocationComponent with + */ + public LocationComponent(@NonNull Context context, @NonNull MapboxMap mapboxMap) { + this.mapboxMap = mapboxMap; + options = LocationComponentOptions.createFromAttributes(context, R.style.mapbox_LocationComponent); + initialize(context); + } + + /** + * This method will show or hide the location icon and enable or disable the camera + * tracking the location. + * + * @param isEnabled true to show layers and enable camera, false otherwise + */ + private void setLocationComponentEnabled(boolean isEnabled) { + if (isEnabled) { + enableLocationComponent(); + } else { + disableLocationComponent(); + } + } + + /** + * This method will show the location icon and enable the camera tracking the location. + *

+ * Note: This method will initialize and use an internal {@link LocationEngine}. + * + * @param context the context + */ + @RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION}) + public void activateLocationComponent(@NonNull Context context) { + activateLocationComponent(context, LocationComponentOptions.createFromAttributes(context, R.style + .mapbox_LocationComponent)); + } + + /** + * This method will show the location icon and enable the camera tracking the location. + * + * @param context the context + * @param useDefaultLocationEngine true if you want to initialize and use the built-in location engine or false if + * there should be no location engine initialized + */ + @RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION}) + public void activateLocationComponent(@NonNull Context context, boolean useDefaultLocationEngine) { + if (useDefaultLocationEngine) { + activateLocationComponent(context, R.style.mapbox_LocationComponent); + } else { + activateLocationComponent(context, null, R.style.mapbox_LocationComponent); + } + } + + /** + * This method will show the location icon and enable the camera tracking the location. + *

+ * Note: This method will initialize and use an internal {@link LocationEngine}. + * + * @param context the context + * @param styleRes the LocationComponent style res + */ + @RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION}) + public void activateLocationComponent(@NonNull Context context, @StyleRes int styleRes) { + activateLocationComponent(context, LocationComponentOptions.createFromAttributes(context, styleRes)); + } + + /** + * This method will show the location icon and enable the camera tracking the location. + *

+ * Note: This method will initialize and use an internal {@link LocationEngine}. + *

+ * + * @param context the context + * @param options the options + */ + @RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION}) + public void activateLocationComponent(@NonNull Context context, @NonNull LocationComponentOptions options) { + applyStyle(options); + initializeLocationEngine(context); + setLocationComponentEnabled(true); + } + + /** + * This method will show the location icon and enable the camera tracking the location. + * + * @param context the context + * @param locationEngine the engine, or null if you'd like to only force location updates + * @param styleRes the LocationComponent style res + */ + public void activateLocationComponent(@NonNull Context context, @Nullable LocationEngine locationEngine, + @StyleRes int styleRes) { + activateLocationComponent(locationEngine, LocationComponentOptions.createFromAttributes(context, styleRes)); + } + + /** + * This method will show the location icon and enable the camera tracking the location. + * + * @param context the context + * @param locationEngine the engine + */ + public void activateLocationComponent(@NonNull Context context, @NonNull LocationEngine locationEngine) { + activateLocationComponent(context, locationEngine, R.style.mapbox_LocationComponent); + } + + /** + * This method will show the location icon and enable the camera tracking the location. + * + * @param locationEngine the engine, or null if you'd like to only force location updates + * @param options the options + */ + public void activateLocationComponent(@Nullable LocationEngine locationEngine, + @NonNull LocationComponentOptions options) { + setLocationEngine(locationEngine); + applyStyle(options); + setLocationComponentEnabled(true); + } + + /** + * This method will hide the location icon and disable the camera tracking the location. + */ + public void deactivateLocationComponent() { + setLocationComponentEnabled(false); + } + + /** + * Returns whether the plugin is enabled, meaning that location can be displayed and camera modes can be used. + * + * @return true if the plugin is enabled, false otherwise + */ + public boolean isLocationLayerEnabled() { + return isEnabled; + } + + /** + * Sets the camera mode, which determines how the map camera will track the rendered location. + *

+ *

    + *
  • {@link CameraMode#NONE}: No camera tracking
  • + *
  • {@link CameraMode#NONE_COMPASS}: Camera does not track location, but does track compass bearing
  • + *
  • {@link CameraMode#NONE_GPS}: Camera does not track location, but does track GPS bearing
  • + *
  • {@link CameraMode#TRACKING}: Camera tracks the user location
  • + *
  • {@link CameraMode#TRACKING_COMPASS}: Camera tracks the user location, with bearing provided by a compass
  • + *
  • {@link CameraMode#TRACKING_GPS}: Camera tracks the user location, with normalized bearing
  • + *
  • {@link CameraMode#TRACKING_GPS_NORTH}: Camera tracks the user location, with bearing always set to north
  • + *
+ * + * @param cameraMode one of the modes found in {@link CameraMode} + */ + public void setCameraMode(@CameraMode.Mode int cameraMode) { + locationCameraController.setCameraMode(cameraMode); + boolean isGpsNorth = cameraMode == CameraMode.TRACKING_GPS_NORTH; + locationAnimatorCoordinator.resetAllCameraAnimations(mapboxMap.getCameraPosition(), isGpsNorth); + } + + /** + * Provides the current camera mode being used to track + * the location or compass updates. + * + * @return the current camera mode + */ + @CameraMode.Mode + public int getCameraMode() { + return locationCameraController.getCameraMode(); + } + + /** + * Sets the render mode, which determines how the location updates will be rendered on the map. + *

+ *

    + *
  • {@link RenderMode#NORMAL}: Shows user location, bearing ignored
  • + *
  • {@link RenderMode#COMPASS}: Shows user location with bearing considered from compass
  • + *
  • {@link RenderMode#GPS}: Shows user location with bearing considered from location
  • + *
+ * + * @param renderMode one of the modes found in {@link RenderMode} + */ + public void setRenderMode(@RenderMode.Mode int renderMode) { + locationLayerController.setRenderMode(renderMode); + updateLayerOffsets(true); + } + + /** + * Provides the current render mode being used to show + * the location and/or compass updates on the map. + * + * @return the current render mode + */ + @RenderMode.Mode + public int getRenderMode() { + return locationLayerController.getRenderMode(); + } + + /** + * Returns the current location options being used. + * + * @return the current {@link LocationComponentOptions} + */ + public LocationComponentOptions getLocationComponentOptions() { + return options; + } + + /** + * Apply a new LocationLayerController style with a style resource. + * + * @param styleRes a XML style overriding some or all the options + */ + public void applyStyle(@NonNull Context context, @StyleRes int styleRes) { + applyStyle(LocationComponentOptions.createFromAttributes(context, styleRes)); + } + + /** + * Apply a new LocationLayerController style with location layer options. + * + * @param options to update the current style + */ + public void applyStyle(LocationComponentOptions options) { + this.options = options; + locationLayerController.applyStyle(options); + staleStateManager.setEnabled(options.enableStaleState()); + staleStateManager.setDelayTime(options.staleStateTimeout()); + updateMapWithOptions(options); + } + + /** + * Zooms to the desired zoom level. + * This API can only be used in pair with camera modes other than {@link CameraMode#NONE}. + * If you are not using any of {@link CameraMode} modes, + * use one of {@link MapboxMap#moveCamera(CameraUpdate)}, + * {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead. + * + * @param zoomLevel The desired zoom level. + * @param animationDuration The zoom animation duration. + * @param callback The callback with finish/cancel information + */ + public void zoomWhileTracking(double zoomLevel, long animationDuration, + @Nullable MapboxMap.CancelableCallback callback) { + if (!isLayerReady) { + return; + } else if (getCameraMode() == CameraMode.NONE) { + Logger.e(TAG, String.format("%s%s", + "LocationComponent#zoomWhileTracking method can only be used", + " when a camera mode other than CameraMode#NONE is engaged.")); + return; + } + locationAnimatorCoordinator.feedNewZoomLevel(zoomLevel, mapboxMap.getCameraPosition(), animationDuration, callback); + } + + /** + * Zooms to the desired zoom level. + * This API can only be used in pair with camera modes other than {@link CameraMode#NONE}. + * If you are not using any of {@link CameraMode} modes, + * use one of {@link MapboxMap#moveCamera(CameraUpdate)}, + * {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead. + * + * @param zoomLevel The desired zoom level. + * @param animationDuration The zoom animation duration. + */ + public void zoomWhileTracking(double zoomLevel, long animationDuration) { + zoomWhileTracking(zoomLevel, animationDuration, null); + } + + /** + * Zooms to the desired zoom level. + * This API can only be used in pair with camera modes other than {@link CameraMode#NONE}. + * If you are not using any of {@link CameraMode} modes, + * use one of {@link MapboxMap#moveCamera(CameraUpdate)}, + * {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead. + * + * @param zoomLevel The desired zoom level. + */ + public void zoomWhileTracking(double zoomLevel) { + zoomWhileTracking(zoomLevel, DEFAULT_TRACKING_ZOOM_ANIM_DURATION, null); + } + + /** + * Cancels animation started by {@link #zoomWhileTracking(double, long, MapboxMap.CancelableCallback)}. + */ + public void cancelZoomWhileTrackingAnimation() { + locationAnimatorCoordinator.cancelZoomAnimation(); + } + + /** + * Tilts the camera. + * This API can only be used in pair with camera modes other than {@link CameraMode#NONE}. + * If you are not using any of {@link CameraMode} modes, + * use one of {@link MapboxMap#moveCamera(CameraUpdate)}, + * {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead. + * + * @param tilt The desired camera tilt. + * @param animationDuration The tilt animation duration. + * @param callback The callback with finish/cancel information + */ + public void tiltWhileTracking(double tilt, long animationDuration, + @Nullable MapboxMap.CancelableCallback callback) { + if (!isLayerReady) { + return; + } else if (getCameraMode() == CameraMode.NONE) { + Logger.e(TAG, String.format("%s%s", + "LocationComponent#tiltWhileTracking method can only be used", + " when a camera mode other than CameraMode#NONE is engaged.")); + return; + } + locationAnimatorCoordinator.feedNewTilt(tilt, mapboxMap.getCameraPosition(), animationDuration, callback); + } + + /** + * Tilts the camera. + * This API can only be used in pair with camera modes other than {@link CameraMode#NONE}. + * If you are not using any of {@link CameraMode} modes, + * use one of {@link MapboxMap#moveCamera(CameraUpdate)}, + * {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead. + * + * @param tilt The desired camera tilt. + * @param animationDuration The tilt animation duration. + */ + public void tiltWhileTracking(double tilt, long animationDuration) { + tiltWhileTracking(tilt, animationDuration, null); + } + + /** + * Tilts the camera. + * This API can only be used in pair with camera modes other than {@link CameraMode#NONE}. + * If you are not using any of {@link CameraMode} modes, + * use one of {@link MapboxMap#moveCamera(CameraUpdate)}, + * {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead. + * + * @param tilt The desired camera tilt. + */ + public void tiltWhileTracking(double tilt) { + tiltWhileTracking(tilt, DEFAULT_TRACKING_TILT_ANIM_DURATION, null); + } + + /** + * Cancels animation started by {@link #tiltWhileTracking(double, long, MapboxMap.CancelableCallback)}. + */ + public void cancelTiltWhileTrackingAnimation() { + locationAnimatorCoordinator.cancelTiltAnimation(); + } + + /** + * Use to either force a location update or to manually control when the user location gets + * updated. + * + * @param location where the location icon is placed on the map + */ + public void forceLocationUpdate(@Nullable Location location) { + updateLocation(location, false); + } + + /** + * Set the location engine to update the current user location. + *

+ * If {@code null} is passed in, all updates will occur through the + * {@link LocationComponent#forceLocationUpdate(Location)} method. + * + * @param locationEngine a {@link LocationEngine} this plugin should use to handle updates + */ + public void setLocationEngine(@Nullable LocationEngine locationEngine) { + if (this.locationEngine != null) { + // If internal location engines being used, extra steps need to be taken to deconstruct the + // instance. + if (usingInternalLocationEngine) { + this.locationEngine.removeLocationUpdates(); + this.locationEngine.deactivate(); + usingInternalLocationEngine = false; + } + this.locationEngine.removeLocationEngineListener(locationEngineListener); + this.locationEngine = null; + } + + if (locationEngine != null) { + this.locationEngine = locationEngine; + if (isEnabled) { + this.locationEngine.addLocationEngineListener(locationEngineListener); + } + } + } + + /** + * Returns the current {@link LocationEngine} being used for updating the user location layer. + * + * @return the {@link LocationEngine} being used to update the user location layer + */ + @Nullable + public LocationEngine getLocationEngine() { + return locationEngine; + } + + /** + * Sets the compass engine used to provide compass heading values. + * + * @param compassEngine to be used + */ + public void setCompassEngine(@NonNull CompassEngine compassEngine) { + this.compassEngine.removeCompassListener(compassListener); + this.compassEngine = compassEngine; + compassEngine.addCompassListener(compassListener); + } + + /** + * Returns the compass engine used to provide compass heading values. + * + * @return compass engine currently being used + */ + @NonNull + public CompassEngine getCompassEngine() { + return compassEngine; + } + + /** + * Get the last know location of the location layer plugin. + * + * @return the last known location + */ + @Nullable + @RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION}) + public Location getLastKnownLocation() { + Location location = locationEngine != null ? locationEngine.getLastLocation() : null; + if (location == null) { + location = lastLocation; + } + return location; + } + + /** + * Return the last known {@link CompassEngine} accuracy status of the location layer plugin. + *

+ * The last known accuracy of the compass sensor, one of SensorManager.SENSOR_STATUS_* + * + * @return the last know compass accuracy bearing + */ + public float getLastKnownCompassAccuracyStatus() { + return compassEngine.getLastAccuracySensorStatus(); + } + + /** + * Add a compass listener to get heading updates every second. Once the first listener gets added, + * the sensor gets initiated and starts returning values. + * + * @param compassListener a {@link CompassListener} for listening into compass heading and + * accuracy changes + */ + public void addCompassListener(@NonNull CompassListener compassListener) { + compassEngine.addCompassListener(compassListener); + } + + /** + * Remove a compass listener. + * + * @param compassListener the {@link CompassListener} which you'd like to remove from the listener + * list. + */ + public void removeCompassListener(@NonNull CompassListener compassListener) { + compassEngine.removeCompassListener(compassListener); + } + + /** + * Adds a listener that gets invoked when the user clicks the location layer. + * + * @param listener The location layer click listener that is invoked when the + * location layer is clicked + */ + public void addOnLocationClickListener(@NonNull OnLocationComponentClickListener listener) { + onLocationComponentClickListeners.add(listener); + } + + /** + * Removes the passed listener from the current list of location click listeners. + * + * @param listener to be removed + */ + public void removeOnLocationClickListener(@NonNull OnLocationComponentClickListener listener) { + onLocationComponentClickListeners.remove(listener); + } + + /** + * Adds a listener that gets invoked when the user long clicks the location layer. + * + * @param listener The location layer click listener that is invoked when the + * location layer is clicked + */ + public void addOnLocationLongClickListener(@NonNull OnLocationComponentLongClickListener listener) { + onLocationComponentLongClickListeners.add(listener); + } + + /** + * Removes the passed listener from the current list of location long click listeners. + * + * @param listener to be removed + */ + public void removeOnLocationLongClickListener(@NonNull OnLocationComponentLongClickListener listener) { + onLocationComponentLongClickListeners.remove(listener); + } + + /** + * Adds a listener that gets invoked when camera tracking state changes. + * + * @param listener Listener that gets invoked when camera tracking state changes. + */ + public void addOnCameraTrackingChangedListener(@NonNull OnCameraTrackingChangedListener listener) { + onCameraTrackingChangedListeners.add(listener); + } + + /** + * Removes a listener that gets invoked when camera tracking state changes. + * + * @param listener Listener that gets invoked when camera tracking state changes. + */ + public void removeOnCameraTrackingChangedListener(@NonNull OnCameraTrackingChangedListener listener) { + onCameraTrackingChangedListeners.remove(listener); + } + + /** + * Adds the passed listener that gets invoked when user updates have stopped long enough for the last update + * to be considered stale. + *

+ * This timeout is set by {@link LocationComponentOptions#staleStateTimeout()}. + * + * @param listener invoked when last update is considered stale + */ + public void addOnLocationStaleListener(@NonNull OnLocationStaleListener listener) { + onLocationStaleListeners.add(listener); + } + + /** + * Removes the passed listener from the current list of stale listeners. + * + * @param listener to be removed from the list + */ + public void removeOnLocationStaleListener(@NonNull OnLocationStaleListener listener) { + onLocationStaleListeners.remove(listener); + } + + /** + * Internal use. + */ + public void onStart() { + isComponentStarted = true; + onLocationLayerStart(); + } + + /** + * Internal use. + */ + public void onStop() { + onLocationLayerStop(); + isComponentStarted = false; + } + + /** + * Internal use. + */ + public void onSaveInstanceState(@NonNull Bundle outState) { + outState.putBoolean(STATE_LOCATION_ENABLED, isEnabled); + outState.putParcelable(STATE_LOCATION_OPTIONS, options); + outState.putInt(STATE_LOCATION_RENDER_MODE, locationLayerController.getRenderMode()); + outState.putInt(STATE_LOCATION_CAMERA_MODE, locationCameraController.getCameraMode()); + outState.putParcelable(STATE_LOCATION_LAST_LOCATION, lastLocation); + } + + /** + * Internal use. + */ + public void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { + updateLocation(savedInstanceState.getParcelable(STATE_LOCATION_LAST_LOCATION), true); + setCameraMode(savedInstanceState.getInt(STATE_LOCATION_CAMERA_MODE)); + setRenderMode(savedInstanceState.getInt(STATE_LOCATION_RENDER_MODE)); + applyStyle(savedInstanceState.getParcelable(STATE_LOCATION_OPTIONS)); + setLocationComponentEnabled(savedInstanceState.getBoolean(STATE_LOCATION_ENABLED)); + } + + /** + * Internal use. + */ + public void onDestroy() { + if (locationEngine != null && usingInternalLocationEngine) { + locationEngine.deactivate(); + } + } + + /** + * Internal use. + */ + public void onStartLoadingMap() { + onLocationLayerStop(); + } + + /** + * Internal use. + */ + public void onFinishLoadingStyle() { + locationLayerController.initializeComponents(options); + locationCameraController.initializeOptions(options); + onLocationLayerStart(); + } + + @SuppressLint("MissingPermission") + private void onLocationLayerStart() { + if (!isComponentStarted) { + return; + } + + if (!isLayerReady) { + isLayerReady = true; + if (mapboxMap != null) { + mapboxMap.addOnCameraMoveListener(onCameraMoveListener); + mapboxMap.addOnCameraIdleListener(onCameraIdleListener); + } + if (options.enableStaleState()) { + staleStateManager.onStart(); + } + compassEngine.onStart(); + } + + if (isEnabled) { + if (locationEngine != null) { + locationEngine.addLocationEngineListener(locationEngineListener); + if (locationEngine.isConnected() && usingInternalLocationEngine) { + locationEngine.requestLocationUpdates(); + } + } + setCameraMode(locationCameraController.getCameraMode()); + setLastLocation(); + setLastCompassHeading(); + } + } + + private void onLocationLayerStop() { + if (!isLayerReady || !isComponentStarted) { + return; + } + + isLayerReady = false; + locationLayerController.hide(); + staleStateManager.onStop(); + compassEngine.onStop(); + locationAnimatorCoordinator.cancelAllAnimations(); + if (locationEngine != null) { + if (usingInternalLocationEngine) { + locationEngine.removeLocationUpdates(); + } + locationEngine.removeLocationEngineListener(locationEngineListener); + } + if (mapboxMap != null) { + mapboxMap.removeOnCameraMoveListener(onCameraMoveListener); + mapboxMap.removeOnCameraIdleListener(onCameraIdleListener); + } + } + + private void initialize(@NonNull Context context) { + AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); + + mapboxMap.addOnMapClickListener(onMapClickListener); + mapboxMap.addOnMapLongClickListener(onMapLongClickListener); + + LayerSourceProvider sourceProvider = new LayerSourceProvider(); + LayerFeatureProvider featureProvider = new LayerFeatureProvider(); + LayerBitmapProvider bitmapProvider = new LayerBitmapProvider(context); + locationLayerController = new LocationLayerController(mapboxMap, sourceProvider, featureProvider, bitmapProvider, + options); + locationCameraController = new LocationCameraController( + context, mapboxMap, cameraTrackingChangedListener, options, onCameraMoveInvalidateListener); + locationAnimatorCoordinator = new LocationAnimatorCoordinator(); + locationAnimatorCoordinator.addLayerListener(locationLayerController); + locationAnimatorCoordinator.addCameraListener(locationCameraController); + + WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + compassEngine = new LocationComponentCompassEngine(windowManager, sensorManager); + compassEngine.addCompassListener(compassListener); + staleStateManager = new StaleStateManager(onLocationStaleListener, options); + + updateMapWithOptions(options); + + setRenderMode(RenderMode.NORMAL); + setCameraMode(CameraMode.NONE); + } + + private void initializeLocationEngine(@NonNull Context context) { + if (this.locationEngine != null) { + if (usingInternalLocationEngine) { + this.locationEngine.removeLocationUpdates(); + this.locationEngine.deactivate(); + } + this.locationEngine.removeLocationEngineListener(locationEngineListener); + } + + usingInternalLocationEngine = true; + locationEngine = new LocationEngineProvider(context).obtainBestLocationEngineAvailable(); + locationEngine.setPriority(LocationEnginePriority.HIGH_ACCURACY); + locationEngine.setFastestInterval(1000); + locationEngine.addLocationEngineListener(locationEngineListener); + locationEngine.activate(); + } + + private void enableLocationComponent() { + isEnabled = true; + onLocationLayerStart(); + } + + private void disableLocationComponent() { + isEnabled = false; + onLocationLayerStop(); + } + + private void updateMapWithOptions(final LocationComponentOptions options) { + mapboxMap.setPadding( + options.padding()[0], options.padding()[1], options.padding()[2], options.padding()[3] + ); + + mapboxMap.setMaxZoomPreference(options.maxZoom()); + mapboxMap.setMinZoomPreference(options.minZoom()); + } + + /** + * Updates the user location icon. + * + * @param location the latest user location + */ + private void updateLocation(final Location location, boolean fromLastLocation) { + if (location == null) { + return; + } else if (!isLayerReady) { + lastLocation = location; + return; + } + + if (isEnabled && isComponentStarted) { + locationLayerController.show(); + } + + if (!fromLastLocation) { + staleStateManager.updateLatestLocationTime(); + } + CameraPosition currentCameraPosition = mapboxMap.getCameraPosition(); + boolean isGpsNorth = getCameraMode() == CameraMode.TRACKING_GPS_NORTH; + locationAnimatorCoordinator.feedNewLocation(location, currentCameraPosition, isGpsNorth); + updateAccuracyRadius(location, false); + lastLocation = location; + } + + private void updateCompassHeading(float heading) { + locationAnimatorCoordinator.feedNewCompassBearing(heading, mapboxMap.getCameraPosition()); + } + + /** + * If the locationEngine contains a last location value, we use it for the initial location layer + * position. + */ + @SuppressLint("MissingPermission") + private void setLastLocation() { + updateLocation(getLastKnownLocation(), true); + } + + private void setLastCompassHeading() { + updateCompassHeading(compassEngine.getLastHeading()); + } + + @SuppressLint("MissingPermission") + private void updateLayerOffsets(boolean forceUpdate) { + CameraPosition position = mapboxMap.getCameraPosition(); + if (lastCameraPosition == null || forceUpdate) { + lastCameraPosition = position; + locationLayerController.updateForegroundBearing((float) position.bearing); + locationLayerController.updateForegroundOffset(position.tilt); + updateAccuracyRadius(getLastKnownLocation(), true); + return; + } + + if (position.bearing != lastCameraPosition.bearing) { + locationLayerController.updateForegroundBearing((float) position.bearing); + } + if (position.tilt != lastCameraPosition.tilt) { + locationLayerController.updateForegroundOffset(position.tilt); + } + if (position.zoom != lastCameraPosition.zoom) { + updateAccuracyRadius(getLastKnownLocation(), true); + } + lastCameraPosition = position; + } + + private void updateAccuracyRadius(Location location, boolean noAnimation) { + locationAnimatorCoordinator.feedNewAccuracyRadius(Utils.calculateZoomLevelRadius(mapboxMap, location), noAnimation); + } + + private OnCameraMoveListener onCameraMoveListener = new OnCameraMoveListener() { + @Override + public void onCameraMove() { + updateLayerOffsets(false); + } + }; + + private OnCameraIdleListener onCameraIdleListener = new OnCameraIdleListener() { + @Override + public void onCameraIdle() { + updateLayerOffsets(false); + } + }; + + private OnMapClickListener onMapClickListener = new OnMapClickListener() { + @Override + public void onMapClick(@NonNull LatLng point) { + if (!onLocationComponentClickListeners.isEmpty() && locationLayerController.onMapClick(point)) { + for (OnLocationComponentClickListener listener : onLocationComponentClickListeners) { + listener.onLocationComponentClick(); + } + } + } + }; + + private MapboxMap.OnMapLongClickListener onMapLongClickListener = new MapboxMap.OnMapLongClickListener() { + @Override + public void onMapLongClick(@NonNull LatLng point) { + if (!onLocationComponentLongClickListeners.isEmpty() && locationLayerController.onMapClick(point)) { + for (OnLocationComponentLongClickListener listener : onLocationComponentLongClickListeners) { + listener.onLocationComponentLongClick(); + } + } + } + }; + + private OnLocationStaleListener onLocationStaleListener = new OnLocationStaleListener() { + @Override + public void onStaleStateChange(boolean isStale) { + locationLayerController.setLocationsStale(isStale); + + for (OnLocationStaleListener listener : onLocationStaleListeners) { + listener.onStaleStateChange(isStale); + } + } + }; + + private OnCameraMoveInvalidateListener onCameraMoveInvalidateListener = new OnCameraMoveInvalidateListener() { + @Override + public void onInvalidateCameraMove() { + onCameraMoveListener.onCameraMove(); + } + }; + + private CompassListener compassListener = new CompassListener() { + @Override + public void onCompassChanged(float userHeading) { + updateCompassHeading(userHeading); + } + + @Override + public void onCompassAccuracyChange(int compassStatus) { + // Currently don't handle this inside SDK + } + }; + + private LocationEngineListener locationEngineListener = new LocationEngineListener() { + @Override + @SuppressWarnings( {"MissingPermission"}) + public void onConnected() { + if (usingInternalLocationEngine && isLayerReady && isEnabled) { + locationEngine.requestLocationUpdates(); + } + } + + @Override + public void onLocationChanged(Location location) { + updateLocation(location, false); + } + }; + + private OnCameraTrackingChangedListener cameraTrackingChangedListener = new OnCameraTrackingChangedListener() { + @Override + public void onCameraTrackingDismissed() { + for (OnCameraTrackingChangedListener listener : onCameraTrackingChangedListeners) { + listener.onCameraTrackingDismissed(); + } + } + + @Override + public void onCameraTrackingChanged(int currentMode) { + locationAnimatorCoordinator.cancelZoomAnimation(); + locationAnimatorCoordinator.cancelTiltAnimation(); + for (OnCameraTrackingChangedListener listener : onCameraTrackingChangedListeners) { + listener.onCameraTrackingChanged(currentMode); + } + } + }; +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentCompassEngine.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentCompassEngine.java new file mode 100644 index 0000000000..b53d909de3 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentCompassEngine.java @@ -0,0 +1,267 @@ +package com.mapbox.mapboxsdk.location; + +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.os.SystemClock; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.view.Surface; +import android.view.WindowManager; + +import java.util.ArrayList; +import java.util.List; + +import timber.log.Timber; + +/** + * This manager class handles compass events such as starting the tracking of device bearing, or + * when a new compass update occurs. + */ +class LocationComponentCompassEngine implements CompassEngine, SensorEventListener { + + // The rate sensor events will be delivered at. As the Android documentation states, this is only + // a hint to the system and the events might actually be received faster or slower then this + // specified rate. Since the minimum Android API levels about 9, we are able to set this value + // ourselves rather than using one of the provided constants which deliver updates too quickly for + // our use case. The default is set to 100ms + private static final int SENSOR_DELAY_MICROS = 100 * 1000; + // Filtering coefficient 0 < ALPHA < 1 + private static final float ALPHA = 0.45f; + + private final WindowManager windowManager; + private final SensorManager sensorManager; + private final List compassListeners = new ArrayList<>(); + + // Not all devices have a compassSensor + @Nullable + private Sensor compassSensor; + @Nullable + private Sensor gravitySensor; + @Nullable + private Sensor magneticFieldSensor; + + private float[] truncatedRotationVectorValue = new float[4]; + private float[] rotationMatrix = new float[9]; + private float[] rotationVectorValue; + private float lastHeading; + private int lastAccuracySensorStatus; + + private long compassUpdateNextTimestamp; + private float[] gravityValues = new float[3]; + private float[] magneticValues = new float[3]; + + /** + * Construct a new instance of the this class. A internal compass listeners needed to separate it + * from the cleared list of public listeners. + */ + LocationComponentCompassEngine(WindowManager windowManager, SensorManager sensorManager) { + this.windowManager = windowManager; + this.sensorManager = sensorManager; + compassSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR); + if (compassSensor == null) { + if (isGyroscopeAvailable()) { + Timber.d("Rotation vector sensor not supported on device, falling back to orientation."); + compassSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION); + } else { + Timber.d("Rotation vector sensor not supported on device, falling back to accelerometer and magnetic field."); + gravitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); + magneticFieldSensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); + } + } + } + + @Override + public void addCompassListener(@NonNull CompassListener compassListener) { + if (compassListeners.isEmpty()) { + onStart(); + } + compassListeners.add(compassListener); + } + + @Override + public void removeCompassListener(@NonNull CompassListener compassListener) { + compassListeners.remove(compassListener); + if (compassListeners.isEmpty()) { + onStop(); + } + } + + @Override + public int getLastAccuracySensorStatus() { + return lastAccuracySensorStatus; + } + + @Override + public float getLastHeading() { + return lastHeading; + } + + @Override + public void onStart() { + registerSensorListeners(); + } + + @Override + public void onStop() { + unregisterSensorListeners(); + } + + @Override + public void onSensorChanged(SensorEvent event) { + // check when the last time the compass was updated, return if too soon. + long currentTime = SystemClock.elapsedRealtime(); + if (currentTime < compassUpdateNextTimestamp) { + return; + } + if (lastAccuracySensorStatus == SensorManager.SENSOR_STATUS_UNRELIABLE) { + Timber.d("Compass sensor is unreliable, device calibration is needed."); + return; + } + if (event.sensor.getType() == Sensor.TYPE_ROTATION_VECTOR) { + rotationVectorValue = getRotationVectorFromSensorEvent(event); + updateOrientation(); + + // Update the compassUpdateNextTimestamp + compassUpdateNextTimestamp = currentTime + LocationComponentConstants.COMPASS_UPDATE_RATE_MS; + } else if (event.sensor.getType() == Sensor.TYPE_ORIENTATION) { + notifyCompassChangeListeners((event.values[0] + 360) % 360); + } else if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { + gravityValues = lowPassFilter(getRotationVectorFromSensorEvent(event), gravityValues); + updateOrientation(); + } else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) { + magneticValues = lowPassFilter(getRotationVectorFromSensorEvent(event), magneticValues); + updateOrientation(); + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + if (lastAccuracySensorStatus != accuracy) { + for (CompassListener compassListener : compassListeners) { + compassListener.onCompassAccuracyChange(accuracy); + } + lastAccuracySensorStatus = accuracy; + } + } + + private boolean isGyroscopeAvailable() { + return sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) != null; + } + + @SuppressWarnings("SuspiciousNameCombination") + private void updateOrientation() { + if (rotationVectorValue != null) { + SensorManager.getRotationMatrixFromVector(rotationMatrix, rotationVectorValue); + } else { + // Get rotation matrix given the gravity and geomagnetic matrices + SensorManager.getRotationMatrix(rotationMatrix, null, gravityValues, magneticValues); + } + + final int worldAxisForDeviceAxisX; + final int worldAxisForDeviceAxisY; + + // Remap the axes as if the device screen was the instrument panel, + // and adjust the rotation matrix for the device orientation. + switch (windowManager.getDefaultDisplay().getRotation()) { + case Surface.ROTATION_90: + worldAxisForDeviceAxisX = SensorManager.AXIS_Z; + worldAxisForDeviceAxisY = SensorManager.AXIS_MINUS_X; + break; + case Surface.ROTATION_180: + worldAxisForDeviceAxisX = SensorManager.AXIS_MINUS_X; + worldAxisForDeviceAxisY = SensorManager.AXIS_MINUS_Z; + break; + case Surface.ROTATION_270: + worldAxisForDeviceAxisX = SensorManager.AXIS_MINUS_Z; + worldAxisForDeviceAxisY = SensorManager.AXIS_X; + break; + case Surface.ROTATION_0: + default: + worldAxisForDeviceAxisX = SensorManager.AXIS_X; + worldAxisForDeviceAxisY = SensorManager.AXIS_Z; + break; + } + + float[] adjustedRotationMatrix = new float[9]; + SensorManager.remapCoordinateSystem(rotationMatrix, worldAxisForDeviceAxisX, + worldAxisForDeviceAxisY, adjustedRotationMatrix); + + // Transform rotation matrix into azimuth/pitch/roll + float[] orientation = new float[3]; + SensorManager.getOrientation(adjustedRotationMatrix, orientation); + + // The x-axis is all we care about here. + notifyCompassChangeListeners((float) Math.toDegrees(orientation[0])); + } + + private void notifyCompassChangeListeners(float heading) { + for (CompassListener compassListener : compassListeners) { + compassListener.onCompassChanged(heading); + } + lastHeading = heading; + } + + private void registerSensorListeners() { + if (isCompassSensorAvailable()) { + // Does nothing if the sensors already registered. + sensorManager.registerListener(this, compassSensor, SENSOR_DELAY_MICROS); + } else { + sensorManager.registerListener(this, gravitySensor, SENSOR_DELAY_MICROS); + sensorManager.registerListener(this, magneticFieldSensor, SENSOR_DELAY_MICROS); + } + } + + private void unregisterSensorListeners() { + if (isCompassSensorAvailable()) { + sensorManager.unregisterListener(this, compassSensor); + } else { + sensorManager.unregisterListener(this, gravitySensor); + sensorManager.unregisterListener(this, magneticFieldSensor); + } + } + + private boolean isCompassSensorAvailable() { + return compassSensor != null; + } + + /** + * Helper function, that filters newValues, considering previous values + * + * @param newValues array of float, that contains new data + * @param smoothedValues array of float, that contains previous state + * @return float filtered array of float + */ + private float[] lowPassFilter(float[] newValues, float[] smoothedValues) { + if (smoothedValues == null) { + return newValues; + } + for (int i = 0; i < newValues.length; i++) { + smoothedValues[i] = smoothedValues[i] + ALPHA * (newValues[i] - smoothedValues[i]); + } + return smoothedValues; + } + + /** + * Pulls out the rotation vector from a SensorEvent, with a maximum length + * vector of four elements to avoid potential compatibility issues. + * + * @param event the sensor event + * @return the events rotation vector, potentially truncated + */ + @NonNull + private float[] getRotationVectorFromSensorEvent(@NonNull SensorEvent event) { + if (event.values.length > 4) { + // On some Samsung devices SensorManager.getRotationMatrixFromVector + // appears to throw an exception if rotation vector has length > 4. + // For the purposes of this class the first 4 values of the + // rotation vector are sufficient (see crbug.com/335298 for details). + // Only affects Android 4.3 + System.arraycopy(event.values, 0, truncatedRotationVectorValue, 0, 4); + return truncatedRotationVectorValue; + } else { + return event.values; + } + } +} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentConstants.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentConstants.java new file mode 100644 index 0000000000..854170d617 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentConstants.java @@ -0,0 +1,66 @@ +package com.mapbox.mapboxsdk.location; + +/** + * Contains all the constants being used for the Location layer. + */ +final class LocationComponentConstants { + + static final String STATE_LOCATION_ENABLED = "mapbox_location_locationEnabled"; + static final String STATE_LOCATION_OPTIONS = "mapbox_location_options"; + static final String STATE_LOCATION_LAST_LOCATION = "mapbox_location_lastLocation"; + static final String STATE_LOCATION_RENDER_MODE = "mapbox_location_renderMode"; + static final String STATE_LOCATION_CAMERA_MODE = "mapbox_location_cameraMode"; + + // Controls the compass update rate in milliseconds + static final int COMPASS_UPDATE_RATE_MS = 500; + + // Sets the transition animation duration when switching camera modes. + static final long TRANSITION_ANIMATION_DURATION_MS = 750; + + // Sets the max allowed time for the location icon animation from one LatLng to another. + static final long MAX_ANIMATION_DURATION_MS = 2000; + + // Sets the duration of change of accuracy radius when a different value is provided. + static final long ACCURACY_RADIUS_ANIMATION_DURATION = 250; + + // Default animation duration for zooming while tracking. + static final long DEFAULT_TRACKING_ZOOM_ANIM_DURATION = 750; + + // Default animation duration for tilting while tracking. + static final long DEFAULT_TRACKING_TILT_ANIM_DURATION = 1250; + + // Sources + static final String LOCATION_SOURCE = "mapbox-location-source"; + static final String PROPERTY_GPS_BEARING = "mapbox-property-gps-bearing"; + static final String PROPERTY_COMPASS_BEARING = "mapbox-property-compass-bearing"; + static final String PROPERTY_LOCATION_STALE = "mapbox-property-location-stale"; + static final String PROPERTY_ACCURACY_RADIUS = "mapbox-property-accuracy-radius"; + static final String PROPERTY_ACCURACY_COLOR = "mapbox-property-accuracy-color"; + static final String PROPERTY_ACCURACY_ALPHA = "mapbox-property-accuracy-alpha"; + static final String PROPERTY_FOREGROUND_ICON_OFFSET = "mapbox-property-foreground-icon-offset"; + static final String PROPERTY_SHADOW_ICON_OFFSET = "mapbox-property-shadow-icon-offset"; + static final String PROPERTY_FOREGROUND_ICON = "mapbox-property-foreground-icon"; + static final String PROPERTY_BACKGROUND_ICON = "mapbox-property-background-icon"; + static final String PROPERTY_FOREGROUND_STALE_ICON = "mapbox-property-foreground-stale-icon"; + static final String PROPERTY_BACKGROUND_STALE_ICON = "mapbox-property-background-stale-icon"; + static final String PROPERTY_BEARING_ICON = "mapbox-property-shadow-icon"; + + // Layers + static final String SHADOW_LAYER = "mapbox-location-shadow"; + static final String FOREGROUND_LAYER = "mapbox-location-layer"; + static final String BACKGROUND_LAYER = "mapbox-location-stroke-layer"; + static final String ACCURACY_LAYER = "mapbox-location-accuracy-layer"; + static final String BEARING_LAYER = "mapbox-location-bearing-layer"; + + // Icons + static final String FOREGROUND_ICON = "mapbox-location-icon"; + static final String BACKGROUND_ICON = "mapbox-location-stroke-icon"; + static final String FOREGROUND_STALE_ICON = "mapbox-location-stale-icon"; + static final String BACKGROUND_STALE_ICON = "mapbox-location-background-stale-icon"; + static final String SHADOW_ICON = "mapbox-location-shadow-icon"; + static final String BEARING_ICON = "mapbox-location-bearing-icon"; + + private LocationComponentConstants() { + // Class should not be initialized + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentOptions.java new file mode 100644 index 0000000000..9a50840926 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentOptions.java @@ -0,0 +1,1561 @@ +package com.mapbox.mapboxsdk.location; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.ColorInt; +import android.support.annotation.Dimension; +import android.support.annotation.DrawableRes; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.StyleRes; + +import com.mapbox.mapboxsdk.R; +import com.mapbox.mapboxsdk.constants.MapboxConstants; + +import java.util.Arrays; + +/** + * This class exposes options for the Location Layer Plugin. The options can be set by defining a + * style in your apps style.xml file and passing in directly into the {@link LocationComponent} + * class. Alternatively, if properties need to be changed at runtime depending on a specific state, + * you can build an instance of this class, setting the values you desire, and then passing it into + * either the {@link LocationComponent} constructor (if it isn't initialized yet) or + * {@link LocationComponent#applyStyle(LocationComponentOptions)}. + *

+ * When the {@link #createFromAttributes(Context, int)} methods called, any attributes not found + * inside the style will revert back to using their default set values. Likewise, when building a + * new {@link LocationComponentOptions} class using the builder, any options neglecting to be set will + * reset to their default values. + *

+ * If you would like to keep your custom style changes while modifying a single attribute, you can + * get the currently used options object using {@link LocationComponent#getLocationComponentOptions()} + * and it's {@code toBuilder} method to modify a single entry while also maintaining the other + * settings. Once your modifications have been made, you'll need to pass it back into the location + * layer plugin using {@link LocationComponent#applyStyle(LocationComponentOptions)}. + */ +public class LocationComponentOptions implements Parcelable { + + /** + * Default accuracy alpha + */ + private static final float ACCURACY_ALPHA_DEFAULT = 0.15f; + + /** + * Default max map zoom + */ + private static final float MAX_ZOOM_DEFAULT = 18; + + /** + * Default min map zoom + */ + private static final float MIN_ZOOM_DEFAULT = 2; + + /** + * Default icon scale factor when the map is zoomed out + */ + private static final float MIN_ZOOM_ICON_SCALE_DEFAULT = 0.6f; + + /** + * Default icon scale factor when the map is zoomed in + */ + private static final float MAX_ZOOM_ICON_SCALE_DEFAULT = 1f; + + /** + * Default map padding + */ + private static final int[] PADDING_DEFAULT = {0, 0, 0, 0}; + + /** + * The default value which is used when the stale state is enabled + */ + private static final long STALE_STATE_DELAY_MS = 30000; + + private float accuracyAlpha; + private int accuracyColor; + private int backgroundDrawableStale; + private String backgroundStaleName; + private int foregroundDrawableStale; + private String foregroundStaleName; + private int gpsDrawable; + private String gpsName; + private int foregroundDrawable; + private String foregroundName; + private int backgroundDrawable; + private String backgroundName; + private int bearingDrawable; + private String bearingName; + private Integer bearingTintColor; + private Integer foregroundTintColor; + private Integer backgroundTintColor; + private Integer foregroundStaleTintColor; + private Integer backgroundStaleTintColor; + private float elevation; + private boolean enableStaleState; + private long staleStateTimeout; + private int[] padding; + private double maxZoom; + private double minZoom; + private float maxZoomIconScale; + private float minZoomIconScale; + private float trackingInitialMoveThreshold; + private float trackingMultiFingerMoveThreshold; + private String layerBelow; + + public LocationComponentOptions( + float accuracyAlpha, + int accuracyColor, + int backgroundDrawableStale, + @Nullable String backgroundStaleName, + int foregroundDrawableStale, + @Nullable String foregroundStaleName, + int gpsDrawable, + @Nullable String gpsName, + int foregroundDrawable, + @Nullable String foregroundName, + int backgroundDrawable, + @Nullable String backgroundName, + int bearingDrawable, + @Nullable String bearingName, + @Nullable Integer bearingTintColor, + @Nullable Integer foregroundTintColor, + @Nullable Integer backgroundTintColor, + @Nullable Integer foregroundStaleTintColor, + @Nullable Integer backgroundStaleTintColor, + float elevation, + boolean enableStaleState, + long staleStateTimeout, + int[] padding, + double maxZoom, + double minZoom, + float maxZoomIconScale, + float minZoomIconScale, + float trackingInitialMoveThreshold, + float trackingMultiFingerMoveThreshold, + String layerBelow) { + this.accuracyAlpha = accuracyAlpha; + this.accuracyColor = accuracyColor; + this.backgroundDrawableStale = backgroundDrawableStale; + this.backgroundStaleName = backgroundStaleName; + this.foregroundDrawableStale = foregroundDrawableStale; + this.foregroundStaleName = foregroundStaleName; + this.gpsDrawable = gpsDrawable; + this.gpsName = gpsName; + this.foregroundDrawable = foregroundDrawable; + this.foregroundName = foregroundName; + this.backgroundDrawable = backgroundDrawable; + this.backgroundName = backgroundName; + this.bearingDrawable = bearingDrawable; + this.bearingName = bearingName; + this.bearingTintColor = bearingTintColor; + this.foregroundTintColor = foregroundTintColor; + this.backgroundTintColor = backgroundTintColor; + this.foregroundStaleTintColor = foregroundStaleTintColor; + this.backgroundStaleTintColor = backgroundStaleTintColor; + this.elevation = elevation; + this.enableStaleState = enableStaleState; + this.staleStateTimeout = staleStateTimeout; + if (padding == null) { + throw new NullPointerException("Null padding"); + } + this.padding = padding; + this.maxZoom = maxZoom; + this.minZoom = minZoom; + this.maxZoomIconScale = maxZoomIconScale; + this.minZoomIconScale = minZoomIconScale; + this.trackingInitialMoveThreshold = trackingInitialMoveThreshold; + this.trackingMultiFingerMoveThreshold = trackingMultiFingerMoveThreshold; + this.layerBelow = layerBelow; + } + + /** + * Construct a new Location Layer Options class using the attributes found within a style + * resource. It's important to note that you only need to define the attributes you plan to + * change and can safely ignore the other attributes which will be set to their default value. + * + * @param context your activity's context used for acquiring resources + * @param styleRes the style id where your custom attributes are defined + * @return a new {@link LocationComponentOptions} object with the settings you defined in your style + * resource + */ + public static LocationComponentOptions createFromAttributes(@NonNull Context context, + @StyleRes int styleRes) { + + TypedArray typedArray = context.obtainStyledAttributes( + styleRes, R.styleable.mapbox_LocationComponent); + + LocationComponentOptions.Builder builder = new LocationComponentOptions.Builder() + .enableStaleState(true) + .staleStateTimeout(STALE_STATE_DELAY_MS) + .maxZoom(MAX_ZOOM_DEFAULT) + .minZoom(MIN_ZOOM_DEFAULT) + .maxZoomIconScale(MAX_ZOOM_ICON_SCALE_DEFAULT) + .minZoomIconScale(MIN_ZOOM_ICON_SCALE_DEFAULT) + .padding(PADDING_DEFAULT); + + builder.foregroundDrawable(typedArray.getResourceId( + R.styleable.mapbox_LocationComponent_mapbox_foregroundDrawable, -1)); + if (typedArray.hasValue(R.styleable.mapbox_LocationComponent_mapbox_foregroundTintColor)) { + builder.foregroundTintColor(typedArray.getColor( + R.styleable.mapbox_LocationComponent_mapbox_foregroundTintColor, -1)); + } + builder.backgroundDrawable(typedArray.getResourceId( + R.styleable.mapbox_LocationComponent_mapbox_backgroundDrawable, -1)); + if (typedArray.hasValue(R.styleable.mapbox_LocationComponent_mapbox_backgroundTintColor)) { + builder.backgroundTintColor(typedArray.getColor( + R.styleable.mapbox_LocationComponent_mapbox_backgroundTintColor, -1)); + } + builder.foregroundDrawableStale(typedArray.getResourceId( + R.styleable.mapbox_LocationComponent_mapbox_foregroundDrawableStale, -1)); + if (typedArray.hasValue(R.styleable.mapbox_LocationComponent_mapbox_foregroundStaleTintColor)) { + builder.foregroundStaleTintColor(typedArray.getColor( + R.styleable.mapbox_LocationComponent_mapbox_foregroundStaleTintColor, -1)); + } + builder.backgroundDrawableStale(typedArray.getResourceId( + R.styleable.mapbox_LocationComponent_mapbox_backgroundDrawableStale, -1)); + if (typedArray.hasValue(R.styleable.mapbox_LocationComponent_mapbox_backgroundStaleTintColor)) { + builder.backgroundStaleTintColor(typedArray.getColor( + R.styleable.mapbox_LocationComponent_mapbox_backgroundStaleTintColor, -1)); + } + builder.bearingDrawable(typedArray.getResourceId( + R.styleable.mapbox_LocationComponent_mapbox_bearingDrawable, -1)); + if (typedArray.hasValue(R.styleable.mapbox_LocationComponent_mapbox_bearingTintColor)) { + builder.bearingTintColor(typedArray.getColor( + R.styleable.mapbox_LocationComponent_mapbox_bearingTintColor, -1)); + } + if (typedArray.hasValue(R.styleable.mapbox_LocationComponent_mapbox_enableStaleState)) { + builder.enableStaleState(typedArray.getBoolean( + R.styleable.mapbox_LocationComponent_mapbox_enableStaleState, true)); + } + if (typedArray.hasValue(R.styleable.mapbox_LocationComponent_mapbox_staleStateTimeout)) { + builder.staleStateTimeout(typedArray.getInteger( + R.styleable.mapbox_LocationComponent_mapbox_staleStateTimeout, (int) STALE_STATE_DELAY_MS)); + } + builder.gpsDrawable(typedArray.getResourceId( + R.styleable.mapbox_LocationComponent_mapbox_gpsDrawable, -1)); + float elevation = typedArray.getDimension( + R.styleable.mapbox_LocationComponent_mapbox_elevation, 0); + builder.accuracyColor(typedArray.getColor( + R.styleable.mapbox_LocationComponent_mapbox_accuracyColor, -1)); + builder.accuracyAlpha(typedArray.getFloat( + R.styleable.mapbox_LocationComponent_mapbox_accuracyAlpha, ACCURACY_ALPHA_DEFAULT)); + builder.elevation(elevation); + + builder.trackingInitialMoveThreshold(typedArray.getDimension( + R.styleable.mapbox_LocationComponent_mapbox_trackingInitialMoveThreshold, + context.getResources().getDimension(R.dimen.mapbox_locationComponentTrackingInitialMoveThreshold))); + + builder.trackingMultiFingerMoveThreshold(typedArray.getDimension( + R.styleable.mapbox_LocationComponent_mapbox_trackingMultiFingerMoveThreshold, + context.getResources().getDimension(R.dimen.mapbox_locationComponentTrackingMultiFingerMoveThreshold))); + + builder.padding(new int[] { + typedArray.getInt(R.styleable.mapbox_LocationComponent_mapbox_iconPaddingLeft, 0), + typedArray.getInt(R.styleable.mapbox_LocationComponent_mapbox_iconPaddingTop, 0), + typedArray.getInt(R.styleable.mapbox_LocationComponent_mapbox_iconPaddingRight, 0), + typedArray.getInt(R.styleable.mapbox_LocationComponent_mapbox_iconPaddingBottom, 0), + }); + + float maxZoom + = typedArray.getFloat(R.styleable.mapbox_LocationComponent_mapbox_maxZoom, MAX_ZOOM_DEFAULT); + if (maxZoom < MapboxConstants.MINIMUM_ZOOM || maxZoom > MapboxConstants.MAXIMUM_ZOOM) { + throw new IllegalArgumentException("Max zoom value must be within " + + MapboxConstants.MINIMUM_ZOOM + " and " + MapboxConstants.MAXIMUM_ZOOM); + } + + float minZoom + = typedArray.getFloat(R.styleable.mapbox_LocationComponent_mapbox_minZoom, MIN_ZOOM_DEFAULT); + if (minZoom < MapboxConstants.MINIMUM_ZOOM || minZoom > MapboxConstants.MAXIMUM_ZOOM) { + throw new IllegalArgumentException("Min zoom value must be within " + + MapboxConstants.MINIMUM_ZOOM + " and " + MapboxConstants.MAXIMUM_ZOOM); + } + + builder.maxZoom(maxZoom); + builder.minZoom(minZoom); + + builder.layerBelow( + typedArray.getString(R.styleable.mapbox_LocationComponent_mapbox_layer_below)); + + float minScale = typedArray.getFloat( + R.styleable.mapbox_LocationComponent_mapbox_minZoomIconScale, MIN_ZOOM_ICON_SCALE_DEFAULT); + float maxScale = typedArray.getFloat( + R.styleable.mapbox_LocationComponent_mapbox_maxZoomIconScale, MAX_ZOOM_ICON_SCALE_DEFAULT); + builder.minZoomIconScale(minScale); + builder.maxZoomIconScale(maxScale); + + typedArray.recycle(); + + return builder.build(); + } + + /** + * Takes the currently constructed {@link LocationComponentOptions} object and provides it's builder + * with all the values set matching the values in this instance. This allows you to modify a + * single attribute and then rebuild the object. + * + * @return the Location Layer builder which contains the values defined in this current instance + * as defaults. + */ + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Build a new instance of the {@link LocationComponentOptions} class with all the attributes set + * automatically to their defined defaults in this library. This allows you to adjust a few + * attributes while leaving the rest alone and maintaining their default behavior. + * + * @param context your activities context used to acquire the style resource + * @return the Location Layer builder which contains the default values defined by the style + * resource + */ + public static Builder builder(Context context) { + return LocationComponentOptions.createFromAttributes(context, + R.style.mapbox_LocationComponent).toBuilder(); + } + + /** + * Set the opacity of the accuracy view to a value from 0 to 1, where 0 means the accuracy view is + * completely transparent and 1 means the view is completely opaque. + * + * @return the opacity of the accuracy view + * @attr ref R.styleable#LocationLayer_accuracyAlpha + */ + public float accuracyAlpha() { + return accuracyAlpha; + } + + /** + * Solid color to use as the accuracy view color property. + * + * @return the color of the accuracy view + * @attr ref R.styleable#LocationLayer_accuracyColor + */ + @ColorInt + public int accuracyColor() { + return accuracyColor; + } + + /** + * Defines the drawable used for the stale background icon. + * + * @return the drawable resource ID + * @attr ref R.styleable#LocationLayer_backgroundDrawableStale + */ + @DrawableRes + public int backgroundDrawableStale() { + return backgroundDrawableStale; + } + + /** + * String image name, identical to one used in + * the first parameter of {@link com.mapbox.mapboxsdk.maps.MapboxMap#addImage(String, Bitmap)}, the + * plugin, will used this image in place of the provided or default mapbox_foregroundDrawableStale. + *

+ * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded + * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. + *

+ * + * @return String icon or maki-icon name + */ + @Nullable + public String backgroundStaleName() { + return backgroundStaleName; + } + + /** + * Defines the drawable used for the stale foreground icon. + * + * @return the drawable resource ID + * @attr ref R.styleable#LocationLayer_foregroundDrawableStale + */ + @DrawableRes + public int foregroundDrawableStale() { + return foregroundDrawableStale; + } + + /** + * String image name, identical to one used in + * the first parameter of {@link com.mapbox.mapboxsdk.maps.MapboxMap#addImage(String, Bitmap)}, the + * plugin, will used this image in place of the provided or default mapbox_foregroundDrawableStale. + *

+ * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded + * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. + *

+ * + * @return String icon or maki-icon name + */ + @Nullable + public String foregroundStaleName() { + return foregroundStaleName; + } + + /** + * Defines the drawable used for the navigation state icon. + * + * @return the drawable resource ID + * @attr ref R.styleable#LocationLayer_gpsDrawable + */ + @DrawableRes + public int gpsDrawable() { + return gpsDrawable; + } + + /** + * String image name, identical to one used in + * the first parameter of {@link com.mapbox.mapboxsdk.maps.MapboxMap#addImage(String, Bitmap)}, the + * plugin, will used this image in place of the provided or default mapbox_gpsDrawable. + *

+ * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded + * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. + *

+ * + * @return String icon or maki-icon name + */ + @Nullable + public String gpsName() { + return gpsName; + } + + /** + * Supply a Drawable that is to be rendered on top of all of the content in the Location Layer + * Plugin layer stack. + * + * @return the drawable resource used for the foreground layer + * @attr ref R.styleable#LocationLayer_foregroundDrawable + */ + @DrawableRes + public int foregroundDrawable() { + return foregroundDrawable; + } + + /** + * String image name, identical to one used in + * the first parameter of {@link com.mapbox.mapboxsdk.maps.MapboxMap#addImage(String, Bitmap)}, the + * plugin, will used this image in place of the provided or default mapbox_foregroundDrawable. + *

+ * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded + * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. + *

+ * + * @return String icon or maki-icon name + */ + @Nullable + public String foregroundName() { + return foregroundName; + } + + /** + * Defines the drawable used for the background state icon. + * + * @return the drawable resource ID + * @attr ref R.styleable#LocationLayer_backgroundDrawable + */ + @DrawableRes + public int backgroundDrawable() { + return backgroundDrawable; + } + + /** + * String image name, identical to one used in + * the first parameter of {@link com.mapbox.mapboxsdk.maps.MapboxMap#addImage(String, Bitmap)}, the + * plugin, will used this image in place of the provided or default mapbox_backgroundDrawable. + *

+ * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded + * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. + *

+ * + * @return String icon or maki-icon name + */ + @Nullable + public String backgroundName() { + return backgroundName; + } + + /** + * Defines the drawable used for the bearing icon. + * + * @return the drawable resource ID + * @attr ref R.styleable#LocationLayer_bearingDrawable + */ + @DrawableRes + public int bearingDrawable() { + return bearingDrawable; + } + + /** + * String image name, identical to one used in + * the first parameter of {@link com.mapbox.mapboxsdk.maps.MapboxMap#addImage(String, Bitmap)}, the + * plugin, will used this image in place of the provided or default mapbox_bearingDrawable. + *

+ * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded + * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. + *

+ * + * @return String icon or maki-icon name + */ + @Nullable + public String bearingName() { + return bearingName; + } + + /** + * Defines the bearing icon color as an integer. + * + * @return the color integer resource + * @attr ref R.styleable#LocationLayer_bearingTintColor + */ + @ColorInt + @Nullable + public Integer bearingTintColor() { + return bearingTintColor; + } + + /** + * Defines the foreground color as an integer. + * + * @return the color integer resource + * @attr ref R.styleable#LocationLayer_foregroundTintColor + */ + @ColorInt + @Nullable + public Integer foregroundTintColor() { + return foregroundTintColor; + } + + /** + * Defines the background color as an integer. + * + * @return the color integer resource + * @attr ref R.styleable#LocationLayer_backgroundTintColor + */ + @ColorInt + @Nullable + public Integer backgroundTintColor() { + return backgroundTintColor; + } + + /** + * Defines the foreground stale color as an integer. + * + * @return the color integer resource + * @attr ref R.styleable#LocationLayer_foregroundStaleTintColor + */ + @ColorInt + @Nullable + public Integer foregroundStaleTintColor() { + return foregroundStaleTintColor; + } + + /** + * Defines the background stale color as an integer. + * + * @return the color integer resource + * @attr ref R.styleable#LocationLayer_backgroundStaleTintColor + */ + @ColorInt + @Nullable + public Integer backgroundStaleTintColor() { + return backgroundStaleTintColor; + } + + /** + * Sets the base elevation of this view, in pixels. + * + * @return the elevation currently set for the location layer icon + * @attr ref R.styleable#LocationLayer_elevation + */ + @Dimension + public float elevation() { + return elevation; + } + + /** + * Enable or disable to stale state mode. This mode indicates to the user that the location being + * displayed on the map hasn't been updated in a specific amount of time. + * + * @return whether the stale state mode is enabled or not + * @attr ref R.styleable#LocationLayer_enableStaleState + */ + public boolean enableStaleState() { + return enableStaleState; + } + + /** + * Set the delay before the location icon becomes stale. The timer begins approximately when a new + * location update comes in and using this defined time, if an update hasn't occured by the end, + * the location is considered stale. + * + * @return the duration in milliseconds which it should take before the location layer is + * considered stale + * @attr ref R.styleable#LocationLayer_staleStateDelay + */ + public long staleStateTimeout() { + return staleStateTimeout; + } + + /** + * Sets the distance from the edges of the map view’s frame to the edges of the map + * view’s logical viewport. + *

+ *

+ * When the value of this property is equal to {0,0,0,0}, viewport + * properties such as `centerCoordinate` assume a viewport that matches the map + * view’s frame. Otherwise, those properties are inset, excluding part of the + * frame from the viewport. For instance, if the only the top edge is inset, the + * map center is effectively shifted downward. + *

+ * + * @return integer array of padding values + */ + @SuppressWarnings("mutable") + public int[] padding() { + return padding; + } + + /** + * The maximum zoom level the map can be displayed at. + * + * @return the maximum zoom level + */ + public double maxZoom() { + return maxZoom; + } + + /** + * The minimum zoom level the map can be displayed at. + * + * @return the minimum zoom level + */ + public double minZoom() { + return minZoom; + } + + /** + * The scale factor of the location icon when the map is zoomed in. Based on {@link #maxZoom()}. + * Scaling is linear. + * + * @return icon scale factor + */ + public float maxZoomIconScale() { + return maxZoomIconScale; + } + + /** + * The scale factor of the location icon when the map is zoomed out. Based on {@link #minZoom()}. + * Scaling is linear. + * + * @return icon scale factor + */ + public float minZoomIconScale() { + return minZoomIconScale; + } + + /** + * Minimum single pointer movement in pixels required to break camera tracking. + * + * @return the minimum movement + */ + public float trackingInitialMoveThreshold() { + return trackingInitialMoveThreshold; + } + + /** + * Minimum multi pointer movement in pixels required to break camera tracking (for example during scale gesture). + * + * @return the minimum movement + */ + public float trackingMultiFingerMoveThreshold() { + return trackingMultiFingerMoveThreshold; + } + + /** + * Gets the id of the layer to add the location layer above to. + * + * @return layerBelow the id of the layer to add the location layer above to + */ + public String layerBelow() { + return layerBelow; + } + + @Override + public String toString() { + return "LocationComponentOptions{" + + "accuracyAlpha=" + accuracyAlpha + ", " + + "accuracyColor=" + accuracyColor + ", " + + "backgroundDrawableStale=" + backgroundDrawableStale + ", " + + "backgroundStaleName=" + backgroundStaleName + ", " + + "foregroundDrawableStale=" + foregroundDrawableStale + ", " + + "foregroundStaleName=" + foregroundStaleName + ", " + + "gpsDrawable=" + gpsDrawable + ", " + + "gpsName=" + gpsName + ", " + + "foregroundDrawable=" + foregroundDrawable + ", " + + "foregroundName=" + foregroundName + ", " + + "backgroundDrawable=" + backgroundDrawable + ", " + + "backgroundName=" + backgroundName + ", " + + "bearingDrawable=" + bearingDrawable + ", " + + "bearingName=" + bearingName + ", " + + "bearingTintColor=" + bearingTintColor + ", " + + "foregroundTintColor=" + foregroundTintColor + ", " + + "backgroundTintColor=" + backgroundTintColor + ", " + + "foregroundStaleTintColor=" + foregroundStaleTintColor + ", " + + "backgroundStaleTintColor=" + backgroundStaleTintColor + ", " + + "elevation=" + elevation + ", " + + "enableStaleState=" + enableStaleState + ", " + + "staleStateTimeout=" + staleStateTimeout + ", " + + "padding=" + Arrays.toString(padding) + ", " + + "maxZoom=" + maxZoom + ", " + + "minZoom=" + minZoom + ", " + + "maxZoomIconScale=" + maxZoomIconScale + ", " + + "minZoomIconScale=" + minZoomIconScale + ", " + + "trackingInitialMoveThreshold=" + trackingInitialMoveThreshold + ", " + + "trackingMultiFingerMoveThreshold=" + trackingMultiFingerMoveThreshold + ", " + + "layerBelow=" + layerBelow + + "}"; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o instanceof LocationComponentOptions) { + LocationComponentOptions that = (LocationComponentOptions) o; + return (Float.floatToIntBits(this.accuracyAlpha) == Float.floatToIntBits(that.accuracyAlpha())) + && (this.accuracyColor == that.accuracyColor()) + && (this.backgroundDrawableStale == that.backgroundDrawableStale()) + && ((this.backgroundStaleName == null) ? (that.backgroundStaleName() == null) + : this.backgroundStaleName.equals(that.backgroundStaleName())) + && (this.foregroundDrawableStale == that.foregroundDrawableStale()) + && ((this.foregroundStaleName == null) ? (that.foregroundStaleName() == null) + : this.foregroundStaleName.equals(that.foregroundStaleName())) + && (this.gpsDrawable == that.gpsDrawable()) + && ((this.gpsName == null) ? (that.gpsName() == null) : this.gpsName.equals(that.gpsName())) + && (this.foregroundDrawable == that.foregroundDrawable()) + && ((this.foregroundName == null) ? (that.foregroundName() == null) + : this.foregroundName.equals(that.foregroundName())) + && (this.backgroundDrawable == that.backgroundDrawable()) + && ((this.backgroundName == null) ? (that.backgroundName() == null) + : this.backgroundName.equals(that.backgroundName())) + && (this.bearingDrawable == that.bearingDrawable()) + && ((this.bearingName == null) ? (that.bearingName() == null) + : this.bearingName.equals(that.bearingName())) + && ((this.bearingTintColor == null) ? (that.bearingTintColor() == null) + : this.bearingTintColor.equals(that.bearingTintColor())) + && ((this.foregroundTintColor == null) ? (that.foregroundTintColor() == null) + : this.foregroundTintColor.equals(that.foregroundTintColor())) + && ((this.backgroundTintColor == null) ? (that.backgroundTintColor() == null) + : this.backgroundTintColor.equals(that.backgroundTintColor())) + && ((this.foregroundStaleTintColor == null) ? (that.foregroundStaleTintColor() == null) + : this.foregroundStaleTintColor.equals(that.foregroundStaleTintColor())) + && ((this.backgroundStaleTintColor == null) ? (that.backgroundStaleTintColor() == null) + : this.backgroundStaleTintColor.equals(that.backgroundStaleTintColor())) + && (Float.floatToIntBits(this.elevation) == Float.floatToIntBits(that.elevation())) + && (this.enableStaleState == that.enableStaleState()) + && (this.staleStateTimeout == that.staleStateTimeout()) + && (Arrays.equals(this.padding, that.padding()) + && (Double.doubleToLongBits(this.maxZoom) == Double.doubleToLongBits(that.maxZoom())) + && (Double.doubleToLongBits(this.minZoom) == Double.doubleToLongBits(that.minZoom())) + && (Float.floatToIntBits(this.maxZoomIconScale) == Float.floatToIntBits(that.maxZoomIconScale())) + && (Float.floatToIntBits(this.minZoomIconScale) == Float.floatToIntBits(that.minZoomIconScale())) + && (Float.floatToIntBits(this.trackingInitialMoveThreshold) + == Float.floatToIntBits(that.trackingInitialMoveThreshold())) + && (Float.floatToIntBits(this.trackingMultiFingerMoveThreshold) + == Float.floatToIntBits(that.trackingMultiFingerMoveThreshold())) + && layerBelow.equals(that.layerBelow)); + } + return false; + } + + @Override + public int hashCode() { + int h$ = 1; + h$ *= 1000003; + h$ ^= Float.floatToIntBits(accuracyAlpha); + h$ *= 1000003; + h$ ^= accuracyColor; + h$ *= 1000003; + h$ ^= backgroundDrawableStale; + h$ *= 1000003; + h$ ^= (backgroundStaleName == null) ? 0 : backgroundStaleName.hashCode(); + h$ *= 1000003; + h$ ^= foregroundDrawableStale; + h$ *= 1000003; + h$ ^= (foregroundStaleName == null) ? 0 : foregroundStaleName.hashCode(); + h$ *= 1000003; + h$ ^= gpsDrawable; + h$ *= 1000003; + h$ ^= (gpsName == null) ? 0 : gpsName.hashCode(); + h$ *= 1000003; + h$ ^= foregroundDrawable; + h$ *= 1000003; + h$ ^= (foregroundName == null) ? 0 : foregroundName.hashCode(); + h$ *= 1000003; + h$ ^= backgroundDrawable; + h$ *= 1000003; + h$ ^= (backgroundName == null) ? 0 : backgroundName.hashCode(); + h$ *= 1000003; + h$ ^= bearingDrawable; + h$ *= 1000003; + h$ ^= (bearingName == null) ? 0 : bearingName.hashCode(); + h$ *= 1000003; + h$ ^= (bearingTintColor == null) ? 0 : bearingTintColor.hashCode(); + h$ *= 1000003; + h$ ^= (foregroundTintColor == null) ? 0 : foregroundTintColor.hashCode(); + h$ *= 1000003; + h$ ^= (backgroundTintColor == null) ? 0 : backgroundTintColor.hashCode(); + h$ *= 1000003; + h$ ^= (foregroundStaleTintColor == null) ? 0 : foregroundStaleTintColor.hashCode(); + h$ *= 1000003; + h$ ^= (backgroundStaleTintColor == null) ? 0 : backgroundStaleTintColor.hashCode(); + h$ *= 1000003; + h$ ^= Float.floatToIntBits(elevation); + h$ *= 1000003; + h$ ^= enableStaleState ? 1231 : 1237; + h$ *= 1000003; + h$ ^= (int) ((staleStateTimeout >>> 32) ^ staleStateTimeout); + h$ *= 1000003; + h$ ^= Arrays.hashCode(padding); + h$ *= 1000003; + h$ ^= (int) ((Double.doubleToLongBits(maxZoom) >>> 32) ^ Double.doubleToLongBits(maxZoom)); + h$ *= 1000003; + h$ ^= (int) ((Double.doubleToLongBits(minZoom) >>> 32) ^ Double.doubleToLongBits(minZoom)); + h$ *= 1000003; + h$ ^= Float.floatToIntBits(maxZoomIconScale); + h$ *= 1000003; + h$ ^= Float.floatToIntBits(minZoomIconScale); + h$ *= 1000003; + h$ ^= Float.floatToIntBits(trackingInitialMoveThreshold); + h$ *= 1000003; + h$ ^= Float.floatToIntBits(trackingMultiFingerMoveThreshold); + return h$; + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public LocationComponentOptions createFromParcel(Parcel in) { + return new LocationComponentOptions( + in.readFloat(), + in.readInt(), + in.readInt(), + in.readInt() == 0 ? in.readString() : null, + in.readInt(), + in.readInt() == 0 ? in.readString() : null, + in.readInt(), + in.readInt() == 0 ? in.readString() : null, + in.readInt(), + in.readInt() == 0 ? in.readString() : null, + in.readInt(), + in.readInt() == 0 ? in.readString() : null, + in.readInt(), + in.readInt() == 0 ? in.readString() : null, + in.readInt() == 0 ? in.readInt() : null, + in.readInt() == 0 ? in.readInt() : null, + in.readInt() == 0 ? in.readInt() : null, + in.readInt() == 0 ? in.readInt() : null, + in.readInt() == 0 ? in.readInt() : null, + in.readFloat(), + in.readInt() == 1, + in.readLong(), + in.createIntArray(), + in.readDouble(), + in.readDouble(), + in.readFloat(), + in.readFloat(), + in.readFloat(), + in.readFloat(), + in.readString() + ); + } + + @Override + public LocationComponentOptions[] newArray(int size) { + return new LocationComponentOptions[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeFloat(accuracyAlpha()); + dest.writeInt(accuracyColor()); + dest.writeInt(backgroundDrawableStale()); + if (backgroundStaleName() == null) { + dest.writeInt(1); + } else { + dest.writeInt(0); + dest.writeString(backgroundStaleName()); + } + dest.writeInt(foregroundDrawableStale()); + if (foregroundStaleName() == null) { + dest.writeInt(1); + } else { + dest.writeInt(0); + dest.writeString(foregroundStaleName()); + } + dest.writeInt(gpsDrawable()); + if (gpsName() == null) { + dest.writeInt(1); + } else { + dest.writeInt(0); + dest.writeString(gpsName()); + } + dest.writeInt(foregroundDrawable()); + if (foregroundName() == null) { + dest.writeInt(1); + } else { + dest.writeInt(0); + dest.writeString(foregroundName()); + } + dest.writeInt(backgroundDrawable()); + if (backgroundName() == null) { + dest.writeInt(1); + } else { + dest.writeInt(0); + dest.writeString(backgroundName()); + } + dest.writeInt(bearingDrawable()); + if (bearingName() == null) { + dest.writeInt(1); + } else { + dest.writeInt(0); + dest.writeString(bearingName()); + } + if (bearingTintColor() == null) { + dest.writeInt(1); + } else { + dest.writeInt(0); + dest.writeInt(bearingTintColor()); + } + if (foregroundTintColor() == null) { + dest.writeInt(1); + } else { + dest.writeInt(0); + dest.writeInt(foregroundTintColor()); + } + if (backgroundTintColor() == null) { + dest.writeInt(1); + } else { + dest.writeInt(0); + dest.writeInt(backgroundTintColor()); + } + if (foregroundStaleTintColor() == null) { + dest.writeInt(1); + } else { + dest.writeInt(0); + dest.writeInt(foregroundStaleTintColor()); + } + if (backgroundStaleTintColor() == null) { + dest.writeInt(1); + } else { + dest.writeInt(0); + dest.writeInt(backgroundStaleTintColor()); + } + dest.writeFloat(elevation()); + dest.writeInt(enableStaleState() ? 1 : 0); + dest.writeLong(staleStateTimeout()); + dest.writeIntArray(padding()); + dest.writeDouble(maxZoom()); + dest.writeDouble(minZoom()); + dest.writeFloat(maxZoomIconScale()); + dest.writeFloat(minZoomIconScale()); + dest.writeFloat(trackingInitialMoveThreshold()); + dest.writeFloat(trackingMultiFingerMoveThreshold()); + dest.writeString(layerBelow()); + } + + @Override + public int describeContents() { + return 0; + } + + /** + * Builder class for constructing a new instance of {@link LocationComponentOptions}. + */ + public static class Builder { + + /** + * Build a new instance of this {@link LocationComponentOptions} class. + * + * @return a new instance of {@link LocationComponentOptions} + */ + public LocationComponentOptions build() { + LocationComponentOptions locationComponentOptions = autoBuild(); + if (locationComponentOptions.accuracyAlpha() < 0 || locationComponentOptions.accuracyAlpha() > 1) { + throw new IllegalArgumentException( + "Accuracy alpha value must be between 0.0 and 1.0."); + } + + if (locationComponentOptions.elevation() < 0f) { + throw new IllegalArgumentException("Invalid shadow size " + + locationComponentOptions.elevation() + ". Must be >= 0"); + } + + return locationComponentOptions; + } + + private Float accuracyAlpha; + private Integer accuracyColor; + private Integer backgroundDrawableStale; + private String backgroundStaleName; + private Integer foregroundDrawableStale; + private String foregroundStaleName; + private Integer gpsDrawable; + private String gpsName; + private Integer foregroundDrawable; + private String foregroundName; + private Integer backgroundDrawable; + private String backgroundName; + private Integer bearingDrawable; + private String bearingName; + private Integer bearingTintColor; + private Integer foregroundTintColor; + private Integer backgroundTintColor; + private Integer foregroundStaleTintColor; + private Integer backgroundStaleTintColor; + private Float elevation; + private Boolean enableStaleState; + private Long staleStateTimeout; + private int[] padding; + private Double maxZoom; + private Double minZoom; + private Float maxZoomIconScale; + private Float minZoomIconScale; + private Float trackingInitialMoveThreshold; + private Float trackingMultiFingerMoveThreshold; + private String layerBelow; + + Builder() { + } + + private Builder(LocationComponentOptions source) { + this.accuracyAlpha = source.accuracyAlpha(); + this.accuracyColor = source.accuracyColor(); + this.backgroundDrawableStale = source.backgroundDrawableStale(); + this.backgroundStaleName = source.backgroundStaleName(); + this.foregroundDrawableStale = source.foregroundDrawableStale(); + this.foregroundStaleName = source.foregroundStaleName(); + this.gpsDrawable = source.gpsDrawable(); + this.gpsName = source.gpsName(); + this.foregroundDrawable = source.foregroundDrawable(); + this.foregroundName = source.foregroundName(); + this.backgroundDrawable = source.backgroundDrawable(); + this.backgroundName = source.backgroundName(); + this.bearingDrawable = source.bearingDrawable(); + this.bearingName = source.bearingName(); + this.bearingTintColor = source.bearingTintColor(); + this.foregroundTintColor = source.foregroundTintColor(); + this.backgroundTintColor = source.backgroundTintColor(); + this.foregroundStaleTintColor = source.foregroundStaleTintColor(); + this.backgroundStaleTintColor = source.backgroundStaleTintColor(); + this.elevation = source.elevation(); + this.enableStaleState = source.enableStaleState(); + this.staleStateTimeout = source.staleStateTimeout(); + this.padding = source.padding(); + this.maxZoom = source.maxZoom(); + this.minZoom = source.minZoom(); + this.maxZoomIconScale = source.maxZoomIconScale(); + this.minZoomIconScale = source.minZoomIconScale(); + this.trackingInitialMoveThreshold = source.trackingInitialMoveThreshold(); + this.trackingMultiFingerMoveThreshold = source.trackingMultiFingerMoveThreshold(); + this.layerBelow = source.layerBelow(); + } + + /** + * Set the opacity of the accuracy view to a value from 0 to 1, where 0 means the accuracy view + * is completely transparent and 1 means the view is completely opaque. + * + * @param accuracyAlpha the opacity of the accuracy view + * @return this builder for chaining options together + * @attr ref R.styleable#LocationLayer_accuracyAlpha + */ + public LocationComponentOptions.Builder accuracyAlpha(float accuracyAlpha) { + this.accuracyAlpha = accuracyAlpha; + return this; + } + + /** + * Solid color to use as the accuracy view color property. + * + * @param accuracyColor the color of the accuracy view + * @return this builder for chaining options together + * @attr ref R.styleable#LocationLayer_accuracyColor + */ + public LocationComponentOptions.Builder accuracyColor(int accuracyColor) { + this.accuracyColor = accuracyColor; + return this; + } + + /** + * Defines the drawable used for the stale background icon. + * + * @param backgroundDrawableStale the drawable resource ID + * @return this builder for chaining options together + * @attr ref R.styleable#LocationLayer_backgroundDrawableStale + */ + public LocationComponentOptions.Builder backgroundDrawableStale(int backgroundDrawableStale) { + this.backgroundDrawableStale = backgroundDrawableStale; + return this; + } + + /** + * Given a String image name, identical to one used in + * the first parameter of {@link com.mapbox.mapboxsdk.maps.MapboxMap#addImage(String, Bitmap)}, the + * plugin, will used this image in place of the provided or default mapbox_backgroundDrawableStale. + *

+ * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded + * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. + *

+ * + * @param backgroundStaleName String icon or maki-icon name + * @return this builder for chaining options together + */ + public LocationComponentOptions.Builder backgroundStaleName(@Nullable String backgroundStaleName) { + this.backgroundStaleName = backgroundStaleName; + return this; + } + + /** + * Defines the drawable used for the stale foreground icon. + * + * @param foregroundDrawableStale the drawable resource ID + * @return this builder for chaining options together + * @attr ref R.styleable#LocationLayer_foregroundDrawableStale + */ + public LocationComponentOptions.Builder foregroundDrawableStale(int foregroundDrawableStale) { + this.foregroundDrawableStale = foregroundDrawableStale; + return this; + } + + /** + * Given a String image name, identical to one used in + * the first parameter of {@link com.mapbox.mapboxsdk.maps.MapboxMap#addImage(String, Bitmap)}, the + * plugin, will used this image in place of the provided or default mapbox_foregroundDrawableStale. + *

+ * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded + * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. + *

+ * + * @param foregroundStaleName String icon or maki-icon name + * @return this builder for chaining options together + */ + public LocationComponentOptions.Builder foregroundStaleName(@Nullable String foregroundStaleName) { + this.foregroundStaleName = foregroundStaleName; + return this; + } + + /** + * Defines the drawable used for the navigation state icon. + * + * @param gpsDrawable the drawable resource ID + * @return this builder for chaining options together + * @attr ref R.styleable#LocationLayer_gpsDrawable + */ + public LocationComponentOptions.Builder gpsDrawable(int gpsDrawable) { + this.gpsDrawable = gpsDrawable; + return this; + } + + /** + * Given a String image name, identical to one used in + * the first parameter of {@link com.mapbox.mapboxsdk.maps.MapboxMap#addImage(String, Bitmap)}, the + * plugin, will used this image in place of the provided or default mapbox_gpsDrawable. + *

+ * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded + * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. + *

+ * + * @param gpsName String icon or maki-icon name + * @return this builder for chaining options together + */ + public LocationComponentOptions.Builder gpsName(@Nullable String gpsName) { + this.gpsName = gpsName; + return this; + } + + /** + * Supply a Drawable that is to be rendered on top of all of the content in the Location Layer + * Plugin layer stack. + * + * @param foregroundDrawable the drawable resource used for the foreground layer + * @return this builder for chaining options together + * @attr ref R.styleable#LocationLayer_foregroundDrawable + */ + public LocationComponentOptions.Builder foregroundDrawable(int foregroundDrawable) { + this.foregroundDrawable = foregroundDrawable; + return this; + } + + /** + * Given a String image name, identical to one used in + * the first parameter of {@link com.mapbox.mapboxsdk.maps.MapboxMap#addImage(String, Bitmap)}, the + * plugin, will used this image in place of the provided or default mapbox_foregroundDrawable. + *

+ * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded + * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. + *

+ * + * @param foregroundName String icon or maki-icon name + * @return this builder for chaining options together + */ + public LocationComponentOptions.Builder foregroundName(@Nullable String foregroundName) { + this.foregroundName = foregroundName; + return this; + } + + /** + * Defines the drawable used for the background state icon. + * + * @param backgroundDrawable the drawable resource ID + * @return this builder for chaining options together + * @attr ref R.styleable#LocationLayer_backgroundDrawable + */ + public LocationComponentOptions.Builder backgroundDrawable(int backgroundDrawable) { + this.backgroundDrawable = backgroundDrawable; + return this; + } + + /** + * Given a String image name, identical to one used in + * the first parameter of {@link com.mapbox.mapboxsdk.maps.MapboxMap#addImage(String, Bitmap)}, the + * plugin, will used this image in place of the provided or default mapbox_backgroundDrawable. + *

+ * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded + * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. + *

+ * + * @param backgroundName String icon or maki-icon name + * @return this builder for chaining options together + */ + public LocationComponentOptions.Builder backgroundName(@Nullable String backgroundName) { + this.backgroundName = backgroundName; + return this; + } + + /** + * Defines the drawable used for the bearing icon. + * + * @param bearingDrawable the drawable resource ID + * @return this builder for chaining options together + * @attr ref R.styleable#LocationLayer_bearingDrawable + */ + public LocationComponentOptions.Builder bearingDrawable(int bearingDrawable) { + this.bearingDrawable = bearingDrawable; + return this; + } + + /** + * Given a String image name, identical to one used in + * the first parameter of {@link com.mapbox.mapboxsdk.maps.MapboxMap#addImage(String, Bitmap)}, the + * plugin, will used this image in place of the provided or default mapbox_bearingDrawable. + *

+ * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded + * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. + *

+ * + * @param bearingName String icon or maki-icon name + * @return this builder for chaining options together + */ + public LocationComponentOptions.Builder bearingName(@Nullable String bearingName) { + this.bearingName = bearingName; + return this; + } + + /** + * Defines the bearing icon color as an integer. + * + * @param bearingTintColor the color integer resource + * @return this builder for chaining options together + * @attr ref R.styleable#LocationLayer_bearingTintColor + */ + public LocationComponentOptions.Builder bearingTintColor(@Nullable Integer bearingTintColor) { + this.bearingTintColor = bearingTintColor; + return this; + } + + /** + * Defines the foreground color as an integer. + * + * @param foregroundTintColor the color integer resource + * @return this builder for chaining options together + * @attr ref R.styleable#LocationLayer_foregroundTintColor + */ + public LocationComponentOptions.Builder foregroundTintColor(@Nullable Integer foregroundTintColor) { + this.foregroundTintColor = foregroundTintColor; + return this; + } + + /** + * Defines the background color as an integer. + * + * @param backgroundTintColor the color integer resource + * @return this builder for chaining options together + * @attr ref R.styleable#LocationLayer_backgroundTintColor + */ + public LocationComponentOptions.Builder backgroundTintColor(@Nullable Integer backgroundTintColor) { + this.backgroundTintColor = backgroundTintColor; + return this; + } + + /** + * Defines the foreground stale color as an integer. + * + * @param foregroundStaleTintColor the color integer resource + * @return this builder for chaining options together + * @attr ref R.styleable#LocationLayer_foregroundStaleTintColor + */ + public LocationComponentOptions.Builder foregroundStaleTintColor(@Nullable Integer foregroundStaleTintColor) { + this.foregroundStaleTintColor = foregroundStaleTintColor; + return this; + } + + /** + * Defines the background stale color as an integer. + * + * @param backgroundStaleTintColor the color integer resource + * @return this builder for chaining options together + * @attr ref R.styleable#LocationLayer_backgroundStaleTintColor + */ + public LocationComponentOptions.Builder backgroundStaleTintColor(@Nullable Integer backgroundStaleTintColor) { + this.backgroundStaleTintColor = backgroundStaleTintColor; + return this; + } + + /** + * Sets the base elevation of this view, in pixels. + * + * @param elevation the elevation currently set for the location layer icon + * @return this builder for chaining options together + * @attr ref R.styleable#LocationLayer_elevation + */ + public LocationComponentOptions.Builder elevation(float elevation) { + this.elevation = elevation; + return this; + } + + /** + * Enable or disable to stale state mode. This mode indicates to the user that the location + * being displayed on the map hasn't been updated in a specific amount of time. + * + * @param enabled whether the stale state mode is enabled or not + * @return this builder for chaining options together + * @attr ref R.styleable#LocationLayer_enableStaleState + */ + public LocationComponentOptions.Builder enableStaleState(boolean enabled) { + this.enableStaleState = enabled; + return this; + } + + /** + * Set the timeout before the location icon becomes stale. The timer begins approximately when a + * new location update comes in and using this defined time, if an update hasn't occurred by the + * end, the location is considered stale. + * + * @param timeout the duration in milliseconds which it should take before the location layer is + * considered stale + * @return this builder for chaining options together + * @attr ref R.styleable#LocationLayer_staleStateTimeout + */ + public LocationComponentOptions.Builder staleStateTimeout(long timeout) { + this.staleStateTimeout = timeout; + return this; + } + + /** + * Sets the distance from the edges of the map view’s frame to the edges of the map + * view’s logical viewport. + *

+ *

+ * When the value of this property is equal to {0,0,0,0}, viewport + * properties such as `centerCoordinate` assume a viewport that matches the map + * view’s frame. Otherwise, those properties are inset, excluding part of the + * frame from the viewport. For instance, if the only the top edge is inset, the + * map center is effectively shifted downward. + *

+ * + * @param padding The margins for the map in pixels (left, top, right, bottom). + */ + public LocationComponentOptions.Builder padding(int[] padding) { + if (padding == null) { + throw new NullPointerException("Null padding"); + } + this.padding = padding; + return this; + } + + /** + * Sets the maximum zoom level the map can be displayed at. + *

+ * The default maximum zoomn level is 22. The upper bound for this value is 25.5. + * + * @param maxZoom The new maximum zoom level. + */ + public LocationComponentOptions.Builder maxZoom(double maxZoom) { + this.maxZoom = maxZoom; + return this; + } + + /** + * Sets the minimum zoom level the map can be displayed at. + * + * @param minZoom The new minimum zoom level. + */ + public LocationComponentOptions.Builder minZoom(double minZoom) { + this.minZoom = minZoom; + return this; + } + + /** + * Sets the scale factor of the location icon when the map is zoomed in. Based on {@link #maxZoom()}. + * Scaling is linear and the new pixel size of the image will be the original pixel size multiplied by the argument. + *

+ * Set both this and {@link #minZoomIconScale(float)} to 1f to disable location icon scaling. + *

+ * + * @param maxZoomIconScale icon scale factor + */ + public LocationComponentOptions.Builder maxZoomIconScale(float maxZoomIconScale) { + this.maxZoomIconScale = maxZoomIconScale; + return this; + } + + /** + * Sets the scale factor of the location icon when the map is zoomed out. Based on {@link #maxZoom()}. + * Scaling is linear and the new pixel size of the image will be the original pixel size multiplied by the argument. + *

+ * Set both this and {@link #maxZoomIconScale(float)} to 1f to disable location icon scaling. + *

+ * + * @param minZoomIconScale icon scale factor + */ + public LocationComponentOptions.Builder minZoomIconScale(float minZoomIconScale) { + this.minZoomIconScale = minZoomIconScale; + return this; + } + + /** + * Sets minimum single pointer movement (map pan) in pixels required to break camera tracking. + * + * @param moveThreshold the minimum movement + */ + public LocationComponentOptions.Builder trackingInitialMoveThreshold(float moveThreshold) { + this.trackingInitialMoveThreshold = moveThreshold; + return this; + } + + /** + * Sets minimum multi pointer movement (map pan) in pixels required to break camera tracking + * (for example during scale gesture). + * + * @param moveThreshold the minimum movement + */ + public LocationComponentOptions.Builder trackingMultiFingerMoveThreshold(float moveThreshold) { + this.trackingMultiFingerMoveThreshold = moveThreshold; + return this; + } + + /** + * Sets the layer id to set the location layer plugin below to. + * + * @param layerBelow the id to set the location layer plugin below to. + */ + public LocationComponentOptions.Builder layerBelow(String layerBelow) { + this.layerBelow = layerBelow; + return this; + } + + LocationComponentOptions autoBuild() { + String missing = ""; + if (this.accuracyAlpha == null) { + missing += " accuracyAlpha"; + } + if (this.accuracyColor == null) { + missing += " accuracyColor"; + } + if (this.backgroundDrawableStale == null) { + missing += " backgroundDrawableStale"; + } + if (this.foregroundDrawableStale == null) { + missing += " foregroundDrawableStale"; + } + if (this.gpsDrawable == null) { + missing += " gpsDrawable"; + } + if (this.foregroundDrawable == null) { + missing += " foregroundDrawable"; + } + if (this.backgroundDrawable == null) { + missing += " backgroundDrawable"; + } + if (this.bearingDrawable == null) { + missing += " bearingDrawable"; + } + if (this.elevation == null) { + missing += " elevation"; + } + if (this.enableStaleState == null) { + missing += " enableStaleState"; + } + if (this.staleStateTimeout == null) { + missing += " staleStateTimeout"; + } + if (this.padding == null) { + missing += " padding"; + } + if (this.maxZoom == null) { + missing += " maxZoom"; + } + if (this.minZoom == null) { + missing += " minZoom"; + } + if (this.maxZoomIconScale == null) { + missing += " maxZoomIconScale"; + } + if (this.minZoomIconScale == null) { + missing += " minZoomIconScale"; + } + if (this.trackingInitialMoveThreshold == null) { + missing += " trackingInitialMoveThreshold"; + } + if (this.trackingMultiFingerMoveThreshold == null) { + missing += " trackingMultiFingerMoveThreshold"; + } + if (!missing.isEmpty()) { + throw new IllegalStateException("Missing required properties:" + missing); + } + return new LocationComponentOptions( + this.accuracyAlpha, + this.accuracyColor, + this.backgroundDrawableStale, + this.backgroundStaleName, + this.foregroundDrawableStale, + this.foregroundStaleName, + this.gpsDrawable, + this.gpsName, + this.foregroundDrawable, + this.foregroundName, + this.backgroundDrawable, + this.backgroundName, + this.bearingDrawable, + this.bearingName, + this.bearingTintColor, + this.foregroundTintColor, + this.backgroundTintColor, + this.foregroundStaleTintColor, + this.backgroundStaleTintColor, + this.elevation, + this.enableStaleState, + this.staleStateTimeout, + this.padding, + this.maxZoom, + this.minZoom, + this.maxZoomIconScale, + this.minZoomIconScale, + this.trackingInitialMoveThreshold, + this.trackingMultiFingerMoveThreshold, + this.layerBelow); + } + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationLayerController.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationLayerController.java new file mode 100644 index 0000000000..75826f911b --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationLayerController.java @@ -0,0 +1,394 @@ +package com.mapbox.mapboxsdk.location; + +import android.graphics.Bitmap; +import android.graphics.PointF; +import android.support.annotation.ColorInt; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.Point; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.location.modes.RenderMode; +import com.mapbox.mapboxsdk.style.layers.Layer; +import com.mapbox.mapboxsdk.style.layers.SymbolLayer; +import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; + +import java.util.ArrayList; +import java.util.List; + +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.ACCURACY_LAYER; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.BACKGROUND_ICON; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.BACKGROUND_LAYER; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.BACKGROUND_STALE_ICON; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.BEARING_ICON; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.BEARING_LAYER; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.FOREGROUND_ICON; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.FOREGROUND_LAYER; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.FOREGROUND_STALE_ICON; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.LOCATION_SOURCE; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_ACCURACY_ALPHA; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_ACCURACY_COLOR; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_ACCURACY_RADIUS; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_BACKGROUND_ICON; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_BACKGROUND_STALE_ICON; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_BEARING_ICON; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_COMPASS_BEARING; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_FOREGROUND_ICON; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_FOREGROUND_ICON_OFFSET; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_FOREGROUND_STALE_ICON; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_GPS_BEARING; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_LOCATION_STALE; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_SHADOW_ICON_OFFSET; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.SHADOW_ICON; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.SHADOW_LAYER; +import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate; +import static com.mapbox.mapboxsdk.style.expressions.Expression.linear; +import static com.mapbox.mapboxsdk.style.expressions.Expression.stop; +import static com.mapbox.mapboxsdk.style.expressions.Expression.zoom; +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.colorToRgbaString; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconSize; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility; + +final class LocationLayerController implements MapboxAnimator.OnLayerAnimationsValuesChangeListener { + + @RenderMode.Mode + private int renderMode; + + private final MapboxMap mapboxMap; + private final LayerSourceProvider layerSourceProvider; + private final LayerBitmapProvider bitmapProvider; + private LocationComponentOptions options; + + private final List layerMap = new ArrayList<>(); + private Feature locationFeature; + private GeoJsonSource locationSource; + + private boolean isHidden = true; + + LocationLayerController(MapboxMap mapboxMap, LayerSourceProvider layerSourceProvider, + LayerFeatureProvider featureProvider, LayerBitmapProvider bitmapProvider, + LocationComponentOptions options) { + this.mapboxMap = mapboxMap; + this.layerSourceProvider = layerSourceProvider; + this.bitmapProvider = bitmapProvider; + this.locationFeature = featureProvider.generateLocationFeature(locationFeature, options); + initializeComponents(options); + setRenderMode(RenderMode.NORMAL); + } + + void initializeComponents(LocationComponentOptions options) { + addLocationSource(); + addLayers(options.layerBelow()); + applyStyle(options); + + if (isHidden) { + hide(); + } else { + show(); + } + } + + void applyStyle(@NonNull LocationComponentOptions options) { + this.options = options; + + float elevation = options.elevation(); + // Only add icon elevation if the values greater than 0. + if (elevation > 0) { + styleShadow(options); + } + styleForeground(options); + styleBackground(options); + styleBearing(options); + styleAccuracy(options.accuracyAlpha(), options.accuracyColor()); + styleScaling(options); + determineIconsSource(options); + } + + void setRenderMode(@RenderMode.Mode int renderMode) { + this.renderMode = renderMode; + + if (!isHidden) { + boolean isStale = locationFeature.getBooleanProperty(PROPERTY_LOCATION_STALE); + switch (renderMode) { + case RenderMode.NORMAL: + styleForeground(options); + setLayerVisibility(SHADOW_LAYER, true); + setLayerVisibility(FOREGROUND_LAYER, true); + setLayerVisibility(BACKGROUND_LAYER, true); + setLayerVisibility(ACCURACY_LAYER, !isStale); + setLayerVisibility(BEARING_LAYER, false); + break; + case RenderMode.COMPASS: + styleForeground(options); + setLayerVisibility(SHADOW_LAYER, true); + setLayerVisibility(FOREGROUND_LAYER, true); + setLayerVisibility(BACKGROUND_LAYER, true); + setLayerVisibility(ACCURACY_LAYER, !isStale); + setLayerVisibility(BEARING_LAYER, true); + break; + case RenderMode.GPS: + styleForeground(options); + setLayerVisibility(SHADOW_LAYER, false); + setLayerVisibility(FOREGROUND_LAYER, true); + setLayerVisibility(BACKGROUND_LAYER, true); + setLayerVisibility(ACCURACY_LAYER, false); + setLayerVisibility(BEARING_LAYER, false); + break; + default: + break; + } + + determineIconsSource(options); + } + } + + int getRenderMode() { + return renderMode; + } + + // + // Layer action + // + + void show() { + isHidden = false; + setRenderMode(renderMode); + } + + void hide() { + isHidden = true; + for (String layerId : layerMap) { + setLayerVisibility(layerId, false); + } + } + + void updateForegroundOffset(double tilt) { + JsonArray foregroundJsonArray = new JsonArray(); + foregroundJsonArray.add(0f); + foregroundJsonArray.add((float) (-0.05 * tilt)); + locationFeature.addProperty(PROPERTY_FOREGROUND_ICON_OFFSET, foregroundJsonArray); + + JsonArray backgroundJsonArray = new JsonArray(); + backgroundJsonArray.add(0f); + backgroundJsonArray.add((float) (0.05 * tilt)); + locationFeature.addProperty(PROPERTY_SHADOW_ICON_OFFSET, backgroundJsonArray); + + refreshSource(); + } + + void updateForegroundBearing(float bearing) { + if (renderMode != RenderMode.GPS) { + setBearingProperty(PROPERTY_GPS_BEARING, bearing); + } + } + + private void setLayerVisibility(String layerId, boolean visible) { + Layer layer = mapboxMap.getLayer(layerId); + if (layer != null) { + String targetVisibility = visible ? VISIBLE : NONE; + if (!layer.getVisibility().value.equals(targetVisibility)) { + layer.setProperties(visibility(visible ? VISIBLE : NONE)); + } + } + } + + private void addLayers(String idBelowLayer) { + addSymbolLayer(BEARING_LAYER, idBelowLayer); + addSymbolLayer(FOREGROUND_LAYER, BEARING_LAYER); + addSymbolLayer(BACKGROUND_LAYER, FOREGROUND_LAYER); + addSymbolLayer(SHADOW_LAYER, BACKGROUND_LAYER); + addAccuracyLayer(); + } + + private void addSymbolLayer(String layerId, String beforeLayerId) { + Layer layer = layerSourceProvider.generateLayer(layerId); + addLayerToMap(layer, beforeLayerId); + } + + private void addAccuracyLayer() { + Layer accuracyLayer = layerSourceProvider.generateAccuracyLayer(); + addLayerToMap(accuracyLayer, BACKGROUND_LAYER); + } + + private void addLayerToMap(Layer layer, @NonNull String idBelowLayer) { + mapboxMap.addLayerBelow(layer, idBelowLayer); + layerMap.add(layer.getId()); + } + + private void setBearingProperty(String propertyId, float bearing) { + locationFeature.addNumberProperty(propertyId, bearing); + refreshSource(); + } + + private void updateAccuracyRadius(float accuracy) { + if (renderMode == RenderMode.COMPASS || renderMode == RenderMode.NORMAL) { + locationFeature.addNumberProperty(PROPERTY_ACCURACY_RADIUS, accuracy); + refreshSource(); + } + } + + // + // Source actions + // + + private void addLocationSource() { + locationSource = layerSourceProvider.generateSource(locationFeature); + mapboxMap.addSource(locationSource); + } + + private void refreshSource() { + GeoJsonSource source = mapboxMap.getSourceAs(LOCATION_SOURCE); + if (source != null) { + locationSource.setGeoJson(locationFeature); + } + } + + private void setLocationPoint(Point locationPoint) { + JsonObject properties = locationFeature.properties(); + if (properties != null) { + locationFeature = Feature.fromGeometry(locationPoint, properties); + refreshSource(); + } + } + + // + // Styling + // + + private void styleBackground(LocationComponentOptions options) { + Bitmap backgroundBitmap = bitmapProvider.generateBitmap( + options.backgroundDrawable(), options.backgroundTintColor() + ); + Bitmap backgroundStaleBitmap = bitmapProvider.generateBitmap( + options.backgroundDrawableStale(), options.backgroundStaleTintColor() + ); + mapboxMap.addImage(BACKGROUND_ICON, backgroundBitmap); + mapboxMap.addImage(BACKGROUND_STALE_ICON, backgroundStaleBitmap); + } + + private void styleShadow(LocationComponentOptions options) { + mapboxMap.addImage(SHADOW_ICON, bitmapProvider.generateShadowBitmap(options)); + } + + private void styleBearing(LocationComponentOptions options) { + Bitmap bearingBitmap = bitmapProvider.generateBitmap(options.bearingDrawable(), options.bearingTintColor()); + mapboxMap.addImage(BEARING_ICON, bearingBitmap); + } + + private void styleAccuracy(float accuracyAlpha, @ColorInt int accuracyColor) { + locationFeature.addNumberProperty(PROPERTY_ACCURACY_ALPHA, accuracyAlpha); + locationFeature.addStringProperty(PROPERTY_ACCURACY_COLOR, colorToRgbaString(accuracyColor)); + refreshSource(); + } + + private void styleForeground(LocationComponentOptions options) { + Bitmap foregroundBitmap = bitmapProvider.generateBitmap( + options.foregroundDrawable(), options.foregroundTintColor() + ); + Bitmap foregroundBitmapStale = bitmapProvider.generateBitmap( + options.foregroundDrawableStale(), options.foregroundStaleTintColor() + ); + if (renderMode == RenderMode.GPS) { + foregroundBitmap = bitmapProvider.generateBitmap( + options.gpsDrawable(), options.foregroundTintColor() + ); + foregroundBitmapStale = bitmapProvider.generateBitmap( + options.gpsDrawable(), options.foregroundStaleTintColor() + ); + } + mapboxMap.addImage(FOREGROUND_ICON, foregroundBitmap); + mapboxMap.addImage(FOREGROUND_STALE_ICON, foregroundBitmapStale); + } + + private void styleScaling(LocationComponentOptions options) { + for (String layerId : layerMap) { + Layer layer = mapboxMap.getLayer(layerId); + if (layer != null && layer instanceof SymbolLayer) { + layer.setProperties( + iconSize( + interpolate(linear(), zoom(), + stop(options.minZoom(), options.minZoomIconScale()), + stop(options.maxZoom(), options.maxZoomIconScale()) + ) + ) + ); + } + } + } + + private void determineIconsSource(LocationComponentOptions options) { + String foregroundIconString = buildIconString( + renderMode == RenderMode.GPS ? options.gpsName() : options.foregroundName(), FOREGROUND_ICON); + String foregroundStaleIconString = buildIconString(options.foregroundStaleName(), FOREGROUND_STALE_ICON); + String backgroundIconString = buildIconString(options.backgroundName(), BACKGROUND_ICON); + String backgroundStaleIconString = buildIconString(options.backgroundStaleName(), BACKGROUND_STALE_ICON); + String bearingIconString = buildIconString(options.bearingName(), BEARING_ICON); + + locationFeature.addStringProperty(PROPERTY_FOREGROUND_ICON, foregroundIconString); + locationFeature.addStringProperty(PROPERTY_BACKGROUND_ICON, backgroundIconString); + locationFeature.addStringProperty(PROPERTY_FOREGROUND_STALE_ICON, foregroundStaleIconString); + locationFeature.addStringProperty(PROPERTY_BACKGROUND_STALE_ICON, backgroundStaleIconString); + locationFeature.addStringProperty(PROPERTY_BEARING_ICON, bearingIconString); + refreshSource(); + } + + private String buildIconString(@Nullable String bitmapName, @NonNull String drawableName) { + if (bitmapName != null) { + return bitmapName; + } + return drawableName; + } + + void setLocationsStale(boolean isStale) { + locationFeature.addBooleanProperty(PROPERTY_LOCATION_STALE, isStale); + refreshSource(); + if (renderMode != RenderMode.GPS) { + setLayerVisibility(ACCURACY_LAYER, !isStale); + } + } + + // + // Map click event + // + + boolean onMapClick(LatLng point) { + PointF screenLoc = mapboxMap.getProjection().toScreenLocation(point); + List features = mapboxMap.queryRenderedFeatures(screenLoc, + BACKGROUND_LAYER, + FOREGROUND_LAYER, + BEARING_LAYER + ); + return !features.isEmpty(); + } + + @Override + public void onNewLatLngValue(LatLng latLng) { + Point point = Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude()); + setLocationPoint(point); + } + + @Override + public void onNewGpsBearingValue(float gpsBearing) { + if (renderMode == RenderMode.GPS) { + setBearingProperty(PROPERTY_GPS_BEARING, gpsBearing); + } + } + + @Override + public void onNewCompassBearingValue(float compassBearing) { + if (renderMode == RenderMode.COMPASS) { + setBearingProperty(PROPERTY_COMPASS_BEARING, compassBearing); + } + } + + @Override + public void onNewAccuracyRadiusValue(float accuracyRadiusValue) { + updateAccuracyRadius(accuracyRadiusValue); + } +} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimator.java new file mode 100644 index 0000000000..b22601a15c --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimator.java @@ -0,0 +1,92 @@ +package com.mapbox.mapboxsdk.location; + +import android.animation.TypeEvaluator; +import android.animation.ValueAnimator; +import android.support.annotation.IntDef; + +import com.mapbox.mapboxsdk.geometry.LatLng; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.List; + +/** + * Abstract class for all of the plugin animators. + * + * @param Data type that will be animated. + * @param Listener of animation updates. + */ +abstract class MapboxAnimator extends ValueAnimator implements ValueAnimator.AnimatorUpdateListener { + @Retention(RetentionPolicy.SOURCE) + @IntDef( { + ANIMATOR_LAYER_LATLNG, + ANIMATOR_CAMERA_LATLNG, + ANIMATOR_LAYER_GPS_BEARING, + ANIMATOR_LAYER_COMPASS_BEARING, + ANIMATOR_CAMERA_GPS_BEARING, + ANIMATOR_CAMERA_COMPASS_BEARING, + ANIMATOR_LAYER_ACCURACY, + ANIMATOR_ZOOM, + ANIMATOR_TILT + }) + @interface Type { + } + + static final int ANIMATOR_LAYER_LATLNG = 0; + static final int ANIMATOR_CAMERA_LATLNG = 1; + static final int ANIMATOR_LAYER_GPS_BEARING = 2; + static final int ANIMATOR_LAYER_COMPASS_BEARING = 3; + static final int ANIMATOR_CAMERA_GPS_BEARING = 4; + static final int ANIMATOR_CAMERA_COMPASS_BEARING = 5; + static final int ANIMATOR_LAYER_ACCURACY = 6; + static final int ANIMATOR_ZOOM = 7; + static final int ANIMATOR_TILT = 8; + + private final int animatorType = provideAnimatorType(); + final List updateListeners; + private final K target; + + MapboxAnimator(K previous, K target, List updateListeners) { + setObjectValues(previous, target); + setEvaluator(provideEvaluator()); + this.updateListeners = updateListeners; + this.target = target; + addUpdateListener(this); + } + + K getTarget() { + return target; + } + + @Type + int getAnimatorType() { + return animatorType; + } + + @Type + abstract int provideAnimatorType(); + + abstract TypeEvaluator provideEvaluator(); + + interface OnLayerAnimationsValuesChangeListener { + void onNewLatLngValue(LatLng latLng); + + void onNewGpsBearingValue(float gpsBearing); + + void onNewCompassBearingValue(float compassBearing); + + void onNewAccuracyRadiusValue(float accuracyRadiusValue); + } + + interface OnCameraAnimationsValuesChangeListener { + void onNewLatLngValue(LatLng latLng); + + void onNewGpsBearingValue(float gpsBearing); + + void onNewCompassBearingValue(float compassBearing); + + void onNewZoomValue(float zoom); + + void onNewTiltValue(float tilt); + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxCameraAnimatorAdapter.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxCameraAnimatorAdapter.java new file mode 100644 index 0000000000..89d27a38fa --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxCameraAnimatorAdapter.java @@ -0,0 +1,38 @@ +package com.mapbox.mapboxsdk.location; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.support.annotation.Nullable; + +import com.mapbox.mapboxsdk.maps.MapboxMap; + +import java.util.List; + +abstract class MapboxCameraAnimatorAdapter extends + MapboxFloatAnimator { + private final MapboxMap.CancelableCallback cancelableCallback; + + MapboxCameraAnimatorAdapter(Float previous, Float target, + List updateListeners, + @Nullable MapboxMap.CancelableCallback cancelableCallback) { + super(previous, target, updateListeners); + this.cancelableCallback = cancelableCallback; + addListener(new MapboxAnimatorListener()); + } + + private final class MapboxAnimatorListener extends AnimatorListenerAdapter { + @Override + public void onAnimationCancel(Animator animation) { + if (cancelableCallback != null) { + cancelableCallback.onCancel(); + } + } + + @Override + public void onAnimationEnd(Animator animation) { + if (cancelableCallback != null) { + cancelableCallback.onFinish(); + } + } + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxFloatAnimator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxFloatAnimator.java new file mode 100644 index 0000000000..4a6d8c3b73 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxFloatAnimator.java @@ -0,0 +1,17 @@ +package com.mapbox.mapboxsdk.location; + +import android.animation.FloatEvaluator; +import android.animation.TypeEvaluator; + +import java.util.List; + +abstract class MapboxFloatAnimator extends MapboxAnimator { + MapboxFloatAnimator(Float previous, Float target, List updateListeners) { + super(previous, target, updateListeners); + } + + @Override + TypeEvaluator provideEvaluator() { + return new FloatEvaluator(); + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxLatLngAnimator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxLatLngAnimator.java new file mode 100644 index 0000000000..b2f1b61a2d --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxLatLngAnimator.java @@ -0,0 +1,19 @@ +package com.mapbox.mapboxsdk.location; + +import android.animation.TypeEvaluator; + +import com.mapbox.mapboxsdk.geometry.LatLng; + +import java.util.List; + +abstract class MapboxLatLngAnimator extends MapboxAnimator { + + MapboxLatLngAnimator(LatLng previous, LatLng target, List updateListeners) { + super(previous, target, updateListeners); + } + + @Override + TypeEvaluator provideEvaluator() { + return new LatLngEvaluator(); + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnCameraMoveInvalidateListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnCameraMoveInvalidateListener.java new file mode 100644 index 0000000000..6594e543ff --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnCameraMoveInvalidateListener.java @@ -0,0 +1,7 @@ +package com.mapbox.mapboxsdk.location; + +interface OnCameraMoveInvalidateListener { + + void onInvalidateCameraMove(); + +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnCameraTrackingChangedListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnCameraTrackingChangedListener.java new file mode 100644 index 0000000000..1ea80d26aa --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnCameraTrackingChangedListener.java @@ -0,0 +1,21 @@ +package com.mapbox.mapboxsdk.location; + +import com.mapbox.mapboxsdk.location.modes.CameraMode; + +/** + * Listener that gets invoked when camera tracking state changes. + */ +public interface OnCameraTrackingChangedListener { + /** + * Invoked whenever camera tracking is broken. + * This callback gets invoked just after {@link #onCameraTrackingChanged(int)}, if needed. + */ + void onCameraTrackingDismissed(); + + /** + * Invoked on every {@link CameraMode} change. + * + * @param currentMode current active {@link CameraMode}. + */ + void onCameraTrackingChanged(@CameraMode.Mode int currentMode); +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationComponentClickListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationComponentClickListener.java new file mode 100644 index 0000000000..c592ae3a25 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationComponentClickListener.java @@ -0,0 +1,11 @@ +package com.mapbox.mapboxsdk.location; + +/** + * The Location Layer Plugin exposes an API for listening to when the user clicks on the location + * layer icon visible on the map. when this event occurs, the {@link #onLocationComponentClick()} method + * gets invoked. + */ +public interface OnLocationComponentClickListener { + + void onLocationComponentClick(); +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationComponentLongClickListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationComponentLongClickListener.java new file mode 100644 index 0000000000..dc63e48a58 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationComponentLongClickListener.java @@ -0,0 +1,11 @@ +package com.mapbox.mapboxsdk.location; + +/** + * The Location Layer Plugin exposes an API for listening to when the user long clicks on the location + * layer icon visible on the map. when this event occurs, the {@link #onLocationComponentLongClick()} method + * gets invoked. + */ +public interface OnLocationComponentLongClickListener { + + void onLocationComponentLongClick(); +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationStaleListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationStaleListener.java new file mode 100644 index 0000000000..d42eddf277 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationStaleListener.java @@ -0,0 +1,13 @@ +package com.mapbox.mapboxsdk.location; + +/** + * Listener that can be added as a callback when the last location update + * is considered stale. + *

+ * The time from the last location update that determines if a location update + * is stale or not is provided by {@link LocationComponentOptions#staleStateTimeout()}. + */ +public interface OnLocationStaleListener { + + void onStaleStateChange(boolean isStale); +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/StaleStateManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/StaleStateManager.java new file mode 100644 index 0000000000..c2d50610fa --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/StaleStateManager.java @@ -0,0 +1,80 @@ +package com.mapbox.mapboxsdk.location; + +import android.os.Handler; + +/** + * Class controls the location layer stale state when the {@link android.location.Location} hasn't + * been updated in 'x' amount of time. {@link LocationComponentOptions#staleStateTimeout()} can be used to + * control the amount of time before the locations considered stale. + * {@link LocationComponentOptions#enableStaleState()} is available for disabling this behaviour. + */ +class StaleStateManager { + + private boolean isEnabled; + private final OnLocationStaleListener innerOnLocationStaleListeners; + private final Handler handler; + private boolean isStale = true; + private long delayTime; + + StaleStateManager(OnLocationStaleListener innerListener, LocationComponentOptions options) { + innerOnLocationStaleListeners = innerListener; + handler = new Handler(); + isEnabled = options.enableStaleState(); + delayTime = options.staleStateTimeout(); + } + + private Runnable staleStateRunnable = new Runnable() { + @Override + public void run() { + setState(true); + } + }; + + void setEnabled(boolean enabled) { + if (enabled) { + setState(isStale); + } else if (isEnabled) { + onStop(); + innerOnLocationStaleListeners.onStaleStateChange(false); + } + isEnabled = enabled; + } + + boolean isStale() { + return isStale; + } + + void updateLatestLocationTime() { + setState(false); + postTheCallback(); + } + + void setDelayTime(long delayTime) { + this.delayTime = delayTime; + postTheCallback(); + } + + void onStart() { + if (!isStale) { + postTheCallback(); + } + } + + void onStop() { + handler.removeCallbacksAndMessages(null); + } + + private void postTheCallback() { + handler.removeCallbacksAndMessages(null); + handler.postDelayed(staleStateRunnable, delayTime); + } + + private void setState(boolean stale) { + if (stale != isStale) { + isStale = stale; + if (isEnabled) { + innerOnLocationStaleListeners.onStaleStateChange(stale); + } + } + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/TiltAnimator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/TiltAnimator.java new file mode 100644 index 0000000000..8ff0f97a70 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/TiltAnimator.java @@ -0,0 +1,27 @@ +package com.mapbox.mapboxsdk.location; + +import android.animation.ValueAnimator; +import android.support.annotation.Nullable; + +import com.mapbox.mapboxsdk.maps.MapboxMap; + +import java.util.List; + +class TiltAnimator extends MapboxCameraAnimatorAdapter { + TiltAnimator(Float previous, Float target, List updateListeners, + @Nullable MapboxMap.CancelableCallback cancelableCallback) { + super(previous, target, updateListeners, cancelableCallback); + } + + @Override + int provideAnimatorType() { + return ANIMATOR_TILT; + } + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + for (OnCameraAnimationsValuesChangeListener listener : updateListeners) { + listener.onNewTiltValue((Float) animation.getAnimatedValue()); + } + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/Utils.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/Utils.java new file mode 100644 index 0000000000..aa01c914dd --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/Utils.java @@ -0,0 +1,102 @@ +package com.mapbox.mapboxsdk.location; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.PorterDuff; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.location.Location; +import android.os.Build; +import android.support.annotation.ColorInt; +import android.support.annotation.DrawableRes; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.content.ContextCompat; + +import com.mapbox.mapboxsdk.maps.MapboxMap; + +public final class Utils { + + private Utils() { + // Class should not be initialized + } + + /** + * Util for finding the shortest path from the current icon rotated degree to the new degree. + * + * @param magneticHeading the new position of the rotation + * @param previousMagneticHeading the current position of the rotation + * @return the shortest degree of rotation possible + */ + public static float shortestRotation(float magneticHeading, float previousMagneticHeading) { + double diff = previousMagneticHeading - magneticHeading; + if (diff > 180.0f) { + magneticHeading += 360.0f; + } else if (diff < -180.0f) { + magneticHeading -= 360.f; + } + return magneticHeading; + } + + static Bitmap getBitmapFromDrawable(Drawable drawable) { + if (drawable instanceof BitmapDrawable) { + return ((BitmapDrawable) drawable).getBitmap(); + } else { + // width and height are equal for all assets since they are ovals. + Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), + drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + drawable.draw(canvas); + return bitmap; + } + } + + static Bitmap generateShadow(Drawable drawable, float elevation) { + int width = drawable.getIntrinsicWidth(); + int height = drawable.getIntrinsicHeight(); + Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + drawable.draw(canvas); + bitmap = Bitmap.createScaledBitmap(bitmap, + toEven(width + elevation), toEven(height + elevation), false); + return bitmap; + } + + static Drawable getDrawable(@NonNull Context context, @DrawableRes int drawableRes, + @ColorInt Integer tintColor) { + Drawable drawable = ContextCompat.getDrawable(context, drawableRes); + if (tintColor == null) { + return drawable; + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + drawable.setTint(tintColor); + } else { + drawable.mutate().setColorFilter(tintColor, PorterDuff.Mode.SRC_IN); + } + return drawable; + } + + static float calculateZoomLevelRadius(@NonNull MapboxMap mapboxMap, @Nullable Location location) { + if (location == null) { + return 0; + } + double metersPerPixel = mapboxMap.getProjection().getMetersPerPixelAtLatitude( + location.getLatitude()); + return (float) (location.getAccuracy() * (1 / metersPerPixel)); + } + + /** + * Casts the value to an even integer. + */ + private static int toEven(float value) { + int i = (int) (value + .5f); + if (i % 2 == 1) { + return i - 1; + } + return i; + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/ZoomAnimator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/ZoomAnimator.java new file mode 100644 index 0000000000..204a1457dc --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/ZoomAnimator.java @@ -0,0 +1,29 @@ +package com.mapbox.mapboxsdk.location; + +import android.animation.ValueAnimator; +import android.support.annotation.Nullable; + +import com.mapbox.mapboxsdk.maps.MapboxMap; + +import java.util.List; + +class ZoomAnimator extends MapboxCameraAnimatorAdapter { + + ZoomAnimator(Float previous, Float target, List updateListeners, + @Nullable MapboxMap.CancelableCallback cancelableCallback) { + super(previous, target, updateListeners, cancelableCallback); + } + + @Override + int provideAnimatorType() { + return ANIMATOR_ZOOM; + } + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + for (OnCameraAnimationsValuesChangeListener listener : updateListeners) { + listener.onNewZoomValue((Float) animation.getAnimatedValue()); + } + } + +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/modes/CameraMode.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/modes/CameraMode.java new file mode 100644 index 0000000000..51806a33cd --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/modes/CameraMode.java @@ -0,0 +1,66 @@ +package com.mapbox.mapboxsdk.location.modes; + +import android.location.Location; +import android.support.annotation.IntDef; + +import com.mapbox.mapboxsdk.location.LocationComponent; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Contains the variety of camera modes which determine how the camera will track + * the user location. + */ +public final class CameraMode { + + private CameraMode() { + // Class should not be initialized + } + + /** + * Determine the camera tracking behavior in the {@link LocationComponent}. + */ + @IntDef( {NONE, NONE_COMPASS, NONE_GPS, TRACKING, TRACKING_COMPASS, TRACKING_GPS, TRACKING_GPS_NORTH}) + @Retention(RetentionPolicy.SOURCE) + public @interface Mode { + } + + /** + * No camera tracking. + */ + public static final int NONE = 0x00000008; + + /** + * Camera does not track location, but does track compass bearing. + */ + public static final int NONE_COMPASS = 0x00000010; + + /** + * Camera does not track location, but does track GPS {@link Location} bearing. + */ + public static final int NONE_GPS = 0x00000016; + + /** + * Camera tracks the user location. + */ + public static final int TRACKING = 0x00000018; + + /** + * Camera tracks the user location, with bearing + * provided by a compass. + */ + public static final int TRACKING_COMPASS = 0x00000020; + + /** + * Camera tracks the user location, with bearing + * provided by a normalized {@link Location#getBearing()}. + */ + public static final int TRACKING_GPS = 0x00000022; + + /** + * Camera tracks the user location, with bearing + * always set to north (0). + */ + public static final int TRACKING_GPS_NORTH = 0x00000024; +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/modes/RenderMode.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/modes/RenderMode.java new file mode 100644 index 0000000000..0f7fa24008 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/modes/RenderMode.java @@ -0,0 +1,45 @@ +package com.mapbox.mapboxsdk.location.modes; + +import android.support.annotation.IntDef; + +import com.mapbox.mapboxsdk.location.CompassEngine; +import com.mapbox.mapboxsdk.location.LocationComponent; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Contains the variety of ways the user location can be rendered on the map. + */ +public final class RenderMode { + + private RenderMode() { + // Class should not be initialized + } + + /** + * One of these constants should be used with {@link LocationComponent#setRenderMode(int)}. + * mode can be switched at anytime by calling the {@code setLocationLayerMode} method passing + * in the new mode you'd like the location layer to be in. + */ + @IntDef( {COMPASS, GPS, NORMAL}) + @Retention(RetentionPolicy.SOURCE) + public @interface Mode { + } + + /** + * Basic tracking is enabled, bearing ignored. + */ + public static final int NORMAL = 0x00000012; + + /** + * Tracking the user location with bearing considered + * from a {@link CompassEngine}. + */ + public static final int COMPASS = 0x00000004; + + /** + * Tracking the user location with bearing considered from {@link android.location.Location}. + */ + public static final int GPS = 0x00000008; +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/package-info.java new file mode 100644 index 0000000000..c871a16ebe --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/package-info.java @@ -0,0 +1,4 @@ +/** + * Contains the Mapbox Location layer plugin. + */ +package com.mapbox.mapboxsdk.location; \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java index 83a0ef0e62..60cf9bb31d 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java @@ -44,7 +44,7 @@ import com.mapbox.mapboxsdk.net.ConnectivityReceiver; import com.mapbox.mapboxsdk.offline.OfflineGeometryRegionDefinition; import com.mapbox.mapboxsdk.offline.OfflineRegionDefinition; import com.mapbox.mapboxsdk.offline.OfflineTilePyramidRegionDefinition; -import com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerPlugin; +import com.mapbox.mapboxsdk.location.LocationComponent; import com.mapbox.mapboxsdk.storage.FileSource; import com.mapbox.mapboxsdk.utils.BitmapUtils; @@ -199,8 +199,8 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback { compassView.injectCompassAnimationListener(createCompassAnimationListener(cameraChangeDispatcher)); compassView.setOnClickListener(createCompassClickListener(cameraChangeDispatcher)); - // LocationLayerPlugin - mapboxMap.injectLocationLayerPlugin(new LocationLayerPlugin(context, mapboxMap)); + // LocationComponent + mapboxMap.injectLocationComponent(new LocationComponent(context, mapboxMap)); // inject widgets with MapboxMap attrView.setOnClickListener(new AttributionClickListener(context, mapboxMap)); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java index 7dddcae993..688b90eb8e 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java @@ -43,7 +43,7 @@ import com.mapbox.mapboxsdk.constants.Style; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.geometry.LatLngBounds; import com.mapbox.mapboxsdk.log.Logger; -import com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerPlugin; +import com.mapbox.mapboxsdk.location.LocationComponent; import com.mapbox.mapboxsdk.style.expressions.Expression; import com.mapbox.mapboxsdk.style.layers.Layer; import com.mapbox.mapboxsdk.style.light.Light; @@ -76,7 +76,7 @@ public final class MapboxMap { private final OnGesturesManagerInteractionListener onGesturesManagerInteractionListener; - private LocationLayerPlugin locationLayerPlugin; + private LocationComponent locationComponent; private MapboxMap.OnFpsChangedListener onFpsChangedListener; MapboxMap(NativeMapView map, Transform transform, UiSettings ui, Projection projection, @@ -112,14 +112,14 @@ public final class MapboxMap { // if user hasn't loaded a Style yet nativeMapView.setStyleUrl(Style.MAPBOX_STREETS); } - locationLayerPlugin.onStart(); + locationComponent.onStart(); } /** * Called when the hosting Activity/Fragment onStop() method is called. */ void onStop() { - locationLayerPlugin.onStop(); + locationComponent.onStop(); } /** @@ -132,7 +132,7 @@ public final class MapboxMap { outState.putBoolean(MapboxConstants.STATE_DEBUG_ACTIVE, nativeMapView.getDebug()); outState.putString(MapboxConstants.STATE_STYLE_URL, nativeMapView.getStyleUrl()); uiSettings.onSaveInstanceState(outState); - locationLayerPlugin.onSaveInstanceState(outState); + locationComponent.onSaveInstanceState(outState); } /** @@ -157,14 +157,14 @@ public final class MapboxMap { if (!TextUtils.isEmpty(styleUrl)) { nativeMapView.setStyleUrl(savedInstanceState.getString(MapboxConstants.STATE_STYLE_URL)); } - locationLayerPlugin.onRestoreInstanceState(savedInstanceState); + locationComponent.onRestoreInstanceState(savedInstanceState); } /** * Called when the hosting Activity/Fragment onDestroy()/onDestroyView() method is called. */ void onDestroy() { - locationLayerPlugin.onDestroy(); + locationComponent.onDestroy(); } /** @@ -191,14 +191,14 @@ public final class MapboxMap { * Called when the map will start loading style. */ void onStartLoadingMap() { - locationLayerPlugin.onStartLoadingMap(); + locationComponent.onStartLoadingMap(); } /** * Called the map finished loading style. */ void onFinishLoadingStyle() { - locationLayerPlugin.onFinishLoadingStyle(); + locationComponent.onFinishLoadingStyle(); } /** @@ -2311,11 +2311,11 @@ public final class MapboxMap { } // - // LocationLayerPlugin + // LocationComponent // - void injectLocationLayerPlugin(LocationLayerPlugin locationLayerPlugin) { - this.locationLayerPlugin = locationLayerPlugin; + void injectLocationComponent(LocationComponent locationComponent) { + this.locationComponent = locationComponent; } /** @@ -2323,8 +2323,8 @@ public final class MapboxMap { * @return the location layer */ @NonNull - public LocationLayerPlugin getLocationLayerPlugin() { - return locationLayerPlugin; + public LocationComponent getLocationComponent() { + return locationComponent; } // diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/CameraCompassBearingAnimator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/CameraCompassBearingAnimator.java deleted file mode 100644 index 6d5fd9da8f..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/CameraCompassBearingAnimator.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -import android.animation.ValueAnimator; - -import java.util.List; - -class CameraCompassBearingAnimator extends PluginFloatAnimator { - CameraCompassBearingAnimator(Float previous, Float target, - List updateListeners) { - super(previous, target, updateListeners); - } - - @Override - int provideAnimatorType() { - return ANIMATOR_CAMERA_COMPASS_BEARING; - } - - @Override - public void onAnimationUpdate(ValueAnimator animation) { - for (OnCameraAnimationsValuesChangeListener listener : updateListeners) { - listener.onNewCompassBearingValue((Float) animation.getAnimatedValue()); - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/CameraGpsBearingAnimator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/CameraGpsBearingAnimator.java deleted file mode 100644 index ac88a0258c..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/CameraGpsBearingAnimator.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -import android.animation.ValueAnimator; - -import java.util.List; - -class CameraGpsBearingAnimator extends PluginFloatAnimator { - CameraGpsBearingAnimator(Float previous, Float target, List updateListeners) { - super(previous, target, updateListeners); - } - - @Override - int provideAnimatorType() { - return ANIMATOR_CAMERA_GPS_BEARING; - } - - @Override - public void onAnimationUpdate(ValueAnimator animation) { - for (OnCameraAnimationsValuesChangeListener listener : updateListeners) { - listener.onNewGpsBearingValue((Float) animation.getAnimatedValue()); - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/CameraLatLngAnimator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/CameraLatLngAnimator.java deleted file mode 100644 index 6f383a480a..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/CameraLatLngAnimator.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -import android.animation.ValueAnimator; - -import com.mapbox.mapboxsdk.geometry.LatLng; - -import java.util.List; - -class CameraLatLngAnimator extends PluginLatLngAnimator { - CameraLatLngAnimator(LatLng previous, LatLng target, List updateListeners) { - super(previous, target, updateListeners); - } - - @Override - int provideAnimatorType() { - return ANIMATOR_CAMERA_LATLNG; - } - - @Override - public void onAnimationUpdate(ValueAnimator animation) { - for (OnCameraAnimationsValuesChangeListener listener : updateListeners) { - listener.onNewLatLngValue((LatLng) animation.getAnimatedValue()); - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/CompassEngine.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/CompassEngine.java deleted file mode 100644 index b4b8ea4acb..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/CompassEngine.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -import android.support.annotation.NonNull; - -/** - * Interface defining the source of compass heading data that is - * consumed by the {@link LocationLayerPlugin} when in compass related - * {@link com.mapbox.mapboxsdk.plugins.locationlayer.modes.RenderMode} or - * {@link com.mapbox.mapboxsdk.plugins.locationlayer.modes.CameraMode}s. - */ -public interface CompassEngine { - - /** - * Adds a {@link CompassListener} that can be used to - * receive heading and state changes. - * - * @param compassListener to be added - */ - void addCompassListener(@NonNull CompassListener compassListener); - - /** - * Removes a {@link CompassListener} that can be used to - * receive heading and state changes. - * - * @param compassListener to be removed - */ - void removeCompassListener(@NonNull CompassListener compassListener); - - /** - * Returns the last heading value produced and pushed via - * a compass listener. - * - * @return last heading value - */ - float getLastHeading(); - - /** - * Provides the last know accuracy status from the sensor manager. - *

- * An integer value which is identical to the {@code SensorManager} class constants: - *

    - *
  • {@link android.hardware.SensorManager#SENSOR_STATUS_NO_CONTACT}
  • - *
  • {@link android.hardware.SensorManager#SENSOR_STATUS_UNRELIABLE}
  • - *
  • {@link android.hardware.SensorManager#SENSOR_STATUS_ACCURACY_LOW}
  • - *
  • {@link android.hardware.SensorManager#SENSOR_STATUS_ACCURACY_MEDIUM}
  • - *
  • {@link android.hardware.SensorManager#SENSOR_STATUS_ACCURACY_HIGH}
  • - *
- * - * @return last accuracy status - */ - int getLastAccuracySensorStatus(); - - /** - * Lifecycle method that can be used for adding or releasing resources. - */ - void onStart(); - - /** - * Lifecycle method that can be used for adding or releasing resources. - */ - void onStop(); -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/CompassListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/CompassListener.java deleted file mode 100644 index eabf62afa2..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/CompassListener.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -/** - * Callbacks related to the compass - */ -public interface CompassListener { - - /** - * Callback's invoked when a new compass update occurs. You can listen into the compass updates - * using {@link LocationLayerPlugin#addCompassListener(CompassListener)} and implementing these - * callbacks. Note that this interface is also used internally to to update the UI chevron/arrow. - * - * @param userHeading the new compass heading - */ - void onCompassChanged(float userHeading); - - /** - * This gets invoked when the compass accuracy status changes from one value to another. It - * provides an integer value which is identical to the {@code SensorManager} class constants: - *
    - *
  • {@link android.hardware.SensorManager#SENSOR_STATUS_NO_CONTACT}
  • - *
  • {@link android.hardware.SensorManager#SENSOR_STATUS_UNRELIABLE}
  • - *
  • {@link android.hardware.SensorManager#SENSOR_STATUS_ACCURACY_LOW}
  • - *
  • {@link android.hardware.SensorManager#SENSOR_STATUS_ACCURACY_MEDIUM}
  • - *
  • {@link android.hardware.SensorManager#SENSOR_STATUS_ACCURACY_HIGH}
  • - *
- * - * @param compassStatus the new accuracy of this sensor, one of - * {@code SensorManager.SENSOR_STATUS_*} - */ - void onCompassAccuracyChange(int compassStatus); -} - diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LatLngEvaluator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LatLngEvaluator.java deleted file mode 100644 index b137ea0134..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LatLngEvaluator.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -import android.animation.TypeEvaluator; - -import com.mapbox.mapboxsdk.geometry.LatLng; - -class LatLngEvaluator implements TypeEvaluator { - - private final LatLng latLng = new LatLng(); - - @Override - public LatLng evaluate(float fraction, LatLng startValue, LatLng endValue) { - latLng.setLatitude(startValue.getLatitude() - + ((endValue.getLatitude() - startValue.getLatitude()) * fraction)); - latLng.setLongitude(startValue.getLongitude() - + ((endValue.getLongitude() - startValue.getLongitude()) * fraction)); - return latLng; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerAccuracyAnimator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerAccuracyAnimator.java deleted file mode 100644 index 1d200f454d..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerAccuracyAnimator.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -import android.animation.ValueAnimator; - -import java.util.List; - -class LayerAccuracyAnimator extends PluginFloatAnimator { - - LayerAccuracyAnimator(Float previous, Float target, List updateListeners) { - super(previous, target, updateListeners); - } - - @Override - int provideAnimatorType() { - return ANIMATOR_LAYER_ACCURACY; - } - - @Override - public void onAnimationUpdate(ValueAnimator animation) { - for (OnLayerAnimationsValuesChangeListener listener : updateListeners) { - listener.onNewAccuracyRadiusValue((Float) animation.getAnimatedValue()); - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerBitmapProvider.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerBitmapProvider.java deleted file mode 100644 index aabf083525..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerBitmapProvider.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.drawable.Drawable; -import android.support.annotation.ColorInt; -import android.support.annotation.DrawableRes; -import android.support.v4.content.ContextCompat; - -import com.mapbox.mapboxsdk.R; - -import static com.mapbox.mapboxsdk.plugins.locationlayer.Utils.generateShadow; -import static com.mapbox.mapboxsdk.plugins.locationlayer.Utils.getBitmapFromDrawable; -import static com.mapbox.mapboxsdk.plugins.locationlayer.Utils.getDrawable; - -class LayerBitmapProvider { - - private final Context context; - - LayerBitmapProvider(Context context) { - this.context = context; - } - - Bitmap generateBitmap(@DrawableRes int drawableRes, @ColorInt Integer tintColor) { - Drawable drawable = getDrawable(context, drawableRes, tintColor); - return getBitmapFromDrawable(drawable); - } - - Bitmap generateShadowBitmap(LocationLayerOptions options) { - Drawable shadowDrawable = ContextCompat.getDrawable(context, R.drawable.mapbox_user_icon_shadow); - return generateShadow(shadowDrawable, options.elevation()); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerCompassBearingAnimator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerCompassBearingAnimator.java deleted file mode 100644 index 3b3898d1e6..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerCompassBearingAnimator.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -import android.animation.ValueAnimator; - -import java.util.List; - -class LayerCompassBearingAnimator extends PluginFloatAnimator { - LayerCompassBearingAnimator(Float previous, - Float target, List updateListeners) { - super(previous, target, updateListeners); - } - - @Override - int provideAnimatorType() { - return ANIMATOR_LAYER_COMPASS_BEARING; - } - - @Override - public void onAnimationUpdate(ValueAnimator animation) { - for (OnLayerAnimationsValuesChangeListener listener : updateListeners) { - listener.onNewCompassBearingValue((Float) animation.getAnimatedValue()); - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerFeatureProvider.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerFeatureProvider.java deleted file mode 100644 index 9f119ef6eb..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerFeatureProvider.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.mapbox.geojson.Feature; -import com.mapbox.geojson.Point; - -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_COMPASS_BEARING; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_GPS_BEARING; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_LOCATION_STALE; - -class LayerFeatureProvider { - - @NonNull - Feature generateLocationFeature(@Nullable Feature locationFeature, LocationLayerOptions options) { - if (locationFeature != null) { - return locationFeature; - } - locationFeature = Feature.fromGeometry(Point.fromLngLat(0.0, 0.0)); - locationFeature.addNumberProperty(PROPERTY_GPS_BEARING, 0f); - locationFeature.addNumberProperty(PROPERTY_COMPASS_BEARING, 0f); - locationFeature.addBooleanProperty(PROPERTY_LOCATION_STALE, options.enableStaleState()); - return locationFeature; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerGpsBearingAnimator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerGpsBearingAnimator.java deleted file mode 100644 index c14ffbb935..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerGpsBearingAnimator.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -import android.animation.ValueAnimator; - -import java.util.List; - -class LayerGpsBearingAnimator extends PluginFloatAnimator { - LayerGpsBearingAnimator(Float previous, Float target, List updateListeners) { - super(previous, target, updateListeners); - } - - @Override - int provideAnimatorType() { - return ANIMATOR_LAYER_GPS_BEARING; - } - - @Override - public void onAnimationUpdate(ValueAnimator animation) { - for (OnLayerAnimationsValuesChangeListener listener : updateListeners) { - listener.onNewGpsBearingValue((Float) animation.getAnimatedValue()); - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerLatLngAnimator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerLatLngAnimator.java deleted file mode 100644 index 0ff0a46bd7..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerLatLngAnimator.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -import android.animation.ValueAnimator; - -import com.mapbox.mapboxsdk.geometry.LatLng; - -import java.util.List; - -class LayerLatLngAnimator extends PluginLatLngAnimator { - LayerLatLngAnimator(LatLng previous, LatLng target, List updateListeners) { - super(previous, target, updateListeners); - } - - @Override - int provideAnimatorType() { - return ANIMATOR_LAYER_LATLNG; - } - - @Override - public void onAnimationUpdate(ValueAnimator animation) { - for (OnLayerAnimationsValuesChangeListener listener : updateListeners) { - listener.onNewLatLngValue((LatLng) animation.getAnimatedValue()); - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerSourceProvider.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerSourceProvider.java deleted file mode 100644 index 31f5b75426..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerSourceProvider.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -import com.mapbox.geojson.Feature; -import com.mapbox.mapboxsdk.style.layers.CircleLayer; -import com.mapbox.mapboxsdk.style.layers.Layer; -import com.mapbox.mapboxsdk.style.layers.Property; -import com.mapbox.mapboxsdk.style.layers.SymbolLayer; -import com.mapbox.mapboxsdk.style.sources.GeoJsonOptions; -import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; - -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.ACCURACY_LAYER; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.BACKGROUND_LAYER; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.BEARING_LAYER; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.FOREGROUND_LAYER; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.LOCATION_SOURCE; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_ACCURACY_ALPHA; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_ACCURACY_COLOR; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_ACCURACY_RADIUS; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_BACKGROUND_ICON; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_BACKGROUND_STALE_ICON; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_BEARING_ICON; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_COMPASS_BEARING; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_FOREGROUND_ICON; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_FOREGROUND_ICON_OFFSET; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_FOREGROUND_STALE_ICON; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_GPS_BEARING; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_LOCATION_STALE; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_SHADOW_ICON_OFFSET; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.SHADOW_ICON; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.SHADOW_LAYER; -import static com.mapbox.mapboxsdk.style.expressions.Expression.get; -import static com.mapbox.mapboxsdk.style.expressions.Expression.literal; -import static com.mapbox.mapboxsdk.style.expressions.Expression.match; -import static com.mapbox.mapboxsdk.style.expressions.Expression.stop; -import static com.mapbox.mapboxsdk.style.expressions.Expression.switchCase; -import static com.mapbox.mapboxsdk.style.layers.Property.ICON_ROTATION_ALIGNMENT_MAP; -import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleColor; -import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleOpacity; -import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circlePitchAlignment; -import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleRadius; -import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleStrokeColor; -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.iconOffset; -import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconRotate; -import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconRotationAlignment; - -class LayerSourceProvider { - - private static final String EMPTY_STRING = ""; - - GeoJsonSource generateSource(Feature locationFeature) { - return new GeoJsonSource( - LOCATION_SOURCE, - locationFeature, - new GeoJsonOptions().withMaxZoom(16) - ); - } - - Layer generateLayer(String layerId) { - SymbolLayer layer = new SymbolLayer(layerId, LOCATION_SOURCE); - layer.setProperties( - iconAllowOverlap(true), - iconIgnorePlacement(true), - iconRotationAlignment(ICON_ROTATION_ALIGNMENT_MAP), - iconRotate( - match(literal(layerId), literal(0f), - stop(FOREGROUND_LAYER, get(PROPERTY_GPS_BEARING)), - stop(BACKGROUND_LAYER, get(PROPERTY_GPS_BEARING)), - stop(SHADOW_LAYER, get(PROPERTY_GPS_BEARING)), - stop(BEARING_LAYER, get(PROPERTY_COMPASS_BEARING)) - ) - ), - iconImage( - match(literal(layerId), literal(EMPTY_STRING), - stop(FOREGROUND_LAYER, switchCase( - get(PROPERTY_LOCATION_STALE), get(PROPERTY_FOREGROUND_STALE_ICON), - get(PROPERTY_FOREGROUND_ICON))), - stop(BACKGROUND_LAYER, switchCase( - get(PROPERTY_LOCATION_STALE), get(PROPERTY_BACKGROUND_STALE_ICON), - get(PROPERTY_BACKGROUND_ICON))), - stop(SHADOW_LAYER, literal(SHADOW_ICON)), - stop(BEARING_LAYER, get(PROPERTY_BEARING_ICON)) - ) - ), - iconOffset( - match(literal(layerId), literal(new Float[] {0f, 0f}), - stop(literal(FOREGROUND_LAYER), get(PROPERTY_FOREGROUND_ICON_OFFSET)), - stop(literal(SHADOW_LAYER), get(PROPERTY_SHADOW_ICON_OFFSET)) - ) - ) - ); - return layer; - } - - Layer generateAccuracyLayer() { - return new CircleLayer(ACCURACY_LAYER, LOCATION_SOURCE) - .withProperties( - circleRadius(get(PROPERTY_ACCURACY_RADIUS)), - circleColor(get(PROPERTY_ACCURACY_COLOR)), - circleOpacity(get(PROPERTY_ACCURACY_ALPHA)), - circleStrokeColor(get(PROPERTY_ACCURACY_COLOR)), - circlePitchAlignment(Property.CIRCLE_PITCH_ALIGNMENT_MAP) - ); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayer.java deleted file mode 100644 index 67cd26d9cc..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayer.java +++ /dev/null @@ -1,394 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -import android.graphics.Bitmap; -import android.graphics.PointF; -import android.support.annotation.ColorInt; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import com.mapbox.geojson.Feature; -import com.mapbox.geojson.Point; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.maps.MapboxMap; -import com.mapbox.mapboxsdk.plugins.locationlayer.modes.RenderMode; -import com.mapbox.mapboxsdk.style.layers.Layer; -import com.mapbox.mapboxsdk.style.layers.SymbolLayer; -import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; - -import java.util.ArrayList; -import java.util.List; - -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.ACCURACY_LAYER; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.BACKGROUND_ICON; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.BACKGROUND_LAYER; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.BACKGROUND_STALE_ICON; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.BEARING_ICON; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.BEARING_LAYER; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.FOREGROUND_ICON; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.FOREGROUND_LAYER; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.FOREGROUND_STALE_ICON; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.LOCATION_SOURCE; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_ACCURACY_ALPHA; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_ACCURACY_COLOR; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_ACCURACY_RADIUS; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_BACKGROUND_ICON; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_BACKGROUND_STALE_ICON; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_BEARING_ICON; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_COMPASS_BEARING; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_FOREGROUND_ICON; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_FOREGROUND_ICON_OFFSET; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_FOREGROUND_STALE_ICON; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_GPS_BEARING; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_LOCATION_STALE; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_SHADOW_ICON_OFFSET; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.SHADOW_ICON; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.SHADOW_LAYER; -import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate; -import static com.mapbox.mapboxsdk.style.expressions.Expression.linear; -import static com.mapbox.mapboxsdk.style.expressions.Expression.stop; -import static com.mapbox.mapboxsdk.style.expressions.Expression.zoom; -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.colorToRgbaString; -import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconSize; -import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility; - -final class LocationLayer implements PluginAnimator.OnLayerAnimationsValuesChangeListener { - - @RenderMode.Mode - private int renderMode; - - private final MapboxMap mapboxMap; - private final LayerSourceProvider layerSourceProvider; - private final LayerBitmapProvider bitmapProvider; - private LocationLayerOptions options; - - private final List layerMap = new ArrayList<>(); - private Feature locationFeature; - private GeoJsonSource locationSource; - - private boolean isHidden = true; - - LocationLayer(MapboxMap mapboxMap, LayerSourceProvider layerSourceProvider, - LayerFeatureProvider featureProvider, LayerBitmapProvider bitmapProvider, - LocationLayerOptions options) { - this.mapboxMap = mapboxMap; - this.layerSourceProvider = layerSourceProvider; - this.bitmapProvider = bitmapProvider; - this.locationFeature = featureProvider.generateLocationFeature(locationFeature, options); - initializeComponents(options); - setRenderMode(RenderMode.NORMAL); - } - - void initializeComponents(LocationLayerOptions options) { - addLocationSource(); - addLayers(options.layerBelow()); - applyStyle(options); - - if (isHidden) { - hide(); - } else { - show(); - } - } - - void applyStyle(@NonNull LocationLayerOptions options) { - this.options = options; - - float elevation = options.elevation(); - // Only add icon elevation if the values greater than 0. - if (elevation > 0) { - styleShadow(options); - } - styleForeground(options); - styleBackground(options); - styleBearing(options); - styleAccuracy(options.accuracyAlpha(), options.accuracyColor()); - styleScaling(options); - determineIconsSource(options); - } - - void setRenderMode(@RenderMode.Mode int renderMode) { - this.renderMode = renderMode; - - if (!isHidden) { - boolean isStale = locationFeature.getBooleanProperty(PROPERTY_LOCATION_STALE); - switch (renderMode) { - case RenderMode.NORMAL: - styleForeground(options); - setLayerVisibility(SHADOW_LAYER, true); - setLayerVisibility(FOREGROUND_LAYER, true); - setLayerVisibility(BACKGROUND_LAYER, true); - setLayerVisibility(ACCURACY_LAYER, !isStale); - setLayerVisibility(BEARING_LAYER, false); - break; - case RenderMode.COMPASS: - styleForeground(options); - setLayerVisibility(SHADOW_LAYER, true); - setLayerVisibility(FOREGROUND_LAYER, true); - setLayerVisibility(BACKGROUND_LAYER, true); - setLayerVisibility(ACCURACY_LAYER, !isStale); - setLayerVisibility(BEARING_LAYER, true); - break; - case RenderMode.GPS: - styleForeground(options); - setLayerVisibility(SHADOW_LAYER, false); - setLayerVisibility(FOREGROUND_LAYER, true); - setLayerVisibility(BACKGROUND_LAYER, true); - setLayerVisibility(ACCURACY_LAYER, false); - setLayerVisibility(BEARING_LAYER, false); - break; - default: - break; - } - - determineIconsSource(options); - } - } - - int getRenderMode() { - return renderMode; - } - - // - // Layer action - // - - void show() { - isHidden = false; - setRenderMode(renderMode); - } - - void hide() { - isHidden = true; - for (String layerId : layerMap) { - setLayerVisibility(layerId, false); - } - } - - void updateForegroundOffset(double tilt) { - JsonArray foregroundJsonArray = new JsonArray(); - foregroundJsonArray.add(0f); - foregroundJsonArray.add((float) (-0.05 * tilt)); - locationFeature.addProperty(PROPERTY_FOREGROUND_ICON_OFFSET, foregroundJsonArray); - - JsonArray backgroundJsonArray = new JsonArray(); - backgroundJsonArray.add(0f); - backgroundJsonArray.add((float) (0.05 * tilt)); - locationFeature.addProperty(PROPERTY_SHADOW_ICON_OFFSET, backgroundJsonArray); - - refreshSource(); - } - - void updateForegroundBearing(float bearing) { - if (renderMode != RenderMode.GPS) { - setBearingProperty(PROPERTY_GPS_BEARING, bearing); - } - } - - private void setLayerVisibility(String layerId, boolean visible) { - Layer layer = mapboxMap.getLayer(layerId); - if (layer != null) { - String targetVisibility = visible ? VISIBLE : NONE; - if (!layer.getVisibility().value.equals(targetVisibility)) { - layer.setProperties(visibility(visible ? VISIBLE : NONE)); - } - } - } - - private void addLayers(String idBelowLayer) { - addSymbolLayer(BEARING_LAYER, idBelowLayer); - addSymbolLayer(FOREGROUND_LAYER, BEARING_LAYER); - addSymbolLayer(BACKGROUND_LAYER, FOREGROUND_LAYER); - addSymbolLayer(SHADOW_LAYER, BACKGROUND_LAYER); - addAccuracyLayer(); - } - - private void addSymbolLayer(String layerId, String beforeLayerId) { - Layer layer = layerSourceProvider.generateLayer(layerId); - addLayerToMap(layer, beforeLayerId); - } - - private void addAccuracyLayer() { - Layer accuracyLayer = layerSourceProvider.generateAccuracyLayer(); - addLayerToMap(accuracyLayer, BACKGROUND_LAYER); - } - - private void addLayerToMap(Layer layer, @NonNull String idBelowLayer) { - mapboxMap.addLayerBelow(layer, idBelowLayer); - layerMap.add(layer.getId()); - } - - private void setBearingProperty(String propertyId, float bearing) { - locationFeature.addNumberProperty(propertyId, bearing); - refreshSource(); - } - - private void updateAccuracyRadius(float accuracy) { - if (renderMode == RenderMode.COMPASS || renderMode == RenderMode.NORMAL) { - locationFeature.addNumberProperty(PROPERTY_ACCURACY_RADIUS, accuracy); - refreshSource(); - } - } - - // - // Source actions - // - - private void addLocationSource() { - locationSource = layerSourceProvider.generateSource(locationFeature); - mapboxMap.addSource(locationSource); - } - - private void refreshSource() { - GeoJsonSource source = mapboxMap.getSourceAs(LOCATION_SOURCE); - if (source != null) { - locationSource.setGeoJson(locationFeature); - } - } - - private void setLocationPoint(Point locationPoint) { - JsonObject properties = locationFeature.properties(); - if (properties != null) { - locationFeature = Feature.fromGeometry(locationPoint, properties); - refreshSource(); - } - } - - // - // Styling - // - - private void styleBackground(LocationLayerOptions options) { - Bitmap backgroundBitmap = bitmapProvider.generateBitmap( - options.backgroundDrawable(), options.backgroundTintColor() - ); - Bitmap backgroundStaleBitmap = bitmapProvider.generateBitmap( - options.backgroundDrawableStale(), options.backgroundStaleTintColor() - ); - mapboxMap.addImage(BACKGROUND_ICON, backgroundBitmap); - mapboxMap.addImage(BACKGROUND_STALE_ICON, backgroundStaleBitmap); - } - - private void styleShadow(LocationLayerOptions options) { - mapboxMap.addImage(SHADOW_ICON, bitmapProvider.generateShadowBitmap(options)); - } - - private void styleBearing(LocationLayerOptions options) { - Bitmap bearingBitmap = bitmapProvider.generateBitmap(options.bearingDrawable(), options.bearingTintColor()); - mapboxMap.addImage(BEARING_ICON, bearingBitmap); - } - - private void styleAccuracy(float accuracyAlpha, @ColorInt int accuracyColor) { - locationFeature.addNumberProperty(PROPERTY_ACCURACY_ALPHA, accuracyAlpha); - locationFeature.addStringProperty(PROPERTY_ACCURACY_COLOR, colorToRgbaString(accuracyColor)); - refreshSource(); - } - - private void styleForeground(LocationLayerOptions options) { - Bitmap foregroundBitmap = bitmapProvider.generateBitmap( - options.foregroundDrawable(), options.foregroundTintColor() - ); - Bitmap foregroundBitmapStale = bitmapProvider.generateBitmap( - options.foregroundDrawableStale(), options.foregroundStaleTintColor() - ); - if (renderMode == RenderMode.GPS) { - foregroundBitmap = bitmapProvider.generateBitmap( - options.gpsDrawable(), options.foregroundTintColor() - ); - foregroundBitmapStale = bitmapProvider.generateBitmap( - options.gpsDrawable(), options.foregroundStaleTintColor() - ); - } - mapboxMap.addImage(FOREGROUND_ICON, foregroundBitmap); - mapboxMap.addImage(FOREGROUND_STALE_ICON, foregroundBitmapStale); - } - - private void styleScaling(LocationLayerOptions options) { - for (String layerId : layerMap) { - Layer layer = mapboxMap.getLayer(layerId); - if (layer != null && layer instanceof SymbolLayer) { - layer.setProperties( - iconSize( - interpolate(linear(), zoom(), - stop(options.minZoom(), options.minZoomIconScale()), - stop(options.maxZoom(), options.maxZoomIconScale()) - ) - ) - ); - } - } - } - - private void determineIconsSource(LocationLayerOptions options) { - String foregroundIconString = buildIconString( - renderMode == RenderMode.GPS ? options.gpsName() : options.foregroundName(), FOREGROUND_ICON); - String foregroundStaleIconString = buildIconString(options.foregroundStaleName(), FOREGROUND_STALE_ICON); - String backgroundIconString = buildIconString(options.backgroundName(), BACKGROUND_ICON); - String backgroundStaleIconString = buildIconString(options.backgroundStaleName(), BACKGROUND_STALE_ICON); - String bearingIconString = buildIconString(options.bearingName(), BEARING_ICON); - - locationFeature.addStringProperty(PROPERTY_FOREGROUND_ICON, foregroundIconString); - locationFeature.addStringProperty(PROPERTY_BACKGROUND_ICON, backgroundIconString); - locationFeature.addStringProperty(PROPERTY_FOREGROUND_STALE_ICON, foregroundStaleIconString); - locationFeature.addStringProperty(PROPERTY_BACKGROUND_STALE_ICON, backgroundStaleIconString); - locationFeature.addStringProperty(PROPERTY_BEARING_ICON, bearingIconString); - refreshSource(); - } - - private String buildIconString(@Nullable String bitmapName, @NonNull String drawableName) { - if (bitmapName != null) { - return bitmapName; - } - return drawableName; - } - - void setLocationsStale(boolean isStale) { - locationFeature.addBooleanProperty(PROPERTY_LOCATION_STALE, isStale); - refreshSource(); - if (renderMode != RenderMode.GPS) { - setLayerVisibility(ACCURACY_LAYER, !isStale); - } - } - - // - // Map click event - // - - boolean onMapClick(LatLng point) { - PointF screenLoc = mapboxMap.getProjection().toScreenLocation(point); - List features = mapboxMap.queryRenderedFeatures(screenLoc, - BACKGROUND_LAYER, - FOREGROUND_LAYER, - BEARING_LAYER - ); - return !features.isEmpty(); - } - - @Override - public void onNewLatLngValue(LatLng latLng) { - Point point = Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude()); - setLocationPoint(point); - } - - @Override - public void onNewGpsBearingValue(float gpsBearing) { - if (renderMode == RenderMode.GPS) { - setBearingProperty(PROPERTY_GPS_BEARING, gpsBearing); - } - } - - @Override - public void onNewCompassBearingValue(float compassBearing) { - if (renderMode == RenderMode.COMPASS) { - setBearingProperty(PROPERTY_COMPASS_BEARING, compassBearing); - } - } - - @Override - public void onNewAccuracyRadiusValue(float accuracyRadiusValue) { - updateAccuracyRadius(accuracyRadiusValue); - } -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerCamera.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerCamera.java deleted file mode 100644 index b59f849aa1..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerCamera.java +++ /dev/null @@ -1,263 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -import android.content.Context; -import android.graphics.PointF; -import android.view.MotionEvent; - -import com.mapbox.android.gestures.AndroidGesturesManager; -import com.mapbox.android.gestures.MoveGestureDetector; -import com.mapbox.android.gestures.RotateGestureDetector; -import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.maps.MapboxMap; -import com.mapbox.mapboxsdk.plugins.locationlayer.modes.CameraMode; - -import java.util.List; -import java.util.Set; - -final class LocationLayerCamera implements PluginAnimator.OnCameraAnimationsValuesChangeListener { - - @CameraMode.Mode - private int cameraMode; - - private final MapboxMap mapboxMap; - private final OnCameraTrackingChangedListener internalCameraTrackingChangedListener; - private LocationLayerOptions options; - private boolean adjustFocalPoint; - - private final MoveGestureDetector moveGestureDetector; - private final OnCameraMoveInvalidateListener onCameraMoveInvalidateListener; - - LocationLayerCamera( - Context context, - MapboxMap mapboxMap, - OnCameraTrackingChangedListener internalCameraTrackingChangedListener, - LocationLayerOptions options, - OnCameraMoveInvalidateListener onCameraMoveInvalidateListener) { - this.mapboxMap = mapboxMap; - mapboxMap.setGesturesManager( - new PluginsGesturesManager(context), true, true); - moveGestureDetector = mapboxMap.getGesturesManager().getMoveGestureDetector(); - mapboxMap.addOnMoveListener(onMoveListener); - mapboxMap.addOnRotateListener(onRotateListener); - mapboxMap.addOnFlingListener(onFlingListener); - - this.internalCameraTrackingChangedListener = internalCameraTrackingChangedListener; - this.onCameraMoveInvalidateListener = onCameraMoveInvalidateListener; - initializeOptions(options); - } - - // Package private for testing purposes - LocationLayerCamera(MapboxMap mapboxMap, - MoveGestureDetector moveGestureDetector, - OnCameraTrackingChangedListener internalCameraTrackingChangedListener, - OnCameraMoveInvalidateListener onCameraMoveInvalidateListener) { - this.mapboxMap = mapboxMap; - this.moveGestureDetector = moveGestureDetector; - this.internalCameraTrackingChangedListener = internalCameraTrackingChangedListener; - this.onCameraMoveInvalidateListener = onCameraMoveInvalidateListener; - } - - void initializeOptions(LocationLayerOptions options) { - this.options = options; - } - - void setCameraMode(@CameraMode.Mode int cameraMode) { - final boolean wasTracking = isLocationTracking(); - this.cameraMode = cameraMode; - mapboxMap.cancelTransitions(); - adjustGesturesThresholds(); - notifyCameraTrackingChangeListener(wasTracking); - } - - int getCameraMode() { - return cameraMode; - } - - private void setBearing(float bearing) { - mapboxMap.moveCamera(CameraUpdateFactory.bearingTo(bearing)); - onCameraMoveInvalidateListener.onInvalidateCameraMove(); - } - - private void setLatLng(LatLng latLng) { - mapboxMap.moveCamera(CameraUpdateFactory.newLatLng(latLng)); - onCameraMoveInvalidateListener.onInvalidateCameraMove(); - } - - private void setZoom(float zoom) { - mapboxMap.moveCamera(CameraUpdateFactory.zoomTo(zoom)); - onCameraMoveInvalidateListener.onInvalidateCameraMove(); - } - - private void setTilt(float tilt) { - mapboxMap.moveCamera(CameraUpdateFactory.tiltTo(tilt)); - onCameraMoveInvalidateListener.onInvalidateCameraMove(); - } - - @Override - public void onNewLatLngValue(LatLng latLng) { - if (cameraMode == CameraMode.TRACKING - || cameraMode == CameraMode.TRACKING_COMPASS - || cameraMode == CameraMode.TRACKING_GPS - || cameraMode == CameraMode.TRACKING_GPS_NORTH) { - setLatLng(latLng); - - if (adjustFocalPoint) { - PointF focalPoint = mapboxMap.getProjection().toScreenLocation(latLng); - mapboxMap.getUiSettings().setFocalPoint(focalPoint); - adjustFocalPoint = false; - } - } - } - - @Override - public void onNewGpsBearingValue(float gpsBearing) { - boolean trackingNorth = cameraMode == CameraMode.TRACKING_GPS_NORTH - && mapboxMap.getCameraPosition().bearing != 0; - - if (cameraMode == CameraMode.TRACKING_GPS - || cameraMode == CameraMode.NONE_GPS - || trackingNorth) { - setBearing(gpsBearing); - } - } - - @Override - public void onNewCompassBearingValue(float compassBearing) { - if (cameraMode == CameraMode.TRACKING_COMPASS - || cameraMode == CameraMode.NONE_COMPASS) { - setBearing(compassBearing); - } - } - - @Override - public void onNewZoomValue(float zoom) { - setZoom(zoom); - } - - @Override - public void onNewTiltValue(float tilt) { - setTilt(tilt); - } - - private void adjustGesturesThresholds() { - if (isLocationTracking()) { - adjustFocalPoint = true; - moveGestureDetector.setMoveThreshold(options.trackingInitialMoveThreshold()); - } else { - moveGestureDetector.setMoveThreshold(0f); - } - } - - private boolean isLocationTracking() { - return cameraMode == CameraMode.TRACKING - || cameraMode == CameraMode.TRACKING_COMPASS - || cameraMode == CameraMode.TRACKING_GPS - || cameraMode == CameraMode.TRACKING_GPS_NORTH; - } - - private boolean isBearingTracking() { - return cameraMode == CameraMode.NONE_COMPASS - || cameraMode == CameraMode.TRACKING_COMPASS - || cameraMode == CameraMode.NONE_GPS - || cameraMode == CameraMode.TRACKING_GPS - || cameraMode == CameraMode.TRACKING_GPS_NORTH; - } - - private void notifyCameraTrackingChangeListener(boolean wasTracking) { - internalCameraTrackingChangedListener.onCameraTrackingChanged(cameraMode); - if (wasTracking && !isLocationTracking()) { - mapboxMap.getUiSettings().setFocalPoint(null); - internalCameraTrackingChangedListener.onCameraTrackingDismissed(); - } - } - - private MapboxMap.OnMoveListener onMoveListener = new MapboxMap.OnMoveListener() { - private boolean interrupt; - - @Override - public void onMoveBegin(MoveGestureDetector detector) { - if (detector.getPointersCount() > 1 - && detector.getMoveThreshold() != options.trackingMultiFingerMoveThreshold() - && isLocationTracking()) { - detector.setMoveThreshold(options.trackingMultiFingerMoveThreshold()); - interrupt = true; - } - } - - @Override - public void onMove(MoveGestureDetector detector) { - if (interrupt) { - detector.interrupt(); - return; - } - - setCameraMode(CameraMode.NONE); - } - - @Override - public void onMoveEnd(MoveGestureDetector detector) { - if (!interrupt && isLocationTracking()) { - moveGestureDetector.setMoveThreshold(options.trackingInitialMoveThreshold()); - } - interrupt = false; - } - }; - - private MapboxMap.OnRotateListener onRotateListener = new MapboxMap.OnRotateListener() { - @Override - public void onRotateBegin(RotateGestureDetector detector) { - if (isBearingTracking()) { - setCameraMode(CameraMode.NONE); - } - } - - @Override - public void onRotate(RotateGestureDetector detector) { - // no implementation - } - - @Override - public void onRotateEnd(RotateGestureDetector detector) { - // no implementation - } - }; - - private MapboxMap.OnFlingListener onFlingListener = new MapboxMap.OnFlingListener() { - @Override - public void onFling() { - setCameraMode(CameraMode.NONE); - } - }; - - private class PluginsGesturesManager extends AndroidGesturesManager { - - public PluginsGesturesManager(Context context) { - super(context); - } - - public PluginsGesturesManager(Context context, boolean applyDefaultThresholds) { - super(context, applyDefaultThresholds); - } - - public PluginsGesturesManager(Context context, Set[] exclusiveGestures) { - super(context, exclusiveGestures); - } - - public PluginsGesturesManager(Context context, List> exclusiveGestures, - boolean applyDefaultThresholds) { - super(context, exclusiveGestures, applyDefaultThresholds); - } - - @Override - public boolean onTouchEvent(MotionEvent motionEvent) { - if (motionEvent != null) { - int action = motionEvent.getActionMasked(); - if (action == MotionEvent.ACTION_UP) { - adjustGesturesThresholds(); - } - } - return super.onTouchEvent(motionEvent); - } - } -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerCompassEngine.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerCompassEngine.java deleted file mode 100644 index f05580a21f..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerCompassEngine.java +++ /dev/null @@ -1,267 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -import android.hardware.Sensor; -import android.hardware.SensorEvent; -import android.hardware.SensorEventListener; -import android.hardware.SensorManager; -import android.os.SystemClock; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.view.Surface; -import android.view.WindowManager; - -import java.util.ArrayList; -import java.util.List; - -import timber.log.Timber; - -/** - * This manager class handles compass events such as starting the tracking of device bearing, or - * when a new compass update occurs. - */ -class LocationLayerCompassEngine implements CompassEngine, SensorEventListener { - - // The rate sensor events will be delivered at. As the Android documentation states, this is only - // a hint to the system and the events might actually be received faster or slower then this - // specified rate. Since the minimum Android API levels about 9, we are able to set this value - // ourselves rather than using one of the provided constants which deliver updates too quickly for - // our use case. The default is set to 100ms - private static final int SENSOR_DELAY_MICROS = 100 * 1000; - // Filtering coefficient 0 < ALPHA < 1 - private static final float ALPHA = 0.45f; - - private final WindowManager windowManager; - private final SensorManager sensorManager; - private final List compassListeners = new ArrayList<>(); - - // Not all devices have a compassSensor - @Nullable - private Sensor compassSensor; - @Nullable - private Sensor gravitySensor; - @Nullable - private Sensor magneticFieldSensor; - - private float[] truncatedRotationVectorValue = new float[4]; - private float[] rotationMatrix = new float[9]; - private float[] rotationVectorValue; - private float lastHeading; - private int lastAccuracySensorStatus; - - private long compassUpdateNextTimestamp; - private float[] gravityValues = new float[3]; - private float[] magneticValues = new float[3]; - - /** - * Construct a new instance of the this class. A internal compass listeners needed to separate it - * from the cleared list of public listeners. - */ - LocationLayerCompassEngine(WindowManager windowManager, SensorManager sensorManager) { - this.windowManager = windowManager; - this.sensorManager = sensorManager; - compassSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR); - if (compassSensor == null) { - if (isGyroscopeAvailable()) { - Timber.d("Rotation vector sensor not supported on device, falling back to orientation."); - compassSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION); - } else { - Timber.d("Rotation vector sensor not supported on device, falling back to accelerometer and magnetic field."); - gravitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); - magneticFieldSensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); - } - } - } - - @Override - public void addCompassListener(@NonNull CompassListener compassListener) { - if (compassListeners.isEmpty()) { - onStart(); - } - compassListeners.add(compassListener); - } - - @Override - public void removeCompassListener(@NonNull CompassListener compassListener) { - compassListeners.remove(compassListener); - if (compassListeners.isEmpty()) { - onStop(); - } - } - - @Override - public int getLastAccuracySensorStatus() { - return lastAccuracySensorStatus; - } - - @Override - public float getLastHeading() { - return lastHeading; - } - - @Override - public void onStart() { - registerSensorListeners(); - } - - @Override - public void onStop() { - unregisterSensorListeners(); - } - - @Override - public void onSensorChanged(SensorEvent event) { - // check when the last time the compass was updated, return if too soon. - long currentTime = SystemClock.elapsedRealtime(); - if (currentTime < compassUpdateNextTimestamp) { - return; - } - if (lastAccuracySensorStatus == SensorManager.SENSOR_STATUS_UNRELIABLE) { - Timber.d("Compass sensor is unreliable, device calibration is needed."); - return; - } - if (event.sensor.getType() == Sensor.TYPE_ROTATION_VECTOR) { - rotationVectorValue = getRotationVectorFromSensorEvent(event); - updateOrientation(); - - // Update the compassUpdateNextTimestamp - compassUpdateNextTimestamp = currentTime + LocationLayerConstants.COMPASS_UPDATE_RATE_MS; - } else if (event.sensor.getType() == Sensor.TYPE_ORIENTATION) { - notifyCompassChangeListeners((event.values[0] + 360) % 360); - } else if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { - gravityValues = lowPassFilter(getRotationVectorFromSensorEvent(event), gravityValues); - updateOrientation(); - } else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) { - magneticValues = lowPassFilter(getRotationVectorFromSensorEvent(event), magneticValues); - updateOrientation(); - } - } - - @Override - public void onAccuracyChanged(Sensor sensor, int accuracy) { - if (lastAccuracySensorStatus != accuracy) { - for (CompassListener compassListener : compassListeners) { - compassListener.onCompassAccuracyChange(accuracy); - } - lastAccuracySensorStatus = accuracy; - } - } - - private boolean isGyroscopeAvailable() { - return sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) != null; - } - - @SuppressWarnings("SuspiciousNameCombination") - private void updateOrientation() { - if (rotationVectorValue != null) { - SensorManager.getRotationMatrixFromVector(rotationMatrix, rotationVectorValue); - } else { - // Get rotation matrix given the gravity and geomagnetic matrices - SensorManager.getRotationMatrix(rotationMatrix, null, gravityValues, magneticValues); - } - - final int worldAxisForDeviceAxisX; - final int worldAxisForDeviceAxisY; - - // Remap the axes as if the device screen was the instrument panel, - // and adjust the rotation matrix for the device orientation. - switch (windowManager.getDefaultDisplay().getRotation()) { - case Surface.ROTATION_90: - worldAxisForDeviceAxisX = SensorManager.AXIS_Z; - worldAxisForDeviceAxisY = SensorManager.AXIS_MINUS_X; - break; - case Surface.ROTATION_180: - worldAxisForDeviceAxisX = SensorManager.AXIS_MINUS_X; - worldAxisForDeviceAxisY = SensorManager.AXIS_MINUS_Z; - break; - case Surface.ROTATION_270: - worldAxisForDeviceAxisX = SensorManager.AXIS_MINUS_Z; - worldAxisForDeviceAxisY = SensorManager.AXIS_X; - break; - case Surface.ROTATION_0: - default: - worldAxisForDeviceAxisX = SensorManager.AXIS_X; - worldAxisForDeviceAxisY = SensorManager.AXIS_Z; - break; - } - - float[] adjustedRotationMatrix = new float[9]; - SensorManager.remapCoordinateSystem(rotationMatrix, worldAxisForDeviceAxisX, - worldAxisForDeviceAxisY, adjustedRotationMatrix); - - // Transform rotation matrix into azimuth/pitch/roll - float[] orientation = new float[3]; - SensorManager.getOrientation(adjustedRotationMatrix, orientation); - - // The x-axis is all we care about here. - notifyCompassChangeListeners((float) Math.toDegrees(orientation[0])); - } - - private void notifyCompassChangeListeners(float heading) { - for (CompassListener compassListener : compassListeners) { - compassListener.onCompassChanged(heading); - } - lastHeading = heading; - } - - private void registerSensorListeners() { - if (isCompassSensorAvailable()) { - // Does nothing if the sensors already registered. - sensorManager.registerListener(this, compassSensor, SENSOR_DELAY_MICROS); - } else { - sensorManager.registerListener(this, gravitySensor, SENSOR_DELAY_MICROS); - sensorManager.registerListener(this, magneticFieldSensor, SENSOR_DELAY_MICROS); - } - } - - private void unregisterSensorListeners() { - if (isCompassSensorAvailable()) { - sensorManager.unregisterListener(this, compassSensor); - } else { - sensorManager.unregisterListener(this, gravitySensor); - sensorManager.unregisterListener(this, magneticFieldSensor); - } - } - - private boolean isCompassSensorAvailable() { - return compassSensor != null; - } - - /** - * Helper function, that filters newValues, considering previous values - * - * @param newValues array of float, that contains new data - * @param smoothedValues array of float, that contains previous state - * @return float filtered array of float - */ - private float[] lowPassFilter(float[] newValues, float[] smoothedValues) { - if (smoothedValues == null) { - return newValues; - } - for (int i = 0; i < newValues.length; i++) { - smoothedValues[i] = smoothedValues[i] + ALPHA * (newValues[i] - smoothedValues[i]); - } - return smoothedValues; - } - - /** - * Pulls out the rotation vector from a SensorEvent, with a maximum length - * vector of four elements to avoid potential compatibility issues. - * - * @param event the sensor event - * @return the events rotation vector, potentially truncated - */ - @NonNull - private float[] getRotationVectorFromSensorEvent(@NonNull SensorEvent event) { - if (event.values.length > 4) { - // On some Samsung devices SensorManager.getRotationMatrixFromVector - // appears to throw an exception if rotation vector has length > 4. - // For the purposes of this class the first 4 values of the - // rotation vector are sufficient (see crbug.com/335298 for details). - // Only affects Android 4.3 - System.arraycopy(event.values, 0, truncatedRotationVectorValue, 0, 4); - return truncatedRotationVectorValue; - } else { - return event.values; - } - } -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerConstants.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerConstants.java deleted file mode 100644 index 6e5f9bf4c6..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerConstants.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -/** - * Contains all the constants being used for the Location layer. - */ -final class LocationLayerConstants { - - static final String STATE_LOCATION_ENABLED = "mapbox_location_locationEnabled"; - static final String STATE_LOCATION_OPTIONS = "mapbox_location_options"; - static final String STATE_LOCATION_LAST_LOCATION = "mapbox_location_lastLocation"; - static final String STATE_LOCATION_RENDER_MODE = "mapbox_location_renderMode"; - static final String STATE_LOCATION_CAMERA_MODE = "mapbox_location_cameraMode"; - - // Controls the compass update rate in milliseconds - static final int COMPASS_UPDATE_RATE_MS = 500; - - // Sets the transition animation duration when switching camera modes. - static final long TRANSITION_ANIMATION_DURATION_MS = 750; - - // Sets the max allowed time for the location icon animation from one LatLng to another. - static final long MAX_ANIMATION_DURATION_MS = 2000; - - // Sets the duration of change of accuracy radius when a different value is provided. - static final long ACCURACY_RADIUS_ANIMATION_DURATION = 250; - - // Default animation duration for zooming while tracking. - static final long DEFAULT_TRACKING_ZOOM_ANIM_DURATION = 750; - - // Default animation duration for tilting while tracking. - static final long DEFAULT_TRACKING_TILT_ANIM_DURATION = 1250; - - // Sources - static final String LOCATION_SOURCE = "mapbox-location-source"; - static final String PROPERTY_GPS_BEARING = "mapbox-property-gps-bearing"; - static final String PROPERTY_COMPASS_BEARING = "mapbox-property-compass-bearing"; - static final String PROPERTY_LOCATION_STALE = "mapbox-property-location-stale"; - static final String PROPERTY_ACCURACY_RADIUS = "mapbox-property-accuracy-radius"; - static final String PROPERTY_ACCURACY_COLOR = "mapbox-property-accuracy-color"; - static final String PROPERTY_ACCURACY_ALPHA = "mapbox-property-accuracy-alpha"; - static final String PROPERTY_FOREGROUND_ICON_OFFSET = "mapbox-property-foreground-icon-offset"; - static final String PROPERTY_SHADOW_ICON_OFFSET = "mapbox-property-shadow-icon-offset"; - static final String PROPERTY_FOREGROUND_ICON = "mapbox-property-foreground-icon"; - static final String PROPERTY_BACKGROUND_ICON = "mapbox-property-background-icon"; - static final String PROPERTY_FOREGROUND_STALE_ICON = "mapbox-property-foreground-stale-icon"; - static final String PROPERTY_BACKGROUND_STALE_ICON = "mapbox-property-background-stale-icon"; - static final String PROPERTY_BEARING_ICON = "mapbox-property-shadow-icon"; - - // Layers - static final String SHADOW_LAYER = "mapbox-location-shadow"; - static final String FOREGROUND_LAYER = "mapbox-location-layer"; - static final String BACKGROUND_LAYER = "mapbox-location-stroke-layer"; - static final String ACCURACY_LAYER = "mapbox-location-accuracy-layer"; - static final String BEARING_LAYER = "mapbox-location-bearing-layer"; - - // Icons - static final String FOREGROUND_ICON = "mapbox-location-icon"; - static final String BACKGROUND_ICON = "mapbox-location-stroke-icon"; - static final String FOREGROUND_STALE_ICON = "mapbox-location-stale-icon"; - static final String BACKGROUND_STALE_ICON = "mapbox-location-background-stale-icon"; - static final String SHADOW_ICON = "mapbox-location-shadow-icon"; - static final String BEARING_ICON = "mapbox-location-bearing-icon"; - - private LocationLayerConstants() { - // Class should not be initialized - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerOptions.java deleted file mode 100644 index 204cb88013..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerOptions.java +++ /dev/null @@ -1,1561 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Bitmap; -import android.os.Parcel; -import android.os.Parcelable; -import android.support.annotation.ColorInt; -import android.support.annotation.Dimension; -import android.support.annotation.DrawableRes; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.StyleRes; - -import com.mapbox.mapboxsdk.R; -import com.mapbox.mapboxsdk.constants.MapboxConstants; - -import java.util.Arrays; - -/** - * This class exposes options for the Location Layer Plugin. The options can be set by defining a - * style in your apps style.xml file and passing in directly into the {@link LocationLayerPlugin} - * class. Alternatively, if properties need to be changed at runtime depending on a specific state, - * you can build an instance of this class, setting the values you desire, and then passing it into - * either the {@link LocationLayerPlugin} constructor (if it isn't initialized yet) or - * {@link LocationLayerPlugin#applyStyle(LocationLayerOptions)}. - *

- * When the {@link #createFromAttributes(Context, int)} methods called, any attributes not found - * inside the style will revert back to using their default set values. Likewise, when building a - * new {@link LocationLayerOptions} class using the builder, any options neglecting to be set will - * reset to their default values. - *

- * If you would like to keep your custom style changes while modifying a single attribute, you can - * get the currently used options object using {@link LocationLayerPlugin#getLocationLayerOptions()} - * and it's {@code toBuilder} method to modify a single entry while also maintaining the other - * settings. Once your modifications have been made, you'll need to pass it back into the location - * layer plugin using {@link LocationLayerPlugin#applyStyle(LocationLayerOptions)}. - */ -public class LocationLayerOptions implements Parcelable { - - /** - * Default accuracy alpha - */ - private static final float ACCURACY_ALPHA_DEFAULT = 0.15f; - - /** - * Default max map zoom - */ - private static final float MAX_ZOOM_DEFAULT = 18; - - /** - * Default min map zoom - */ - private static final float MIN_ZOOM_DEFAULT = 2; - - /** - * Default icon scale factor when the map is zoomed out - */ - private static final float MIN_ZOOM_ICON_SCALE_DEFAULT = 0.6f; - - /** - * Default icon scale factor when the map is zoomed in - */ - private static final float MAX_ZOOM_ICON_SCALE_DEFAULT = 1f; - - /** - * Default map padding - */ - private static final int[] PADDING_DEFAULT = {0, 0, 0, 0}; - - /** - * The default value which is used when the stale state is enabled - */ - private static final long STALE_STATE_DELAY_MS = 30000; - - private float accuracyAlpha; - private int accuracyColor; - private int backgroundDrawableStale; - private String backgroundStaleName; - private int foregroundDrawableStale; - private String foregroundStaleName; - private int gpsDrawable; - private String gpsName; - private int foregroundDrawable; - private String foregroundName; - private int backgroundDrawable; - private String backgroundName; - private int bearingDrawable; - private String bearingName; - private Integer bearingTintColor; - private Integer foregroundTintColor; - private Integer backgroundTintColor; - private Integer foregroundStaleTintColor; - private Integer backgroundStaleTintColor; - private float elevation; - private boolean enableStaleState; - private long staleStateTimeout; - private int[] padding; - private double maxZoom; - private double minZoom; - private float maxZoomIconScale; - private float minZoomIconScale; - private float trackingInitialMoveThreshold; - private float trackingMultiFingerMoveThreshold; - private String layerBelow; - - public LocationLayerOptions( - float accuracyAlpha, - int accuracyColor, - int backgroundDrawableStale, - @Nullable String backgroundStaleName, - int foregroundDrawableStale, - @Nullable String foregroundStaleName, - int gpsDrawable, - @Nullable String gpsName, - int foregroundDrawable, - @Nullable String foregroundName, - int backgroundDrawable, - @Nullable String backgroundName, - int bearingDrawable, - @Nullable String bearingName, - @Nullable Integer bearingTintColor, - @Nullable Integer foregroundTintColor, - @Nullable Integer backgroundTintColor, - @Nullable Integer foregroundStaleTintColor, - @Nullable Integer backgroundStaleTintColor, - float elevation, - boolean enableStaleState, - long staleStateTimeout, - int[] padding, - double maxZoom, - double minZoom, - float maxZoomIconScale, - float minZoomIconScale, - float trackingInitialMoveThreshold, - float trackingMultiFingerMoveThreshold, - String layerBelow) { - this.accuracyAlpha = accuracyAlpha; - this.accuracyColor = accuracyColor; - this.backgroundDrawableStale = backgroundDrawableStale; - this.backgroundStaleName = backgroundStaleName; - this.foregroundDrawableStale = foregroundDrawableStale; - this.foregroundStaleName = foregroundStaleName; - this.gpsDrawable = gpsDrawable; - this.gpsName = gpsName; - this.foregroundDrawable = foregroundDrawable; - this.foregroundName = foregroundName; - this.backgroundDrawable = backgroundDrawable; - this.backgroundName = backgroundName; - this.bearingDrawable = bearingDrawable; - this.bearingName = bearingName; - this.bearingTintColor = bearingTintColor; - this.foregroundTintColor = foregroundTintColor; - this.backgroundTintColor = backgroundTintColor; - this.foregroundStaleTintColor = foregroundStaleTintColor; - this.backgroundStaleTintColor = backgroundStaleTintColor; - this.elevation = elevation; - this.enableStaleState = enableStaleState; - this.staleStateTimeout = staleStateTimeout; - if (padding == null) { - throw new NullPointerException("Null padding"); - } - this.padding = padding; - this.maxZoom = maxZoom; - this.minZoom = minZoom; - this.maxZoomIconScale = maxZoomIconScale; - this.minZoomIconScale = minZoomIconScale; - this.trackingInitialMoveThreshold = trackingInitialMoveThreshold; - this.trackingMultiFingerMoveThreshold = trackingMultiFingerMoveThreshold; - this.layerBelow = layerBelow; - } - - /** - * Construct a new Location Layer Options class using the attributes found within a style - * resource. It's important to note that you only need to define the attributes you plan to - * change and can safely ignore the other attributes which will be set to their default value. - * - * @param context your activity's context used for acquiring resources - * @param styleRes the style id where your custom attributes are defined - * @return a new {@link LocationLayerOptions} object with the settings you defined in your style - * resource - */ - public static LocationLayerOptions createFromAttributes(@NonNull Context context, - @StyleRes int styleRes) { - - TypedArray typedArray = context.obtainStyledAttributes( - styleRes, R.styleable.mapbox_LocationLayer); - - LocationLayerOptions.Builder builder = new LocationLayerOptions.Builder() - .enableStaleState(true) - .staleStateTimeout(STALE_STATE_DELAY_MS) - .maxZoom(MAX_ZOOM_DEFAULT) - .minZoom(MIN_ZOOM_DEFAULT) - .maxZoomIconScale(MAX_ZOOM_ICON_SCALE_DEFAULT) - .minZoomIconScale(MIN_ZOOM_ICON_SCALE_DEFAULT) - .padding(PADDING_DEFAULT); - - builder.foregroundDrawable(typedArray.getResourceId( - R.styleable.mapbox_LocationLayer_mapbox_foregroundDrawable, -1)); - if (typedArray.hasValue(R.styleable.mapbox_LocationLayer_mapbox_foregroundTintColor)) { - builder.foregroundTintColor(typedArray.getColor( - R.styleable.mapbox_LocationLayer_mapbox_foregroundTintColor, -1)); - } - builder.backgroundDrawable(typedArray.getResourceId( - R.styleable.mapbox_LocationLayer_mapbox_backgroundDrawable, -1)); - if (typedArray.hasValue(R.styleable.mapbox_LocationLayer_mapbox_backgroundTintColor)) { - builder.backgroundTintColor(typedArray.getColor( - R.styleable.mapbox_LocationLayer_mapbox_backgroundTintColor, -1)); - } - builder.foregroundDrawableStale(typedArray.getResourceId( - R.styleable.mapbox_LocationLayer_mapbox_foregroundDrawableStale, -1)); - if (typedArray.hasValue(R.styleable.mapbox_LocationLayer_mapbox_foregroundStaleTintColor)) { - builder.foregroundStaleTintColor(typedArray.getColor( - R.styleable.mapbox_LocationLayer_mapbox_foregroundStaleTintColor, -1)); - } - builder.backgroundDrawableStale(typedArray.getResourceId( - R.styleable.mapbox_LocationLayer_mapbox_backgroundDrawableStale, -1)); - if (typedArray.hasValue(R.styleable.mapbox_LocationLayer_mapbox_backgroundStaleTintColor)) { - builder.backgroundStaleTintColor(typedArray.getColor( - R.styleable.mapbox_LocationLayer_mapbox_backgroundStaleTintColor, -1)); - } - builder.bearingDrawable(typedArray.getResourceId( - R.styleable.mapbox_LocationLayer_mapbox_bearingDrawable, -1)); - if (typedArray.hasValue(R.styleable.mapbox_LocationLayer_mapbox_bearingTintColor)) { - builder.bearingTintColor(typedArray.getColor( - R.styleable.mapbox_LocationLayer_mapbox_bearingTintColor, -1)); - } - if (typedArray.hasValue(R.styleable.mapbox_LocationLayer_mapbox_enableStaleState)) { - builder.enableStaleState(typedArray.getBoolean( - R.styleable.mapbox_LocationLayer_mapbox_enableStaleState, true)); - } - if (typedArray.hasValue(R.styleable.mapbox_LocationLayer_mapbox_staleStateTimeout)) { - builder.staleStateTimeout(typedArray.getInteger( - R.styleable.mapbox_LocationLayer_mapbox_staleStateTimeout, (int) STALE_STATE_DELAY_MS)); - } - builder.gpsDrawable(typedArray.getResourceId( - R.styleable.mapbox_LocationLayer_mapbox_gpsDrawable, -1)); - float elevation = typedArray.getDimension( - R.styleable.mapbox_LocationLayer_mapbox_elevation, 0); - builder.accuracyColor(typedArray.getColor( - R.styleable.mapbox_LocationLayer_mapbox_accuracyColor, -1)); - builder.accuracyAlpha(typedArray.getFloat( - R.styleable.mapbox_LocationLayer_mapbox_accuracyAlpha, ACCURACY_ALPHA_DEFAULT)); - builder.elevation(elevation); - - builder.trackingInitialMoveThreshold(typedArray.getDimension( - R.styleable.mapbox_LocationLayer_mapbox_trackingInitialMoveThreshold, - context.getResources().getDimension(R.dimen.mapbox_locationLayerTrackingInitialMoveThreshold))); - - builder.trackingMultiFingerMoveThreshold(typedArray.getDimension( - R.styleable.mapbox_LocationLayer_mapbox_trackingMultiFingerMoveThreshold, - context.getResources().getDimension(R.dimen.mapbox_locationLayerTrackingMultiFingerMoveThreshold))); - - builder.padding(new int[] { - typedArray.getInt(R.styleable.mapbox_LocationLayer_mapbox_iconPaddingLeft, 0), - typedArray.getInt(R.styleable.mapbox_LocationLayer_mapbox_iconPaddingTop, 0), - typedArray.getInt(R.styleable.mapbox_LocationLayer_mapbox_iconPaddingRight, 0), - typedArray.getInt(R.styleable.mapbox_LocationLayer_mapbox_iconPaddingBottom, 0), - }); - - float maxZoom - = typedArray.getFloat(R.styleable.mapbox_LocationLayer_mapbox_maxZoom, MAX_ZOOM_DEFAULT); - if (maxZoom < MapboxConstants.MINIMUM_ZOOM || maxZoom > MapboxConstants.MAXIMUM_ZOOM) { - throw new IllegalArgumentException("Max zoom value must be within " - + MapboxConstants.MINIMUM_ZOOM + " and " + MapboxConstants.MAXIMUM_ZOOM); - } - - float minZoom - = typedArray.getFloat(R.styleable.mapbox_LocationLayer_mapbox_minZoom, MIN_ZOOM_DEFAULT); - if (minZoom < MapboxConstants.MINIMUM_ZOOM || minZoom > MapboxConstants.MAXIMUM_ZOOM) { - throw new IllegalArgumentException("Min zoom value must be within " - + MapboxConstants.MINIMUM_ZOOM + " and " + MapboxConstants.MAXIMUM_ZOOM); - } - - builder.maxZoom(maxZoom); - builder.minZoom(minZoom); - - builder.layerBelow( - typedArray.getString(R.styleable.mapbox_LocationLayer_mapbox_layer_below)); - - float minScale = typedArray.getFloat( - R.styleable.mapbox_LocationLayer_mapbox_minZoomIconScale, MIN_ZOOM_ICON_SCALE_DEFAULT); - float maxScale = typedArray.getFloat( - R.styleable.mapbox_LocationLayer_mapbox_maxZoomIconScale, MAX_ZOOM_ICON_SCALE_DEFAULT); - builder.minZoomIconScale(minScale); - builder.maxZoomIconScale(maxScale); - - typedArray.recycle(); - - return builder.build(); - } - - /** - * Takes the currently constructed {@link LocationLayerOptions} object and provides it's builder - * with all the values set matching the values in this instance. This allows you to modify a - * single attribute and then rebuild the object. - * - * @return the Location Layer builder which contains the values defined in this current instance - * as defaults. - */ - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Build a new instance of the {@link LocationLayerOptions} class with all the attributes set - * automatically to their defined defaults in this library. This allows you to adjust a few - * attributes while leaving the rest alone and maintaining their default behavior. - * - * @param context your activities context used to acquire the style resource - * @return the Location Layer builder which contains the default values defined by the style - * resource - */ - public static Builder builder(Context context) { - return LocationLayerOptions.createFromAttributes(context, - R.style.mapbox_LocationLayer).toBuilder(); - } - - /** - * Set the opacity of the accuracy view to a value from 0 to 1, where 0 means the accuracy view is - * completely transparent and 1 means the view is completely opaque. - * - * @return the opacity of the accuracy view - * @attr ref R.styleable#LocationLayer_accuracyAlpha - */ - public float accuracyAlpha() { - return accuracyAlpha; - } - - /** - * Solid color to use as the accuracy view color property. - * - * @return the color of the accuracy view - * @attr ref R.styleable#LocationLayer_accuracyColor - */ - @ColorInt - public int accuracyColor() { - return accuracyColor; - } - - /** - * Defines the drawable used for the stale background icon. - * - * @return the drawable resource ID - * @attr ref R.styleable#LocationLayer_backgroundDrawableStale - */ - @DrawableRes - public int backgroundDrawableStale() { - return backgroundDrawableStale; - } - - /** - * String image name, identical to one used in - * the first parameter of {@link com.mapbox.mapboxsdk.maps.MapboxMap#addImage(String, Bitmap)}, the - * plugin, will used this image in place of the provided or default mapbox_foregroundDrawableStale. - *

- * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded - * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. - *

- * - * @return String icon or maki-icon name - */ - @Nullable - public String backgroundStaleName() { - return backgroundStaleName; - } - - /** - * Defines the drawable used for the stale foreground icon. - * - * @return the drawable resource ID - * @attr ref R.styleable#LocationLayer_foregroundDrawableStale - */ - @DrawableRes - public int foregroundDrawableStale() { - return foregroundDrawableStale; - } - - /** - * String image name, identical to one used in - * the first parameter of {@link com.mapbox.mapboxsdk.maps.MapboxMap#addImage(String, Bitmap)}, the - * plugin, will used this image in place of the provided or default mapbox_foregroundDrawableStale. - *

- * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded - * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. - *

- * - * @return String icon or maki-icon name - */ - @Nullable - public String foregroundStaleName() { - return foregroundStaleName; - } - - /** - * Defines the drawable used for the navigation state icon. - * - * @return the drawable resource ID - * @attr ref R.styleable#LocationLayer_gpsDrawable - */ - @DrawableRes - public int gpsDrawable() { - return gpsDrawable; - } - - /** - * String image name, identical to one used in - * the first parameter of {@link com.mapbox.mapboxsdk.maps.MapboxMap#addImage(String, Bitmap)}, the - * plugin, will used this image in place of the provided or default mapbox_gpsDrawable. - *

- * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded - * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. - *

- * - * @return String icon or maki-icon name - */ - @Nullable - public String gpsName() { - return gpsName; - } - - /** - * Supply a Drawable that is to be rendered on top of all of the content in the Location Layer - * Plugin layer stack. - * - * @return the drawable resource used for the foreground layer - * @attr ref R.styleable#LocationLayer_foregroundDrawable - */ - @DrawableRes - public int foregroundDrawable() { - return foregroundDrawable; - } - - /** - * String image name, identical to one used in - * the first parameter of {@link com.mapbox.mapboxsdk.maps.MapboxMap#addImage(String, Bitmap)}, the - * plugin, will used this image in place of the provided or default mapbox_foregroundDrawable. - *

- * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded - * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. - *

- * - * @return String icon or maki-icon name - */ - @Nullable - public String foregroundName() { - return foregroundName; - } - - /** - * Defines the drawable used for the background state icon. - * - * @return the drawable resource ID - * @attr ref R.styleable#LocationLayer_backgroundDrawable - */ - @DrawableRes - public int backgroundDrawable() { - return backgroundDrawable; - } - - /** - * String image name, identical to one used in - * the first parameter of {@link com.mapbox.mapboxsdk.maps.MapboxMap#addImage(String, Bitmap)}, the - * plugin, will used this image in place of the provided or default mapbox_backgroundDrawable. - *

- * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded - * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. - *

- * - * @return String icon or maki-icon name - */ - @Nullable - public String backgroundName() { - return backgroundName; - } - - /** - * Defines the drawable used for the bearing icon. - * - * @return the drawable resource ID - * @attr ref R.styleable#LocationLayer_bearingDrawable - */ - @DrawableRes - public int bearingDrawable() { - return bearingDrawable; - } - - /** - * String image name, identical to one used in - * the first parameter of {@link com.mapbox.mapboxsdk.maps.MapboxMap#addImage(String, Bitmap)}, the - * plugin, will used this image in place of the provided or default mapbox_bearingDrawable. - *

- * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded - * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. - *

- * - * @return String icon or maki-icon name - */ - @Nullable - public String bearingName() { - return bearingName; - } - - /** - * Defines the bearing icon color as an integer. - * - * @return the color integer resource - * @attr ref R.styleable#LocationLayer_bearingTintColor - */ - @ColorInt - @Nullable - public Integer bearingTintColor() { - return bearingTintColor; - } - - /** - * Defines the foreground color as an integer. - * - * @return the color integer resource - * @attr ref R.styleable#LocationLayer_foregroundTintColor - */ - @ColorInt - @Nullable - public Integer foregroundTintColor() { - return foregroundTintColor; - } - - /** - * Defines the background color as an integer. - * - * @return the color integer resource - * @attr ref R.styleable#LocationLayer_backgroundTintColor - */ - @ColorInt - @Nullable - public Integer backgroundTintColor() { - return backgroundTintColor; - } - - /** - * Defines the foreground stale color as an integer. - * - * @return the color integer resource - * @attr ref R.styleable#LocationLayer_foregroundStaleTintColor - */ - @ColorInt - @Nullable - public Integer foregroundStaleTintColor() { - return foregroundStaleTintColor; - } - - /** - * Defines the background stale color as an integer. - * - * @return the color integer resource - * @attr ref R.styleable#LocationLayer_backgroundStaleTintColor - */ - @ColorInt - @Nullable - public Integer backgroundStaleTintColor() { - return backgroundStaleTintColor; - } - - /** - * Sets the base elevation of this view, in pixels. - * - * @return the elevation currently set for the location layer icon - * @attr ref R.styleable#LocationLayer_elevation - */ - @Dimension - public float elevation() { - return elevation; - } - - /** - * Enable or disable to stale state mode. This mode indicates to the user that the location being - * displayed on the map hasn't been updated in a specific amount of time. - * - * @return whether the stale state mode is enabled or not - * @attr ref R.styleable#LocationLayer_enableStaleState - */ - public boolean enableStaleState() { - return enableStaleState; - } - - /** - * Set the delay before the location icon becomes stale. The timer begins approximately when a new - * location update comes in and using this defined time, if an update hasn't occured by the end, - * the location is considered stale. - * - * @return the duration in milliseconds which it should take before the location layer is - * considered stale - * @attr ref R.styleable#LocationLayer_staleStateDelay - */ - public long staleStateTimeout() { - return staleStateTimeout; - } - - /** - * Sets the distance from the edges of the map view’s frame to the edges of the map - * view’s logical viewport. - *

- *

- * When the value of this property is equal to {0,0,0,0}, viewport - * properties such as `centerCoordinate` assume a viewport that matches the map - * view’s frame. Otherwise, those properties are inset, excluding part of the - * frame from the viewport. For instance, if the only the top edge is inset, the - * map center is effectively shifted downward. - *

- * - * @return integer array of padding values - */ - @SuppressWarnings("mutable") - public int[] padding() { - return padding; - } - - /** - * The maximum zoom level the map can be displayed at. - * - * @return the maximum zoom level - */ - public double maxZoom() { - return maxZoom; - } - - /** - * The minimum zoom level the map can be displayed at. - * - * @return the minimum zoom level - */ - public double minZoom() { - return minZoom; - } - - /** - * The scale factor of the location icon when the map is zoomed in. Based on {@link #maxZoom()}. - * Scaling is linear. - * - * @return icon scale factor - */ - public float maxZoomIconScale() { - return maxZoomIconScale; - } - - /** - * The scale factor of the location icon when the map is zoomed out. Based on {@link #minZoom()}. - * Scaling is linear. - * - * @return icon scale factor - */ - public float minZoomIconScale() { - return minZoomIconScale; - } - - /** - * Minimum single pointer movement in pixels required to break camera tracking. - * - * @return the minimum movement - */ - public float trackingInitialMoveThreshold() { - return trackingInitialMoveThreshold; - } - - /** - * Minimum multi pointer movement in pixels required to break camera tracking (for example during scale gesture). - * - * @return the minimum movement - */ - public float trackingMultiFingerMoveThreshold() { - return trackingMultiFingerMoveThreshold; - } - - /** - * Gets the id of the layer to add the location layer above to. - * - * @return layerBelow the id of the layer to add the location layer above to - */ - public String layerBelow() { - return layerBelow; - } - - @Override - public String toString() { - return "LocationLayerOptions{" - + "accuracyAlpha=" + accuracyAlpha + ", " - + "accuracyColor=" + accuracyColor + ", " - + "backgroundDrawableStale=" + backgroundDrawableStale + ", " - + "backgroundStaleName=" + backgroundStaleName + ", " - + "foregroundDrawableStale=" + foregroundDrawableStale + ", " - + "foregroundStaleName=" + foregroundStaleName + ", " - + "gpsDrawable=" + gpsDrawable + ", " - + "gpsName=" + gpsName + ", " - + "foregroundDrawable=" + foregroundDrawable + ", " - + "foregroundName=" + foregroundName + ", " - + "backgroundDrawable=" + backgroundDrawable + ", " - + "backgroundName=" + backgroundName + ", " - + "bearingDrawable=" + bearingDrawable + ", " - + "bearingName=" + bearingName + ", " - + "bearingTintColor=" + bearingTintColor + ", " - + "foregroundTintColor=" + foregroundTintColor + ", " - + "backgroundTintColor=" + backgroundTintColor + ", " - + "foregroundStaleTintColor=" + foregroundStaleTintColor + ", " - + "backgroundStaleTintColor=" + backgroundStaleTintColor + ", " - + "elevation=" + elevation + ", " - + "enableStaleState=" + enableStaleState + ", " - + "staleStateTimeout=" + staleStateTimeout + ", " - + "padding=" + Arrays.toString(padding) + ", " - + "maxZoom=" + maxZoom + ", " - + "minZoom=" + minZoom + ", " - + "maxZoomIconScale=" + maxZoomIconScale + ", " - + "minZoomIconScale=" + minZoomIconScale + ", " - + "trackingInitialMoveThreshold=" + trackingInitialMoveThreshold + ", " - + "trackingMultiFingerMoveThreshold=" + trackingMultiFingerMoveThreshold + ", " - + "layerBelow=" + layerBelow - + "}"; - } - - @Override - public boolean equals(Object o) { - if (o == this) { - return true; - } - if (o instanceof LocationLayerOptions) { - LocationLayerOptions that = (LocationLayerOptions) o; - return (Float.floatToIntBits(this.accuracyAlpha) == Float.floatToIntBits(that.accuracyAlpha())) - && (this.accuracyColor == that.accuracyColor()) - && (this.backgroundDrawableStale == that.backgroundDrawableStale()) - && ((this.backgroundStaleName == null) ? (that.backgroundStaleName() == null) - : this.backgroundStaleName.equals(that.backgroundStaleName())) - && (this.foregroundDrawableStale == that.foregroundDrawableStale()) - && ((this.foregroundStaleName == null) ? (that.foregroundStaleName() == null) - : this.foregroundStaleName.equals(that.foregroundStaleName())) - && (this.gpsDrawable == that.gpsDrawable()) - && ((this.gpsName == null) ? (that.gpsName() == null) : this.gpsName.equals(that.gpsName())) - && (this.foregroundDrawable == that.foregroundDrawable()) - && ((this.foregroundName == null) ? (that.foregroundName() == null) - : this.foregroundName.equals(that.foregroundName())) - && (this.backgroundDrawable == that.backgroundDrawable()) - && ((this.backgroundName == null) ? (that.backgroundName() == null) - : this.backgroundName.equals(that.backgroundName())) - && (this.bearingDrawable == that.bearingDrawable()) - && ((this.bearingName == null) ? (that.bearingName() == null) - : this.bearingName.equals(that.bearingName())) - && ((this.bearingTintColor == null) ? (that.bearingTintColor() == null) - : this.bearingTintColor.equals(that.bearingTintColor())) - && ((this.foregroundTintColor == null) ? (that.foregroundTintColor() == null) - : this.foregroundTintColor.equals(that.foregroundTintColor())) - && ((this.backgroundTintColor == null) ? (that.backgroundTintColor() == null) - : this.backgroundTintColor.equals(that.backgroundTintColor())) - && ((this.foregroundStaleTintColor == null) ? (that.foregroundStaleTintColor() == null) - : this.foregroundStaleTintColor.equals(that.foregroundStaleTintColor())) - && ((this.backgroundStaleTintColor == null) ? (that.backgroundStaleTintColor() == null) - : this.backgroundStaleTintColor.equals(that.backgroundStaleTintColor())) - && (Float.floatToIntBits(this.elevation) == Float.floatToIntBits(that.elevation())) - && (this.enableStaleState == that.enableStaleState()) - && (this.staleStateTimeout == that.staleStateTimeout()) - && (Arrays.equals(this.padding, that.padding()) - && (Double.doubleToLongBits(this.maxZoom) == Double.doubleToLongBits(that.maxZoom())) - && (Double.doubleToLongBits(this.minZoom) == Double.doubleToLongBits(that.minZoom())) - && (Float.floatToIntBits(this.maxZoomIconScale) == Float.floatToIntBits(that.maxZoomIconScale())) - && (Float.floatToIntBits(this.minZoomIconScale) == Float.floatToIntBits(that.minZoomIconScale())) - && (Float.floatToIntBits(this.trackingInitialMoveThreshold) - == Float.floatToIntBits(that.trackingInitialMoveThreshold())) - && (Float.floatToIntBits(this.trackingMultiFingerMoveThreshold) - == Float.floatToIntBits(that.trackingMultiFingerMoveThreshold())) - && layerBelow.equals(that.layerBelow)); - } - return false; - } - - @Override - public int hashCode() { - int h$ = 1; - h$ *= 1000003; - h$ ^= Float.floatToIntBits(accuracyAlpha); - h$ *= 1000003; - h$ ^= accuracyColor; - h$ *= 1000003; - h$ ^= backgroundDrawableStale; - h$ *= 1000003; - h$ ^= (backgroundStaleName == null) ? 0 : backgroundStaleName.hashCode(); - h$ *= 1000003; - h$ ^= foregroundDrawableStale; - h$ *= 1000003; - h$ ^= (foregroundStaleName == null) ? 0 : foregroundStaleName.hashCode(); - h$ *= 1000003; - h$ ^= gpsDrawable; - h$ *= 1000003; - h$ ^= (gpsName == null) ? 0 : gpsName.hashCode(); - h$ *= 1000003; - h$ ^= foregroundDrawable; - h$ *= 1000003; - h$ ^= (foregroundName == null) ? 0 : foregroundName.hashCode(); - h$ *= 1000003; - h$ ^= backgroundDrawable; - h$ *= 1000003; - h$ ^= (backgroundName == null) ? 0 : backgroundName.hashCode(); - h$ *= 1000003; - h$ ^= bearingDrawable; - h$ *= 1000003; - h$ ^= (bearingName == null) ? 0 : bearingName.hashCode(); - h$ *= 1000003; - h$ ^= (bearingTintColor == null) ? 0 : bearingTintColor.hashCode(); - h$ *= 1000003; - h$ ^= (foregroundTintColor == null) ? 0 : foregroundTintColor.hashCode(); - h$ *= 1000003; - h$ ^= (backgroundTintColor == null) ? 0 : backgroundTintColor.hashCode(); - h$ *= 1000003; - h$ ^= (foregroundStaleTintColor == null) ? 0 : foregroundStaleTintColor.hashCode(); - h$ *= 1000003; - h$ ^= (backgroundStaleTintColor == null) ? 0 : backgroundStaleTintColor.hashCode(); - h$ *= 1000003; - h$ ^= Float.floatToIntBits(elevation); - h$ *= 1000003; - h$ ^= enableStaleState ? 1231 : 1237; - h$ *= 1000003; - h$ ^= (int) ((staleStateTimeout >>> 32) ^ staleStateTimeout); - h$ *= 1000003; - h$ ^= Arrays.hashCode(padding); - h$ *= 1000003; - h$ ^= (int) ((Double.doubleToLongBits(maxZoom) >>> 32) ^ Double.doubleToLongBits(maxZoom)); - h$ *= 1000003; - h$ ^= (int) ((Double.doubleToLongBits(minZoom) >>> 32) ^ Double.doubleToLongBits(minZoom)); - h$ *= 1000003; - h$ ^= Float.floatToIntBits(maxZoomIconScale); - h$ *= 1000003; - h$ ^= Float.floatToIntBits(minZoomIconScale); - h$ *= 1000003; - h$ ^= Float.floatToIntBits(trackingInitialMoveThreshold); - h$ *= 1000003; - h$ ^= Float.floatToIntBits(trackingMultiFingerMoveThreshold); - return h$; - } - - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - @Override - public LocationLayerOptions createFromParcel(Parcel in) { - return new LocationLayerOptions( - in.readFloat(), - in.readInt(), - in.readInt(), - in.readInt() == 0 ? in.readString() : null, - in.readInt(), - in.readInt() == 0 ? in.readString() : null, - in.readInt(), - in.readInt() == 0 ? in.readString() : null, - in.readInt(), - in.readInt() == 0 ? in.readString() : null, - in.readInt(), - in.readInt() == 0 ? in.readString() : null, - in.readInt(), - in.readInt() == 0 ? in.readString() : null, - in.readInt() == 0 ? in.readInt() : null, - in.readInt() == 0 ? in.readInt() : null, - in.readInt() == 0 ? in.readInt() : null, - in.readInt() == 0 ? in.readInt() : null, - in.readInt() == 0 ? in.readInt() : null, - in.readFloat(), - in.readInt() == 1, - in.readLong(), - in.createIntArray(), - in.readDouble(), - in.readDouble(), - in.readFloat(), - in.readFloat(), - in.readFloat(), - in.readFloat(), - in.readString() - ); - } - - @Override - public LocationLayerOptions[] newArray(int size) { - return new LocationLayerOptions[size]; - } - }; - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeFloat(accuracyAlpha()); - dest.writeInt(accuracyColor()); - dest.writeInt(backgroundDrawableStale()); - if (backgroundStaleName() == null) { - dest.writeInt(1); - } else { - dest.writeInt(0); - dest.writeString(backgroundStaleName()); - } - dest.writeInt(foregroundDrawableStale()); - if (foregroundStaleName() == null) { - dest.writeInt(1); - } else { - dest.writeInt(0); - dest.writeString(foregroundStaleName()); - } - dest.writeInt(gpsDrawable()); - if (gpsName() == null) { - dest.writeInt(1); - } else { - dest.writeInt(0); - dest.writeString(gpsName()); - } - dest.writeInt(foregroundDrawable()); - if (foregroundName() == null) { - dest.writeInt(1); - } else { - dest.writeInt(0); - dest.writeString(foregroundName()); - } - dest.writeInt(backgroundDrawable()); - if (backgroundName() == null) { - dest.writeInt(1); - } else { - dest.writeInt(0); - dest.writeString(backgroundName()); - } - dest.writeInt(bearingDrawable()); - if (bearingName() == null) { - dest.writeInt(1); - } else { - dest.writeInt(0); - dest.writeString(bearingName()); - } - if (bearingTintColor() == null) { - dest.writeInt(1); - } else { - dest.writeInt(0); - dest.writeInt(bearingTintColor()); - } - if (foregroundTintColor() == null) { - dest.writeInt(1); - } else { - dest.writeInt(0); - dest.writeInt(foregroundTintColor()); - } - if (backgroundTintColor() == null) { - dest.writeInt(1); - } else { - dest.writeInt(0); - dest.writeInt(backgroundTintColor()); - } - if (foregroundStaleTintColor() == null) { - dest.writeInt(1); - } else { - dest.writeInt(0); - dest.writeInt(foregroundStaleTintColor()); - } - if (backgroundStaleTintColor() == null) { - dest.writeInt(1); - } else { - dest.writeInt(0); - dest.writeInt(backgroundStaleTintColor()); - } - dest.writeFloat(elevation()); - dest.writeInt(enableStaleState() ? 1 : 0); - dest.writeLong(staleStateTimeout()); - dest.writeIntArray(padding()); - dest.writeDouble(maxZoom()); - dest.writeDouble(minZoom()); - dest.writeFloat(maxZoomIconScale()); - dest.writeFloat(minZoomIconScale()); - dest.writeFloat(trackingInitialMoveThreshold()); - dest.writeFloat(trackingMultiFingerMoveThreshold()); - dest.writeString(layerBelow()); - } - - @Override - public int describeContents() { - return 0; - } - - /** - * Builder class for constructing a new instance of {@link LocationLayerOptions}. - */ - public static class Builder { - - /** - * Build a new instance of this {@link LocationLayerOptions} class. - * - * @return a new instance of {@link LocationLayerOptions} - */ - public LocationLayerOptions build() { - LocationLayerOptions locationLayerOptions = autoBuild(); - if (locationLayerOptions.accuracyAlpha() < 0 || locationLayerOptions.accuracyAlpha() > 1) { - throw new IllegalArgumentException( - "Location layer accuracy alpha value must be between 0.0 and 1.0."); - } - - if (locationLayerOptions.elevation() < 0f) { - throw new IllegalArgumentException("Invalid shadow size " - + locationLayerOptions.elevation() + ". Must be >= 0"); - } - - return locationLayerOptions; - } - - private Float accuracyAlpha; - private Integer accuracyColor; - private Integer backgroundDrawableStale; - private String backgroundStaleName; - private Integer foregroundDrawableStale; - private String foregroundStaleName; - private Integer gpsDrawable; - private String gpsName; - private Integer foregroundDrawable; - private String foregroundName; - private Integer backgroundDrawable; - private String backgroundName; - private Integer bearingDrawable; - private String bearingName; - private Integer bearingTintColor; - private Integer foregroundTintColor; - private Integer backgroundTintColor; - private Integer foregroundStaleTintColor; - private Integer backgroundStaleTintColor; - private Float elevation; - private Boolean enableStaleState; - private Long staleStateTimeout; - private int[] padding; - private Double maxZoom; - private Double minZoom; - private Float maxZoomIconScale; - private Float minZoomIconScale; - private Float trackingInitialMoveThreshold; - private Float trackingMultiFingerMoveThreshold; - private String layerBelow; - - Builder() { - } - - private Builder(LocationLayerOptions source) { - this.accuracyAlpha = source.accuracyAlpha(); - this.accuracyColor = source.accuracyColor(); - this.backgroundDrawableStale = source.backgroundDrawableStale(); - this.backgroundStaleName = source.backgroundStaleName(); - this.foregroundDrawableStale = source.foregroundDrawableStale(); - this.foregroundStaleName = source.foregroundStaleName(); - this.gpsDrawable = source.gpsDrawable(); - this.gpsName = source.gpsName(); - this.foregroundDrawable = source.foregroundDrawable(); - this.foregroundName = source.foregroundName(); - this.backgroundDrawable = source.backgroundDrawable(); - this.backgroundName = source.backgroundName(); - this.bearingDrawable = source.bearingDrawable(); - this.bearingName = source.bearingName(); - this.bearingTintColor = source.bearingTintColor(); - this.foregroundTintColor = source.foregroundTintColor(); - this.backgroundTintColor = source.backgroundTintColor(); - this.foregroundStaleTintColor = source.foregroundStaleTintColor(); - this.backgroundStaleTintColor = source.backgroundStaleTintColor(); - this.elevation = source.elevation(); - this.enableStaleState = source.enableStaleState(); - this.staleStateTimeout = source.staleStateTimeout(); - this.padding = source.padding(); - this.maxZoom = source.maxZoom(); - this.minZoom = source.minZoom(); - this.maxZoomIconScale = source.maxZoomIconScale(); - this.minZoomIconScale = source.minZoomIconScale(); - this.trackingInitialMoveThreshold = source.trackingInitialMoveThreshold(); - this.trackingMultiFingerMoveThreshold = source.trackingMultiFingerMoveThreshold(); - this.layerBelow = source.layerBelow(); - } - - /** - * Set the opacity of the accuracy view to a value from 0 to 1, where 0 means the accuracy view - * is completely transparent and 1 means the view is completely opaque. - * - * @param accuracyAlpha the opacity of the accuracy view - * @return this builder for chaining options together - * @attr ref R.styleable#LocationLayer_accuracyAlpha - */ - public LocationLayerOptions.Builder accuracyAlpha(float accuracyAlpha) { - this.accuracyAlpha = accuracyAlpha; - return this; - } - - /** - * Solid color to use as the accuracy view color property. - * - * @param accuracyColor the color of the accuracy view - * @return this builder for chaining options together - * @attr ref R.styleable#LocationLayer_accuracyColor - */ - public LocationLayerOptions.Builder accuracyColor(int accuracyColor) { - this.accuracyColor = accuracyColor; - return this; - } - - /** - * Defines the drawable used for the stale background icon. - * - * @param backgroundDrawableStale the drawable resource ID - * @return this builder for chaining options together - * @attr ref R.styleable#LocationLayer_backgroundDrawableStale - */ - public LocationLayerOptions.Builder backgroundDrawableStale(int backgroundDrawableStale) { - this.backgroundDrawableStale = backgroundDrawableStale; - return this; - } - - /** - * Given a String image name, identical to one used in - * the first parameter of {@link com.mapbox.mapboxsdk.maps.MapboxMap#addImage(String, Bitmap)}, the - * plugin, will used this image in place of the provided or default mapbox_backgroundDrawableStale. - *

- * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded - * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. - *

- * - * @param backgroundStaleName String icon or maki-icon name - * @return this builder for chaining options together - */ - public LocationLayerOptions.Builder backgroundStaleName(@Nullable String backgroundStaleName) { - this.backgroundStaleName = backgroundStaleName; - return this; - } - - /** - * Defines the drawable used for the stale foreground icon. - * - * @param foregroundDrawableStale the drawable resource ID - * @return this builder for chaining options together - * @attr ref R.styleable#LocationLayer_foregroundDrawableStale - */ - public LocationLayerOptions.Builder foregroundDrawableStale(int foregroundDrawableStale) { - this.foregroundDrawableStale = foregroundDrawableStale; - return this; - } - - /** - * Given a String image name, identical to one used in - * the first parameter of {@link com.mapbox.mapboxsdk.maps.MapboxMap#addImage(String, Bitmap)}, the - * plugin, will used this image in place of the provided or default mapbox_foregroundDrawableStale. - *

- * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded - * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. - *

- * - * @param foregroundStaleName String icon or maki-icon name - * @return this builder for chaining options together - */ - public LocationLayerOptions.Builder foregroundStaleName(@Nullable String foregroundStaleName) { - this.foregroundStaleName = foregroundStaleName; - return this; - } - - /** - * Defines the drawable used for the navigation state icon. - * - * @param gpsDrawable the drawable resource ID - * @return this builder for chaining options together - * @attr ref R.styleable#LocationLayer_gpsDrawable - */ - public LocationLayerOptions.Builder gpsDrawable(int gpsDrawable) { - this.gpsDrawable = gpsDrawable; - return this; - } - - /** - * Given a String image name, identical to one used in - * the first parameter of {@link com.mapbox.mapboxsdk.maps.MapboxMap#addImage(String, Bitmap)}, the - * plugin, will used this image in place of the provided or default mapbox_gpsDrawable. - *

- * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded - * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. - *

- * - * @param gpsName String icon or maki-icon name - * @return this builder for chaining options together - */ - public LocationLayerOptions.Builder gpsName(@Nullable String gpsName) { - this.gpsName = gpsName; - return this; - } - - /** - * Supply a Drawable that is to be rendered on top of all of the content in the Location Layer - * Plugin layer stack. - * - * @param foregroundDrawable the drawable resource used for the foreground layer - * @return this builder for chaining options together - * @attr ref R.styleable#LocationLayer_foregroundDrawable - */ - public LocationLayerOptions.Builder foregroundDrawable(int foregroundDrawable) { - this.foregroundDrawable = foregroundDrawable; - return this; - } - - /** - * Given a String image name, identical to one used in - * the first parameter of {@link com.mapbox.mapboxsdk.maps.MapboxMap#addImage(String, Bitmap)}, the - * plugin, will used this image in place of the provided or default mapbox_foregroundDrawable. - *

- * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded - * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. - *

- * - * @param foregroundName String icon or maki-icon name - * @return this builder for chaining options together - */ - public LocationLayerOptions.Builder foregroundName(@Nullable String foregroundName) { - this.foregroundName = foregroundName; - return this; - } - - /** - * Defines the drawable used for the background state icon. - * - * @param backgroundDrawable the drawable resource ID - * @return this builder for chaining options together - * @attr ref R.styleable#LocationLayer_backgroundDrawable - */ - public LocationLayerOptions.Builder backgroundDrawable(int backgroundDrawable) { - this.backgroundDrawable = backgroundDrawable; - return this; - } - - /** - * Given a String image name, identical to one used in - * the first parameter of {@link com.mapbox.mapboxsdk.maps.MapboxMap#addImage(String, Bitmap)}, the - * plugin, will used this image in place of the provided or default mapbox_backgroundDrawable. - *

- * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded - * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. - *

- * - * @param backgroundName String icon or maki-icon name - * @return this builder for chaining options together - */ - public LocationLayerOptions.Builder backgroundName(@Nullable String backgroundName) { - this.backgroundName = backgroundName; - return this; - } - - /** - * Defines the drawable used for the bearing icon. - * - * @param bearingDrawable the drawable resource ID - * @return this builder for chaining options together - * @attr ref R.styleable#LocationLayer_bearingDrawable - */ - public LocationLayerOptions.Builder bearingDrawable(int bearingDrawable) { - this.bearingDrawable = bearingDrawable; - return this; - } - - /** - * Given a String image name, identical to one used in - * the first parameter of {@link com.mapbox.mapboxsdk.maps.MapboxMap#addImage(String, Bitmap)}, the - * plugin, will used this image in place of the provided or default mapbox_bearingDrawable. - *

- * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded - * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. - *

- * - * @param bearingName String icon or maki-icon name - * @return this builder for chaining options together - */ - public LocationLayerOptions.Builder bearingName(@Nullable String bearingName) { - this.bearingName = bearingName; - return this; - } - - /** - * Defines the bearing icon color as an integer. - * - * @param bearingTintColor the color integer resource - * @return this builder for chaining options together - * @attr ref R.styleable#LocationLayer_bearingTintColor - */ - public LocationLayerOptions.Builder bearingTintColor(@Nullable Integer bearingTintColor) { - this.bearingTintColor = bearingTintColor; - return this; - } - - /** - * Defines the foreground color as an integer. - * - * @param foregroundTintColor the color integer resource - * @return this builder for chaining options together - * @attr ref R.styleable#LocationLayer_foregroundTintColor - */ - public LocationLayerOptions.Builder foregroundTintColor(@Nullable Integer foregroundTintColor) { - this.foregroundTintColor = foregroundTintColor; - return this; - } - - /** - * Defines the background color as an integer. - * - * @param backgroundTintColor the color integer resource - * @return this builder for chaining options together - * @attr ref R.styleable#LocationLayer_backgroundTintColor - */ - public LocationLayerOptions.Builder backgroundTintColor(@Nullable Integer backgroundTintColor) { - this.backgroundTintColor = backgroundTintColor; - return this; - } - - /** - * Defines the foreground stale color as an integer. - * - * @param foregroundStaleTintColor the color integer resource - * @return this builder for chaining options together - * @attr ref R.styleable#LocationLayer_foregroundStaleTintColor - */ - public LocationLayerOptions.Builder foregroundStaleTintColor(@Nullable Integer foregroundStaleTintColor) { - this.foregroundStaleTintColor = foregroundStaleTintColor; - return this; - } - - /** - * Defines the background stale color as an integer. - * - * @param backgroundStaleTintColor the color integer resource - * @return this builder for chaining options together - * @attr ref R.styleable#LocationLayer_backgroundStaleTintColor - */ - public LocationLayerOptions.Builder backgroundStaleTintColor(@Nullable Integer backgroundStaleTintColor) { - this.backgroundStaleTintColor = backgroundStaleTintColor; - return this; - } - - /** - * Sets the base elevation of this view, in pixels. - * - * @param elevation the elevation currently set for the location layer icon - * @return this builder for chaining options together - * @attr ref R.styleable#LocationLayer_elevation - */ - public LocationLayerOptions.Builder elevation(float elevation) { - this.elevation = elevation; - return this; - } - - /** - * Enable or disable to stale state mode. This mode indicates to the user that the location - * being displayed on the map hasn't been updated in a specific amount of time. - * - * @param enabled whether the stale state mode is enabled or not - * @return this builder for chaining options together - * @attr ref R.styleable#LocationLayer_enableStaleState - */ - public LocationLayerOptions.Builder enableStaleState(boolean enabled) { - this.enableStaleState = enabled; - return this; - } - - /** - * Set the timeout before the location icon becomes stale. The timer begins approximately when a - * new location update comes in and using this defined time, if an update hasn't occurred by the - * end, the location is considered stale. - * - * @param timeout the duration in milliseconds which it should take before the location layer is - * considered stale - * @return this builder for chaining options together - * @attr ref R.styleable#LocationLayer_staleStateTimeout - */ - public LocationLayerOptions.Builder staleStateTimeout(long timeout) { - this.staleStateTimeout = timeout; - return this; - } - - /** - * Sets the distance from the edges of the map view’s frame to the edges of the map - * view’s logical viewport. - *

- *

- * When the value of this property is equal to {0,0,0,0}, viewport - * properties such as `centerCoordinate` assume a viewport that matches the map - * view’s frame. Otherwise, those properties are inset, excluding part of the - * frame from the viewport. For instance, if the only the top edge is inset, the - * map center is effectively shifted downward. - *

- * - * @param padding The margins for the map in pixels (left, top, right, bottom). - */ - public LocationLayerOptions.Builder padding(int[] padding) { - if (padding == null) { - throw new NullPointerException("Null padding"); - } - this.padding = padding; - return this; - } - - /** - * Sets the maximum zoom level the map can be displayed at. - *

- * The default maximum zoomn level is 22. The upper bound for this value is 25.5. - * - * @param maxZoom The new maximum zoom level. - */ - public LocationLayerOptions.Builder maxZoom(double maxZoom) { - this.maxZoom = maxZoom; - return this; - } - - /** - * Sets the minimum zoom level the map can be displayed at. - * - * @param minZoom The new minimum zoom level. - */ - public LocationLayerOptions.Builder minZoom(double minZoom) { - this.minZoom = minZoom; - return this; - } - - /** - * Sets the scale factor of the location icon when the map is zoomed in. Based on {@link #maxZoom()}. - * Scaling is linear and the new pixel size of the image will be the original pixel size multiplied by the argument. - *

- * Set both this and {@link #minZoomIconScale(float)} to 1f to disable location icon scaling. - *

- * - * @param maxZoomIconScale icon scale factor - */ - public LocationLayerOptions.Builder maxZoomIconScale(float maxZoomIconScale) { - this.maxZoomIconScale = maxZoomIconScale; - return this; - } - - /** - * Sets the scale factor of the location icon when the map is zoomed out. Based on {@link #maxZoom()}. - * Scaling is linear and the new pixel size of the image will be the original pixel size multiplied by the argument. - *

- * Set both this and {@link #maxZoomIconScale(float)} to 1f to disable location icon scaling. - *

- * - * @param minZoomIconScale icon scale factor - */ - public LocationLayerOptions.Builder minZoomIconScale(float minZoomIconScale) { - this.minZoomIconScale = minZoomIconScale; - return this; - } - - /** - * Sets minimum single pointer movement (map pan) in pixels required to break camera tracking. - * - * @param moveThreshold the minimum movement - */ - public LocationLayerOptions.Builder trackingInitialMoveThreshold(float moveThreshold) { - this.trackingInitialMoveThreshold = moveThreshold; - return this; - } - - /** - * Sets minimum multi pointer movement (map pan) in pixels required to break camera tracking - * (for example during scale gesture). - * - * @param moveThreshold the minimum movement - */ - public LocationLayerOptions.Builder trackingMultiFingerMoveThreshold(float moveThreshold) { - this.trackingMultiFingerMoveThreshold = moveThreshold; - return this; - } - - /** - * Sets the layer id to set the location layer plugin below to. - * - * @param layerBelow the id to set the location layer plugin below to. - */ - public LocationLayerOptions.Builder layerBelow(String layerBelow) { - this.layerBelow = layerBelow; - return this; - } - - LocationLayerOptions autoBuild() { - String missing = ""; - if (this.accuracyAlpha == null) { - missing += " accuracyAlpha"; - } - if (this.accuracyColor == null) { - missing += " accuracyColor"; - } - if (this.backgroundDrawableStale == null) { - missing += " backgroundDrawableStale"; - } - if (this.foregroundDrawableStale == null) { - missing += " foregroundDrawableStale"; - } - if (this.gpsDrawable == null) { - missing += " gpsDrawable"; - } - if (this.foregroundDrawable == null) { - missing += " foregroundDrawable"; - } - if (this.backgroundDrawable == null) { - missing += " backgroundDrawable"; - } - if (this.bearingDrawable == null) { - missing += " bearingDrawable"; - } - if (this.elevation == null) { - missing += " elevation"; - } - if (this.enableStaleState == null) { - missing += " enableStaleState"; - } - if (this.staleStateTimeout == null) { - missing += " staleStateTimeout"; - } - if (this.padding == null) { - missing += " padding"; - } - if (this.maxZoom == null) { - missing += " maxZoom"; - } - if (this.minZoom == null) { - missing += " minZoom"; - } - if (this.maxZoomIconScale == null) { - missing += " maxZoomIconScale"; - } - if (this.minZoomIconScale == null) { - missing += " minZoomIconScale"; - } - if (this.trackingInitialMoveThreshold == null) { - missing += " trackingInitialMoveThreshold"; - } - if (this.trackingMultiFingerMoveThreshold == null) { - missing += " trackingMultiFingerMoveThreshold"; - } - if (!missing.isEmpty()) { - throw new IllegalStateException("Missing required properties:" + missing); - } - return new LocationLayerOptions( - this.accuracyAlpha, - this.accuracyColor, - this.backgroundDrawableStale, - this.backgroundStaleName, - this.foregroundDrawableStale, - this.foregroundStaleName, - this.gpsDrawable, - this.gpsName, - this.foregroundDrawable, - this.foregroundName, - this.backgroundDrawable, - this.backgroundName, - this.bearingDrawable, - this.bearingName, - this.bearingTintColor, - this.foregroundTintColor, - this.backgroundTintColor, - this.foregroundStaleTintColor, - this.backgroundStaleTintColor, - this.elevation, - this.enableStaleState, - this.staleStateTimeout, - this.padding, - this.maxZoom, - this.minZoom, - this.maxZoomIconScale, - this.minZoomIconScale, - this.trackingInitialMoveThreshold, - this.trackingMultiFingerMoveThreshold, - this.layerBelow); - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerPlugin.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerPlugin.java deleted file mode 100644 index c98ee9c4ce..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerPlugin.java +++ /dev/null @@ -1,1017 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.hardware.SensorManager; -import android.location.Location; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.RequiresPermission; -import android.support.annotation.StyleRes; -import android.support.v7.app.AppCompatDelegate; -import android.view.WindowManager; - -import com.mapbox.android.core.location.LocationEngine; -import com.mapbox.android.core.location.LocationEngineListener; -import com.mapbox.android.core.location.LocationEnginePriority; -import com.mapbox.android.core.location.LocationEngineProvider; -import com.mapbox.mapboxsdk.R; -import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.camera.CameraUpdate; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.log.Logger; -import com.mapbox.mapboxsdk.maps.MapboxMap; -import com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraIdleListener; -import com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveListener; -import com.mapbox.mapboxsdk.maps.MapboxMap.OnMapClickListener; -import com.mapbox.mapboxsdk.plugins.locationlayer.modes.CameraMode; -import com.mapbox.mapboxsdk.plugins.locationlayer.modes.RenderMode; - -import java.util.concurrent.CopyOnWriteArrayList; - -import static android.Manifest.permission.ACCESS_COARSE_LOCATION; -import static android.Manifest.permission.ACCESS_FINE_LOCATION; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.DEFAULT_TRACKING_TILT_ANIM_DURATION; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.DEFAULT_TRACKING_ZOOM_ANIM_DURATION; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.STATE_LOCATION_CAMERA_MODE; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.STATE_LOCATION_ENABLED; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.STATE_LOCATION_LAST_LOCATION; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.STATE_LOCATION_OPTIONS; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.STATE_LOCATION_RENDER_MODE; - -/** - * The Location layer plugin provides location awareness to your mobile application. Enabling this - * plugin provides a contextual experience to your users by showing an icon representing the users - * current location. A few different modes are offered to provide the right context to your users at - * the correct time. {@link RenderMode#NORMAL} simply shows the users location on the map - * represented as a dot. {@link RenderMode#COMPASS} mode allows you to display an arrow icon - * (by default) that points in the direction the device is pointing in. - * {@link RenderMode#GPS} can be used in conjunction with our Navigation SDK to - * display a larger icon (customized with {@link LocationLayerOptions#gpsDrawable()}) we call the user puck. - *

- * This plugin also offers the ability to set a map camera behavior for tracking the user - * location. These different {@link CameraMode}s will track, stop tracking the location based on the - * mode set with {@link LocationLayerPlugin#setCameraMode(int)}. - *

- * Lastly, {@link LocationLayerPlugin#setLocationLayerEnabled(boolean)} can be used - * to disable the Location Layer but keep the instance around till the activity is destroyed. - *

- * Using this plugin requires you to request permission beforehand manually or using - * {@link com.mapbox.android.core.permissions.PermissionsManager}. Either - * {@code ACCESS_COARSE_LOCATION} or {@code ACCESS_FINE_LOCATION} permissions can be requested and - * this plugin work as expected. - *

- * When instantiating the plugin for the first time, the map's max/min zoom levels will be set to - * {@link LocationLayerOptions#MAX_ZOOM_DEFAULT} and {@link LocationLayerOptions#MIN_ZOOM_DEFAULT} respectively. - * You can adjust the zoom range with {@link LocationLayerOptions#maxZoom()} and {@link LocationLayerOptions#minZoom()}. - *

- * When an activity, or a fragment, that contains the plugin is destroyed and recreated, - * the plugin will restore its state, which is: - *
- * - If the plugin was enabled, last location will be displayed. - * You still need to activate the plugin, or just provide the {@link LocationEngine}. - *
- * - {@link CameraMode} and {@link RenderMode} will be restored. - *
- * - {@link LocationLayerOptions} will be restored. - */ -public final class LocationLayerPlugin { - private static final String TAG = "Mbgl-LocationLayerPlugin"; - - private final MapboxMap mapboxMap; - private LocationLayerOptions options; - private LocationEngine locationEngine; - private CompassEngine compassEngine; - private boolean usingInternalLocationEngine; - - private LocationLayer locationLayer; - private LocationLayerCamera locationLayerCamera; - - private PluginAnimatorCoordinator pluginAnimatorCoordinator; - - /** - * Holds last location which is being returned in the {@link #getLastKnownLocation()} - * when there is no {@link #locationEngine} set or when the last location returned by the engine is null. - */ - private Location lastLocation; - private CameraPosition lastCameraPosition; - - /** - * Indicates that the plugin is enabled and should be displaying location if Mapbox components are available and - * the lifecycle is in a resumed state. - */ - private boolean isEnabled; - - /** - * Indicated that plugin's lifecycle {@link #onStart()} method has been called or the plugin is initialized.. - * This allows Mapbox components enter started state and display data, and adds state safety for methods like - * {@link #setLocationLayerEnabled(boolean)} - *

- * Initialized in a started state because the plugin can be instantiated after lifecycle's onStart() and - * the developer might not register the lifecycle observer but call lifecycle methods manually instead. - */ - private boolean isPluginStarted; - - /** - * Indicates if Mapbox components are ready to be interacted with. This can differ from {@link #isPluginStarted} - * if the Mapbox style is being reloaded. - */ - private boolean isLocationLayerStarted; - - private StaleStateManager staleStateManager; - private final CopyOnWriteArrayList onLocationStaleListeners - = new CopyOnWriteArrayList<>(); - private final CopyOnWriteArrayList onLocationLayerClickListeners - = new CopyOnWriteArrayList<>(); - private final CopyOnWriteArrayList onLocationLayerLongClickListeners - = new CopyOnWriteArrayList<>(); - private final CopyOnWriteArrayList onCameraTrackingChangedListeners - = new CopyOnWriteArrayList<>(); - - /** - * Construct a LocationLayerPlugin. In order to display location, - * the location layer has to be activated with {@link LocationLayerPlugin#activateLocationLayerPlugin(Context)}, - * or one of the overloads. - * - * @param mapboxMap the MapboxMap to apply the LocationLayerPlugin with - */ - public LocationLayerPlugin(@NonNull Context context, @NonNull MapboxMap mapboxMap) { - this.mapboxMap = mapboxMap; - options = LocationLayerOptions.createFromAttributes(context, R.style.mapbox_LocationLayer); - initialize(context); - } - - /** - * This method will show or hide the location icon and enable or disable the camera - * tracking the location. - * - * @param isEnabled true to show layers and enable camera, false otherwise - */ - private void setLocationLayerEnabled(boolean isEnabled) { - if (isEnabled) { - enableLocationLayerPlugin(); - } else { - disableLocationLayerPlugin(); - } - } - - /** - * This method will show the location icon and enable the camera tracking the location. - *

- * Note: This method will initialize and use an internal {@link LocationEngine}. - * - * @param context the context - */ - @RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION}) - public void activateLocationLayerPlugin(@NonNull Context context) { - activateLocationLayerPlugin(context, LocationLayerOptions.createFromAttributes(context, R.style - .mapbox_LocationLayer)); - } - - /** - * This method will show the location icon and enable the camera tracking the location. - * - * @param context the context - * @param useDefaultLocationEngine true if you want to initialize and use the built-in location engine or false if - * there should be no location engine initialized - */ - @RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION}) - public void activateLocationLayerPlugin(@NonNull Context context, boolean useDefaultLocationEngine) { - if (useDefaultLocationEngine) { - activateLocationLayerPlugin(context, R.style.mapbox_LocationLayer); - } else { - activateLocationLayerPlugin(context, null, R.style.mapbox_LocationLayer); - } - } - - /** - * This method will show the location icon and enable the camera tracking the location. - *

- * Note: This method will initialize and use an internal {@link LocationEngine}. - * - * @param context the context - * @param styleRes the LocationLayerPlugin style res - */ - @RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION}) - public void activateLocationLayerPlugin(@NonNull Context context, @StyleRes int styleRes) { - activateLocationLayerPlugin(context, LocationLayerOptions.createFromAttributes(context, styleRes)); - } - - /** - * This method will show the location icon and enable the camera tracking the location. - *

- * Note: This method will initialize and use an internal {@link LocationEngine}. - *

- * - * @param context the context - * @param options the options - */ - @RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION}) - public void activateLocationLayerPlugin(@NonNull Context context, @NonNull LocationLayerOptions options) { - applyStyle(options); - initializeLocationEngine(context); - setLocationLayerEnabled(true); - } - - /** - * This method will show the location icon and enable the camera tracking the location. - * - * @param context the context - * @param locationEngine the engine, or null if you'd like to only force location updates - * @param styleRes the LocationLayerPlugin style res - */ - public void activateLocationLayerPlugin(@NonNull Context context, @Nullable LocationEngine locationEngine, - @StyleRes int styleRes) { - activateLocationLayerPlugin(locationEngine, LocationLayerOptions.createFromAttributes(context, styleRes)); - } - - /** - * This method will show the location icon and enable the camera tracking the location. - * - * @param context the context - * @param locationEngine the engine - */ - public void activateLocationLayerPlugin(@NonNull Context context, @NonNull LocationEngine locationEngine) { - activateLocationLayerPlugin(context, locationEngine, R.style.mapbox_LocationLayer); - } - - /** - * This method will show the location icon and enable the camera tracking the location. - * - * @param locationEngine the engine, or null if you'd like to only force location updates - * @param options the options - */ - public void activateLocationLayerPlugin(@Nullable LocationEngine locationEngine, - @NonNull LocationLayerOptions options) { - setLocationEngine(locationEngine); - applyStyle(options); - setLocationLayerEnabled(true); - } - - /** - * This method will hide the location icon and disable the camera tracking the location. - */ - public void deactivateLocationLayerPlugin() { - setLocationLayerEnabled(false); - } - - /** - * Returns whether the plugin is enabled, meaning that location can be displayed and camera modes can be used. - * - * @return true if the plugin is enabled, false otherwise - */ - public boolean isLocationLayerEnabled() { - return isEnabled; - } - - /** - * Sets the camera mode, which determines how the map camera will track the rendered location. - *

- *

    - *
  • {@link CameraMode#NONE}: No camera tracking
  • - *
  • {@link CameraMode#NONE_COMPASS}: Camera does not track location, but does track compass bearing
  • - *
  • {@link CameraMode#NONE_GPS}: Camera does not track location, but does track GPS bearing
  • - *
  • {@link CameraMode#TRACKING}: Camera tracks the user location
  • - *
  • {@link CameraMode#TRACKING_COMPASS}: Camera tracks the user location, with bearing provided by a compass
  • - *
  • {@link CameraMode#TRACKING_GPS}: Camera tracks the user location, with normalized bearing
  • - *
  • {@link CameraMode#TRACKING_GPS_NORTH}: Camera tracks the user location, with bearing always set to north
  • - *
- * - * @param cameraMode one of the modes found in {@link CameraMode} - */ - public void setCameraMode(@CameraMode.Mode int cameraMode) { - locationLayerCamera.setCameraMode(cameraMode); - boolean isGpsNorth = cameraMode == CameraMode.TRACKING_GPS_NORTH; - pluginAnimatorCoordinator.resetAllCameraAnimations(mapboxMap.getCameraPosition(), isGpsNorth); - } - - /** - * Provides the current camera mode being used to track - * the location or compass updates. - * - * @return the current camera mode - */ - @CameraMode.Mode - public int getCameraMode() { - return locationLayerCamera.getCameraMode(); - } - - /** - * Sets the render mode, which determines how the location updates will be rendered on the map. - *

- *

    - *
  • {@link RenderMode#NORMAL}: Shows user location, bearing ignored
  • - *
  • {@link RenderMode#COMPASS}: Shows user location with bearing considered from compass
  • - *
  • {@link RenderMode#GPS}: Shows user location with bearing considered from location
  • - *
- * - * @param renderMode one of the modes found in {@link RenderMode} - */ - public void setRenderMode(@RenderMode.Mode int renderMode) { - locationLayer.setRenderMode(renderMode); - updateLayerOffsets(true); - } - - /** - * Provides the current render mode being used to show - * the location and/or compass updates on the map. - * - * @return the current render mode - */ - @RenderMode.Mode - public int getRenderMode() { - return locationLayer.getRenderMode(); - } - - /** - * Returns the current location options being used. - * - * @return the current {@link LocationLayerOptions} - */ - public LocationLayerOptions getLocationLayerOptions() { - return options; - } - - /** - * Apply a new LocationLayer style with a style resource. - * - * @param styleRes a XML style overriding some or all the options - */ - public void applyStyle(@NonNull Context context, @StyleRes int styleRes) { - applyStyle(LocationLayerOptions.createFromAttributes(context, styleRes)); - } - - /** - * Apply a new LocationLayer style with location layer options. - * - * @param options to update the current style - */ - public void applyStyle(LocationLayerOptions options) { - this.options = options; - locationLayer.applyStyle(options); - staleStateManager.setEnabled(options.enableStaleState()); - staleStateManager.setDelayTime(options.staleStateTimeout()); - updateMapWithOptions(options); - } - - /** - * Zooms to the desired zoom level. - * This API can only be used in pair with camera modes other than {@link CameraMode#NONE}. - * If you are not using any of {@link CameraMode} modes, - * use one of {@link MapboxMap#moveCamera(CameraUpdate)}, - * {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead. - * - * @param zoomLevel The desired zoom level. - * @param animationDuration The zoom animation duration. - * @param callback The callback with finish/cancel information - */ - public void zoomWhileTracking(double zoomLevel, long animationDuration, - @Nullable MapboxMap.CancelableCallback callback) { - if (!isLocationLayerStarted) { - return; - } else if (getCameraMode() == CameraMode.NONE) { - Logger.e(TAG, String.format("%s%s", - "LocationLayerPlugin#zoomWhileTracking method can only be used", - " when a camera mode other than CameraMode#NONE is engaged.")); - return; - } - pluginAnimatorCoordinator.feedNewZoomLevel(zoomLevel, mapboxMap.getCameraPosition(), animationDuration, callback); - } - - /** - * Zooms to the desired zoom level. - * This API can only be used in pair with camera modes other than {@link CameraMode#NONE}. - * If you are not using any of {@link CameraMode} modes, - * use one of {@link MapboxMap#moveCamera(CameraUpdate)}, - * {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead. - * - * @param zoomLevel The desired zoom level. - * @param animationDuration The zoom animation duration. - */ - public void zoomWhileTracking(double zoomLevel, long animationDuration) { - zoomWhileTracking(zoomLevel, animationDuration, null); - } - - /** - * Zooms to the desired zoom level. - * This API can only be used in pair with camera modes other than {@link CameraMode#NONE}. - * If you are not using any of {@link CameraMode} modes, - * use one of {@link MapboxMap#moveCamera(CameraUpdate)}, - * {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead. - * - * @param zoomLevel The desired zoom level. - */ - public void zoomWhileTracking(double zoomLevel) { - zoomWhileTracking(zoomLevel, DEFAULT_TRACKING_ZOOM_ANIM_DURATION, null); - } - - /** - * Cancels animation started by {@link #zoomWhileTracking(double, long, MapboxMap.CancelableCallback)}. - */ - public void cancelZoomWhileTrackingAnimation() { - pluginAnimatorCoordinator.cancelZoomAnimation(); - } - - /** - * Tilts the camera. - * This API can only be used in pair with camera modes other than {@link CameraMode#NONE}. - * If you are not using any of {@link CameraMode} modes, - * use one of {@link MapboxMap#moveCamera(CameraUpdate)}, - * {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead. - * - * @param tilt The desired camera tilt. - * @param animationDuration The tilt animation duration. - * @param callback The callback with finish/cancel information - */ - public void tiltWhileTracking(double tilt, long animationDuration, - @Nullable MapboxMap.CancelableCallback callback) { - if (!isLocationLayerStarted) { - return; - } else if (getCameraMode() == CameraMode.NONE) { - Logger.e(TAG, String.format("%s%s", - "LocationLayerPlugin#tiltWhileTracking method can only be used", - " when a camera mode other than CameraMode#NONE is engaged.")); - return; - } - pluginAnimatorCoordinator.feedNewTilt(tilt, mapboxMap.getCameraPosition(), animationDuration, callback); - } - - /** - * Tilts the camera. - * This API can only be used in pair with camera modes other than {@link CameraMode#NONE}. - * If you are not using any of {@link CameraMode} modes, - * use one of {@link MapboxMap#moveCamera(CameraUpdate)}, - * {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead. - * - * @param tilt The desired camera tilt. - * @param animationDuration The tilt animation duration. - */ - public void tiltWhileTracking(double tilt, long animationDuration) { - tiltWhileTracking(tilt, animationDuration, null); - } - - /** - * Tilts the camera. - * This API can only be used in pair with camera modes other than {@link CameraMode#NONE}. - * If you are not using any of {@link CameraMode} modes, - * use one of {@link MapboxMap#moveCamera(CameraUpdate)}, - * {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead. - * - * @param tilt The desired camera tilt. - */ - public void tiltWhileTracking(double tilt) { - tiltWhileTracking(tilt, DEFAULT_TRACKING_TILT_ANIM_DURATION, null); - } - - /** - * Cancels animation started by {@link #tiltWhileTracking(double, long, MapboxMap.CancelableCallback)}. - */ - public void cancelTiltWhileTrackingAnimation() { - pluginAnimatorCoordinator.cancelTiltAnimation(); - } - - /** - * Use to either force a location update or to manually control when the user location gets - * updated. - * - * @param location where the location icon is placed on the map - */ - public void forceLocationUpdate(@Nullable Location location) { - updateLocation(location, false); - } - - /** - * Set the location engine to update the current user location. - *

- * If {@code null} is passed in, all updates will occur through the - * {@link LocationLayerPlugin#forceLocationUpdate(Location)} method. - * - * @param locationEngine a {@link LocationEngine} this plugin should use to handle updates - */ - public void setLocationEngine(@Nullable LocationEngine locationEngine) { - if (this.locationEngine != null) { - // If internal location engines being used, extra steps need to be taken to deconstruct the - // instance. - if (usingInternalLocationEngine) { - this.locationEngine.removeLocationUpdates(); - this.locationEngine.deactivate(); - usingInternalLocationEngine = false; - } - this.locationEngine.removeLocationEngineListener(locationEngineListener); - this.locationEngine = null; - } - - if (locationEngine != null) { - this.locationEngine = locationEngine; - if (isEnabled) { - this.locationEngine.addLocationEngineListener(locationEngineListener); - } - } - } - - /** - * Returns the current {@link LocationEngine} being used for updating the user location layer. - * - * @return the {@link LocationEngine} being used to update the user location layer - */ - @Nullable - public LocationEngine getLocationEngine() { - return locationEngine; - } - - /** - * Sets the compass engine used to provide compass heading values. - * - * @param compassEngine to be used - */ - public void setCompassEngine(@NonNull CompassEngine compassEngine) { - this.compassEngine.removeCompassListener(compassListener); - this.compassEngine = compassEngine; - compassEngine.addCompassListener(compassListener); - } - - /** - * Returns the compass engine used to provide compass heading values. - * - * @return compass engine currently being used - */ - @NonNull - public CompassEngine getCompassEngine() { - return compassEngine; - } - - /** - * Get the last know location of the location layer plugin. - * - * @return the last known location - */ - @Nullable - @RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION}) - public Location getLastKnownLocation() { - Location location = locationEngine != null ? locationEngine.getLastLocation() : null; - if (location == null) { - location = lastLocation; - } - return location; - } - - /** - * Return the last known {@link CompassEngine} accuracy status of the location layer plugin. - *

- * The last known accuracy of the compass sensor, one of SensorManager.SENSOR_STATUS_* - * - * @return the last know compass accuracy bearing - */ - public float getLastKnownCompassAccuracyStatus() { - return compassEngine.getLastAccuracySensorStatus(); - } - - /** - * Add a compass listener to get heading updates every second. Once the first listener gets added, - * the sensor gets initiated and starts returning values. - * - * @param compassListener a {@link CompassListener} for listening into compass heading and - * accuracy changes - */ - public void addCompassListener(@NonNull CompassListener compassListener) { - compassEngine.addCompassListener(compassListener); - } - - /** - * Remove a compass listener. - * - * @param compassListener the {@link CompassListener} which you'd like to remove from the listener - * list. - */ - public void removeCompassListener(@NonNull CompassListener compassListener) { - compassEngine.removeCompassListener(compassListener); - } - - /** - * Adds a listener that gets invoked when the user clicks the location layer. - * - * @param listener The location layer click listener that is invoked when the - * location layer is clicked - */ - public void addOnLocationClickListener(@NonNull OnLocationLayerClickListener listener) { - onLocationLayerClickListeners.add(listener); - } - - /** - * Removes the passed listener from the current list of location click listeners. - * - * @param listener to be removed - */ - public void removeOnLocationClickListener(@NonNull OnLocationLayerClickListener listener) { - onLocationLayerClickListeners.remove(listener); - } - - /** - * Adds a listener that gets invoked when the user long clicks the location layer. - * - * @param listener The location layer click listener that is invoked when the - * location layer is clicked - */ - public void addOnLocationLongClickListener(@NonNull OnLocationLayerLongClickListener listener) { - onLocationLayerLongClickListeners.add(listener); - } - - /** - * Removes the passed listener from the current list of location long click listeners. - * - * @param listener to be removed - */ - public void removeOnLocationLongClickListener(@NonNull OnLocationLayerLongClickListener listener) { - onLocationLayerLongClickListeners.remove(listener); - } - - /** - * Adds a listener that gets invoked when camera tracking state changes. - * - * @param listener Listener that gets invoked when camera tracking state changes. - */ - public void addOnCameraTrackingChangedListener(@NonNull OnCameraTrackingChangedListener listener) { - onCameraTrackingChangedListeners.add(listener); - } - - /** - * Removes a listener that gets invoked when camera tracking state changes. - * - * @param listener Listener that gets invoked when camera tracking state changes. - */ - public void removeOnCameraTrackingChangedListener(@NonNull OnCameraTrackingChangedListener listener) { - onCameraTrackingChangedListeners.remove(listener); - } - - /** - * Adds the passed listener that gets invoked when user updates have stopped long enough for the last update - * to be considered stale. - *

- * This timeout is set by {@link LocationLayerOptions#staleStateTimeout()}. - * - * @param listener invoked when last update is considered stale - */ - public void addOnLocationStaleListener(@NonNull OnLocationStaleListener listener) { - onLocationStaleListeners.add(listener); - } - - /** - * Removes the passed listener from the current list of stale listeners. - * - * @param listener to be removed from the list - */ - public void removeOnLocationStaleListener(@NonNull OnLocationStaleListener listener) { - onLocationStaleListeners.remove(listener); - } - - /** - * Internal use. - */ - public void onStart() { - isPluginStarted = true; - onLocationLayerStart(); - } - - /** - * Internal use. - */ - public void onStop() { - onLocationLayerStop(); - isPluginStarted = false; - } - - /** - * Internal use. - */ - public void onSaveInstanceState(@NonNull Bundle outState) { - outState.putBoolean(STATE_LOCATION_ENABLED, isEnabled); - outState.putParcelable(STATE_LOCATION_OPTIONS, options); - outState.putInt(STATE_LOCATION_RENDER_MODE, locationLayer.getRenderMode()); - outState.putInt(STATE_LOCATION_CAMERA_MODE, locationLayerCamera.getCameraMode()); - outState.putParcelable(STATE_LOCATION_LAST_LOCATION, lastLocation); - } - - /** - * Internal use. - */ - public void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { - updateLocation(savedInstanceState.getParcelable(STATE_LOCATION_LAST_LOCATION), true); - setCameraMode(savedInstanceState.getInt(STATE_LOCATION_CAMERA_MODE)); - setRenderMode(savedInstanceState.getInt(STATE_LOCATION_RENDER_MODE)); - applyStyle(savedInstanceState.getParcelable(STATE_LOCATION_OPTIONS)); - setLocationLayerEnabled(savedInstanceState.getBoolean(STATE_LOCATION_ENABLED)); - } - - /** - * Internal use. - */ - public void onDestroy() { - if (locationEngine != null && usingInternalLocationEngine) { - locationEngine.deactivate(); - } - } - - /** - * Internal use. - */ - public void onStartLoadingMap() { - onLocationLayerStop(); - } - - /** - * Internal use. - */ - public void onFinishLoadingStyle() { - locationLayer.initializeComponents(options); - locationLayerCamera.initializeOptions(options); - onLocationLayerStart(); - } - - @SuppressLint("MissingPermission") - private void onLocationLayerStart() { - if (!isPluginStarted) { - return; - } - - if (!isLocationLayerStarted) { - isLocationLayerStarted = true; - if (mapboxMap != null) { - mapboxMap.addOnCameraMoveListener(onCameraMoveListener); - mapboxMap.addOnCameraIdleListener(onCameraIdleListener); - } - if (options.enableStaleState()) { - staleStateManager.onStart(); - } - compassEngine.onStart(); - } - - if (isEnabled) { - if (locationEngine != null) { - locationEngine.addLocationEngineListener(locationEngineListener); - if (locationEngine.isConnected() && usingInternalLocationEngine) { - locationEngine.requestLocationUpdates(); - } - } - setCameraMode(locationLayerCamera.getCameraMode()); - setLastLocation(); - setLastCompassHeading(); - } - } - - private void onLocationLayerStop() { - if (!isLocationLayerStarted || !isPluginStarted) { - return; - } - - isLocationLayerStarted = false; - locationLayer.hide(); - staleStateManager.onStop(); - compassEngine.onStop(); - pluginAnimatorCoordinator.cancelAllAnimations(); - if (locationEngine != null) { - if (usingInternalLocationEngine) { - locationEngine.removeLocationUpdates(); - } - locationEngine.removeLocationEngineListener(locationEngineListener); - } - if (mapboxMap != null) { - mapboxMap.removeOnCameraMoveListener(onCameraMoveListener); - mapboxMap.removeOnCameraIdleListener(onCameraIdleListener); - } - } - - private void initialize(@NonNull Context context) { - AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); - - mapboxMap.addOnMapClickListener(onMapClickListener); - mapboxMap.addOnMapLongClickListener(onMapLongClickListener); - - LayerSourceProvider sourceProvider = new LayerSourceProvider(); - LayerFeatureProvider featureProvider = new LayerFeatureProvider(); - LayerBitmapProvider bitmapProvider = new LayerBitmapProvider(context); - locationLayer = new LocationLayer(mapboxMap, sourceProvider, featureProvider, bitmapProvider, options); - locationLayerCamera = new LocationLayerCamera( - context, mapboxMap, cameraTrackingChangedListener, options, onCameraMoveInvalidateListener); - pluginAnimatorCoordinator = new PluginAnimatorCoordinator(); - pluginAnimatorCoordinator.addLayerListener(locationLayer); - pluginAnimatorCoordinator.addCameraListener(locationLayerCamera); - - WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); - SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); - compassEngine = new LocationLayerCompassEngine(windowManager, sensorManager); - compassEngine.addCompassListener(compassListener); - staleStateManager = new StaleStateManager(onLocationStaleListener, options); - - updateMapWithOptions(options); - - setRenderMode(RenderMode.NORMAL); - setCameraMode(CameraMode.NONE); - } - - private void initializeLocationEngine(@NonNull Context context) { - if (this.locationEngine != null) { - if (usingInternalLocationEngine) { - this.locationEngine.removeLocationUpdates(); - this.locationEngine.deactivate(); - } - this.locationEngine.removeLocationEngineListener(locationEngineListener); - } - - usingInternalLocationEngine = true; - locationEngine = new LocationEngineProvider(context).obtainBestLocationEngineAvailable(); - locationEngine.setPriority(LocationEnginePriority.HIGH_ACCURACY); - locationEngine.setFastestInterval(1000); - locationEngine.addLocationEngineListener(locationEngineListener); - locationEngine.activate(); - } - - private void enableLocationLayerPlugin() { - isEnabled = true; - onLocationLayerStart(); - } - - private void disableLocationLayerPlugin() { - isEnabled = false; - onLocationLayerStop(); - } - - private void updateMapWithOptions(final LocationLayerOptions options) { - mapboxMap.setPadding( - options.padding()[0], options.padding()[1], options.padding()[2], options.padding()[3] - ); - - mapboxMap.setMaxZoomPreference(options.maxZoom()); - mapboxMap.setMinZoomPreference(options.minZoom()); - } - - /** - * Updates the user location icon. - * - * @param location the latest user location - */ - private void updateLocation(final Location location, boolean fromLastLocation) { - if (location == null) { - return; - } else if (!isLocationLayerStarted) { - lastLocation = location; - return; - } - - if (isEnabled && isPluginStarted) { - locationLayer.show(); - } - - if (!fromLastLocation) { - staleStateManager.updateLatestLocationTime(); - } - CameraPosition currentCameraPosition = mapboxMap.getCameraPosition(); - boolean isGpsNorth = getCameraMode() == CameraMode.TRACKING_GPS_NORTH; - pluginAnimatorCoordinator.feedNewLocation(location, currentCameraPosition, isGpsNorth); - updateAccuracyRadius(location, false); - lastLocation = location; - } - - private void updateCompassHeading(float heading) { - pluginAnimatorCoordinator.feedNewCompassBearing(heading, mapboxMap.getCameraPosition()); - } - - /** - * If the locationEngine contains a last location value, we use it for the initial location layer - * position. - */ - @SuppressLint("MissingPermission") - private void setLastLocation() { - updateLocation(getLastKnownLocation(), true); - } - - private void setLastCompassHeading() { - updateCompassHeading(compassEngine.getLastHeading()); - } - - @SuppressLint("MissingPermission") - private void updateLayerOffsets(boolean forceUpdate) { - CameraPosition position = mapboxMap.getCameraPosition(); - if (lastCameraPosition == null || forceUpdate) { - lastCameraPosition = position; - locationLayer.updateForegroundBearing((float) position.bearing); - locationLayer.updateForegroundOffset(position.tilt); - updateAccuracyRadius(getLastKnownLocation(), true); - return; - } - - if (position.bearing != lastCameraPosition.bearing) { - locationLayer.updateForegroundBearing((float) position.bearing); - } - if (position.tilt != lastCameraPosition.tilt) { - locationLayer.updateForegroundOffset(position.tilt); - } - if (position.zoom != lastCameraPosition.zoom) { - updateAccuracyRadius(getLastKnownLocation(), true); - } - lastCameraPosition = position; - } - - private void updateAccuracyRadius(Location location, boolean noAnimation) { - pluginAnimatorCoordinator.feedNewAccuracyRadius(Utils.calculateZoomLevelRadius(mapboxMap, location), noAnimation); - } - - private OnCameraMoveListener onCameraMoveListener = new OnCameraMoveListener() { - @Override - public void onCameraMove() { - updateLayerOffsets(false); - } - }; - - private OnCameraIdleListener onCameraIdleListener = new OnCameraIdleListener() { - @Override - public void onCameraIdle() { - updateLayerOffsets(false); - } - }; - - private OnMapClickListener onMapClickListener = new OnMapClickListener() { - @Override - public void onMapClick(@NonNull LatLng point) { - if (!onLocationLayerClickListeners.isEmpty() && locationLayer.onMapClick(point)) { - for (OnLocationLayerClickListener listener : onLocationLayerClickListeners) { - listener.onLocationLayerClick(); - } - } - } - }; - - private MapboxMap.OnMapLongClickListener onMapLongClickListener = new MapboxMap.OnMapLongClickListener() { - @Override - public void onMapLongClick(@NonNull LatLng point) { - if (!onLocationLayerLongClickListeners.isEmpty() && locationLayer.onMapClick(point)) { - for (OnLocationLayerLongClickListener listener : onLocationLayerLongClickListeners) { - listener.onLocationLayerLongClick(); - } - } - } - }; - - private OnLocationStaleListener onLocationStaleListener = new OnLocationStaleListener() { - @Override - public void onStaleStateChange(boolean isStale) { - locationLayer.setLocationsStale(isStale); - - for (OnLocationStaleListener listener : onLocationStaleListeners) { - listener.onStaleStateChange(isStale); - } - } - }; - - private OnCameraMoveInvalidateListener onCameraMoveInvalidateListener = new OnCameraMoveInvalidateListener() { - @Override - public void onInvalidateCameraMove() { - onCameraMoveListener.onCameraMove(); - } - }; - - private CompassListener compassListener = new CompassListener() { - @Override - public void onCompassChanged(float userHeading) { - updateCompassHeading(userHeading); - } - - @Override - public void onCompassAccuracyChange(int compassStatus) { - // Currently don't handle this inside SDK - } - }; - - private LocationEngineListener locationEngineListener = new LocationEngineListener() { - @Override - @SuppressWarnings( {"MissingPermission"}) - public void onConnected() { - if (usingInternalLocationEngine && isLocationLayerStarted && isEnabled) { - locationEngine.requestLocationUpdates(); - } - } - - @Override - public void onLocationChanged(Location location) { - updateLocation(location, false); - } - }; - - private OnCameraTrackingChangedListener cameraTrackingChangedListener = new OnCameraTrackingChangedListener() { - @Override - public void onCameraTrackingDismissed() { - for (OnCameraTrackingChangedListener listener : onCameraTrackingChangedListeners) { - listener.onCameraTrackingDismissed(); - } - } - - @Override - public void onCameraTrackingChanged(int currentMode) { - pluginAnimatorCoordinator.cancelZoomAnimation(); - pluginAnimatorCoordinator.cancelTiltAnimation(); - for (OnCameraTrackingChangedListener listener : onCameraTrackingChangedListeners) { - listener.onCameraTrackingChanged(currentMode); - } - } - }; -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/MapboxCameraAnimatorAdapter.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/MapboxCameraAnimatorAdapter.java deleted file mode 100644 index d2381cc1e3..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/MapboxCameraAnimatorAdapter.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.support.annotation.Nullable; - -import com.mapbox.mapboxsdk.maps.MapboxMap; - -import java.util.List; - -abstract class MapboxCameraAnimatorAdapter extends - PluginFloatAnimator { - private final MapboxMap.CancelableCallback cancelableCallback; - - MapboxCameraAnimatorAdapter(Float previous, Float target, - List updateListeners, - @Nullable MapboxMap.CancelableCallback cancelableCallback) { - super(previous, target, updateListeners); - this.cancelableCallback = cancelableCallback; - addListener(new MapboxAnimatorListener()); - } - - private final class MapboxAnimatorListener extends AnimatorListenerAdapter { - @Override - public void onAnimationCancel(Animator animation) { - if (cancelableCallback != null) { - cancelableCallback.onCancel(); - } - } - - @Override - public void onAnimationEnd(Animator animation) { - if (cancelableCallback != null) { - cancelableCallback.onFinish(); - } - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/OnCameraMoveInvalidateListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/OnCameraMoveInvalidateListener.java deleted file mode 100644 index 3636df9f87..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/OnCameraMoveInvalidateListener.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -interface OnCameraMoveInvalidateListener { - - void onInvalidateCameraMove(); - -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/OnCameraTrackingChangedListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/OnCameraTrackingChangedListener.java deleted file mode 100644 index 272b3c73f9..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/OnCameraTrackingChangedListener.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -import com.mapbox.mapboxsdk.plugins.locationlayer.modes.CameraMode; - -/** - * Listener that gets invoked when camera tracking state changes. - */ -public interface OnCameraTrackingChangedListener { - /** - * Invoked whenever camera tracking is broken. - * This callback gets invoked just after {@link #onCameraTrackingChanged(int)}, if needed. - */ - void onCameraTrackingDismissed(); - - /** - * Invoked on every {@link CameraMode} change. - * - * @param currentMode current active {@link CameraMode}. - */ - void onCameraTrackingChanged(@CameraMode.Mode int currentMode); -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/OnLocationLayerClickListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/OnLocationLayerClickListener.java deleted file mode 100644 index 83d4ce72cc..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/OnLocationLayerClickListener.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -/** - * The Location Layer Plugin exposes an API for listening to when the user clicks on the location - * layer icon visible on the map. when this event occurs, the {@link #onLocationLayerClick()} method - * gets invoked. - */ -public interface OnLocationLayerClickListener { - - void onLocationLayerClick(); -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/OnLocationLayerLongClickListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/OnLocationLayerLongClickListener.java deleted file mode 100644 index 5724c9f1dc..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/OnLocationLayerLongClickListener.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -/** - * The Location Layer Plugin exposes an API for listening to when the user long clicks on the location - * layer icon visible on the map. when this event occurs, the {@link #onLocationLayerLongClick()} method - * gets invoked. - */ -public interface OnLocationLayerLongClickListener { - - void onLocationLayerLongClick(); -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/OnLocationStaleListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/OnLocationStaleListener.java deleted file mode 100644 index 979bed3e2a..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/OnLocationStaleListener.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -/** - * Listener that can be added as a callback when the last location update - * is considered stale. - *

- * The time from the last location update that determines if a location update - * is stale or not is provided by {@link LocationLayerOptions#staleStateTimeout()}. - */ -public interface OnLocationStaleListener { - - void onStaleStateChange(boolean isStale); -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginAnimator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginAnimator.java deleted file mode 100644 index faa2a1ae44..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginAnimator.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -import android.animation.TypeEvaluator; -import android.animation.ValueAnimator; -import android.support.annotation.IntDef; - -import com.mapbox.mapboxsdk.geometry.LatLng; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.List; - -/** - * Abstract class for all of the plugin animators. - * - * @param Data type that will be animated. - * @param Listener of animation updates. - */ -abstract class PluginAnimator extends ValueAnimator implements ValueAnimator.AnimatorUpdateListener { - @Retention(RetentionPolicy.SOURCE) - @IntDef( { - ANIMATOR_LAYER_LATLNG, - ANIMATOR_CAMERA_LATLNG, - ANIMATOR_LAYER_GPS_BEARING, - ANIMATOR_LAYER_COMPASS_BEARING, - ANIMATOR_CAMERA_GPS_BEARING, - ANIMATOR_CAMERA_COMPASS_BEARING, - ANIMATOR_LAYER_ACCURACY, - ANIMATOR_ZOOM, - ANIMATOR_TILT - }) - @interface Type { - } - - static final int ANIMATOR_LAYER_LATLNG = 0; - static final int ANIMATOR_CAMERA_LATLNG = 1; - static final int ANIMATOR_LAYER_GPS_BEARING = 2; - static final int ANIMATOR_LAYER_COMPASS_BEARING = 3; - static final int ANIMATOR_CAMERA_GPS_BEARING = 4; - static final int ANIMATOR_CAMERA_COMPASS_BEARING = 5; - static final int ANIMATOR_LAYER_ACCURACY = 6; - static final int ANIMATOR_ZOOM = 7; - static final int ANIMATOR_TILT = 8; - - private final int animatorType = provideAnimatorType(); - final List updateListeners; - private final K target; - - PluginAnimator(K previous, K target, List updateListeners) { - setObjectValues(previous, target); - setEvaluator(provideEvaluator()); - this.updateListeners = updateListeners; - this.target = target; - addUpdateListener(this); - } - - K getTarget() { - return target; - } - - @Type - int getAnimatorType() { - return animatorType; - } - - @Type - abstract int provideAnimatorType(); - - abstract TypeEvaluator provideEvaluator(); - - interface OnLayerAnimationsValuesChangeListener { - void onNewLatLngValue(LatLng latLng); - - void onNewGpsBearingValue(float gpsBearing); - - void onNewCompassBearingValue(float compassBearing); - - void onNewAccuracyRadiusValue(float accuracyRadiusValue); - } - - interface OnCameraAnimationsValuesChangeListener { - void onNewLatLngValue(LatLng latLng); - - void onNewGpsBearingValue(float gpsBearing); - - void onNewCompassBearingValue(float compassBearing); - - void onNewZoomValue(float zoom); - - void onNewTiltValue(float tilt); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginAnimatorCoordinator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginAnimatorCoordinator.java deleted file mode 100644 index 2ee87a6e46..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginAnimatorCoordinator.java +++ /dev/null @@ -1,375 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -import android.animation.Animator; -import android.animation.AnimatorSet; -import android.annotation.SuppressLint; -import android.location.Location; -import android.os.SystemClock; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.view.animation.LinearInterpolator; - -import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.maps.MapboxMap; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.ACCURACY_RADIUS_ANIMATION_DURATION; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.COMPASS_UPDATE_RATE_MS; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.MAX_ANIMATION_DURATION_MS; -import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.TRANSITION_ANIMATION_DURATION_MS; -import static com.mapbox.mapboxsdk.plugins.locationlayer.PluginAnimator.ANIMATOR_CAMERA_COMPASS_BEARING; -import static com.mapbox.mapboxsdk.plugins.locationlayer.PluginAnimator.ANIMATOR_CAMERA_GPS_BEARING; -import static com.mapbox.mapboxsdk.plugins.locationlayer.PluginAnimator.ANIMATOR_CAMERA_LATLNG; -import static com.mapbox.mapboxsdk.plugins.locationlayer.PluginAnimator.ANIMATOR_LAYER_ACCURACY; -import static com.mapbox.mapboxsdk.plugins.locationlayer.PluginAnimator.ANIMATOR_LAYER_COMPASS_BEARING; -import static com.mapbox.mapboxsdk.plugins.locationlayer.PluginAnimator.ANIMATOR_LAYER_GPS_BEARING; -import static com.mapbox.mapboxsdk.plugins.locationlayer.PluginAnimator.ANIMATOR_LAYER_LATLNG; -import static com.mapbox.mapboxsdk.plugins.locationlayer.PluginAnimator.ANIMATOR_TILT; -import static com.mapbox.mapboxsdk.plugins.locationlayer.PluginAnimator.ANIMATOR_ZOOM; - -final class PluginAnimatorCoordinator { - - @SuppressLint("UseSparseArrays") - final Map animatorMap = new HashMap<>(); - - final List layerListeners = new ArrayList<>(); - final List cameraListeners = new ArrayList<>(); - - private Location previousLocation; - private float previousAccuracyRadius = -1; - private float previousCompassBearing = -1; - private long locationUpdateTimestamp = -1; - - void addLayerListener(PluginAnimator.OnLayerAnimationsValuesChangeListener listener) { - layerListeners.add(listener); - } - - void removeLayerListener(PluginAnimator.OnLayerAnimationsValuesChangeListener listener) { - layerListeners.remove(listener); - } - - void addCameraListener(PluginAnimator.OnCameraAnimationsValuesChangeListener listener) { - cameraListeners.add(listener); - } - - void removeCameraListener(PluginAnimator.OnCameraAnimationsValuesChangeListener listener) { - cameraListeners.remove(listener); - } - - void feedNewLocation(@NonNull Location newLocation, @NonNull CameraPosition currentCameraPosition, - boolean isGpsNorth) { - if (previousLocation == null) { - previousLocation = newLocation; - locationUpdateTimestamp = SystemClock.elapsedRealtime() - TRANSITION_ANIMATION_DURATION_MS; - } - - LatLng previousLayerLatLng = getPreviousLayerLatLng(); - float previousLayerBearing = getPreviousLayerGpsBearing(); - LatLng previousCameraLatLng = currentCameraPosition.target; - float previousCameraBearing = (float) currentCameraPosition.bearing; - - LatLng targetLatLng = new LatLng(newLocation); - float targetLayerBearing = newLocation.getBearing(); - float targetCameraBearing = newLocation.getBearing(); - targetCameraBearing = checkGpsNorth(isGpsNorth, targetCameraBearing); - - updateLayerAnimators(previousLayerLatLng, targetLatLng, previousLayerBearing, targetLayerBearing); - updateCameraAnimators(previousCameraLatLng, previousCameraBearing, targetLatLng, targetCameraBearing); - - playLocationAnimators(getAnimationDuration()); - - previousLocation = newLocation; - } - - void feedNewCompassBearing(float targetCompassBearing, @NonNull CameraPosition currentCameraPosition) { - if (previousCompassBearing < 0) { - previousCompassBearing = targetCompassBearing; - } - - float previousLayerBearing = getPreviousLayerCompassBearing(); - float previousCameraBearing = (float) currentCameraPosition.bearing; - - updateCompassAnimators(targetCompassBearing, previousLayerBearing, previousCameraBearing); - playCompassAnimators(COMPASS_UPDATE_RATE_MS); - - previousCompassBearing = targetCompassBearing; - } - - void feedNewAccuracyRadius(float targetAccuracyRadius, boolean noAnimation) { - if (previousAccuracyRadius < 0) { - previousAccuracyRadius = targetAccuracyRadius; - } - - float previousAccuracyRadius = getPreviousAccuracyRadius(); - updateAccuracyAnimators(targetAccuracyRadius, previousAccuracyRadius); - playAccuracyAnimator(noAnimation ? 0 : ACCURACY_RADIUS_ANIMATION_DURATION); - - this.previousAccuracyRadius = targetAccuracyRadius; - } - - void feedNewZoomLevel(double targetZoomLevel, @NonNull CameraPosition currentCameraPosition, long animationDuration, - @Nullable MapboxMap.CancelableCallback callback) { - updateZoomAnimator((float) targetZoomLevel, (float) currentCameraPosition.zoom, callback); - playZoomAnimator(animationDuration); - } - - void feedNewTilt(double targetTilt, @NonNull CameraPosition currentCameraPosition, long animationDuration, - @Nullable MapboxMap.CancelableCallback callback) { - updateTiltAnimator((float) targetTilt, (float) currentCameraPosition.tilt, callback); - playTiltAnimator(animationDuration); - } - - private LatLng getPreviousLayerLatLng() { - LatLng previousLatLng; - PluginAnimator latLngAnimator = animatorMap.get(ANIMATOR_LAYER_LATLNG); - if (latLngAnimator != null) { - previousLatLng = (LatLng) latLngAnimator.getAnimatedValue(); - } else { - previousLatLng = new LatLng(previousLocation); - } - return previousLatLng; - } - - private float getPreviousLayerGpsBearing() { - LayerGpsBearingAnimator animator = (LayerGpsBearingAnimator) animatorMap.get(ANIMATOR_LAYER_GPS_BEARING); - float previousBearing; - if (animator != null) { - previousBearing = (float) animator.getAnimatedValue(); - } else { - previousBearing = previousLocation.getBearing(); - } - return previousBearing; - } - - private float getPreviousLayerCompassBearing() { - LayerCompassBearingAnimator animator = - (LayerCompassBearingAnimator) animatorMap.get(ANIMATOR_LAYER_COMPASS_BEARING); - - float previousBearing; - if (animator != null) { - previousBearing = (float) animator.getAnimatedValue(); - } else { - previousBearing = previousCompassBearing; - } - return previousBearing; - } - - private float getPreviousAccuracyRadius() { - LayerAccuracyAnimator animator = (LayerAccuracyAnimator) animatorMap.get(ANIMATOR_LAYER_ACCURACY); - float previousRadius; - if (animator != null) { - previousRadius = (float) animator.getAnimatedValue(); - } else { - previousRadius = previousAccuracyRadius; - } - return previousRadius; - } - - private void updateLayerAnimators(LatLng previousLatLng, LatLng targetLatLng, - float previousBearing, float targetBearing) { - createNewAnimator(ANIMATOR_LAYER_LATLNG, new LayerLatLngAnimator(previousLatLng, targetLatLng, layerListeners)); - - float normalizedLayerBearing = Utils.shortestRotation(targetBearing, previousBearing); - createNewAnimator(ANIMATOR_LAYER_GPS_BEARING, - new LayerGpsBearingAnimator(previousBearing, normalizedLayerBearing, layerListeners)); - } - - private void updateCameraAnimators(LatLng previousCameraLatLng, float previousCameraBearing, - LatLng targetLatLng, float targetBearing) { - createNewAnimator(ANIMATOR_CAMERA_LATLNG, - new CameraLatLngAnimator(previousCameraLatLng, targetLatLng, cameraListeners)); - - float normalizedCameraBearing = Utils.shortestRotation(targetBearing, previousCameraBearing); - createNewAnimator(ANIMATOR_CAMERA_GPS_BEARING, - new CameraGpsBearingAnimator(previousCameraBearing, normalizedCameraBearing, cameraListeners)); - } - - private void updateCompassAnimators(float targetCompassBearing, float previousLayerBearing, - float previousCameraBearing) { - float normalizedLayerBearing = Utils.shortestRotation(targetCompassBearing, previousLayerBearing); - createNewAnimator(ANIMATOR_LAYER_COMPASS_BEARING, - new LayerCompassBearingAnimator(previousLayerBearing, normalizedLayerBearing, layerListeners)); - - float normalizedCameraBearing = Utils.shortestRotation(targetCompassBearing, previousCameraBearing); - createNewAnimator(ANIMATOR_CAMERA_COMPASS_BEARING, - new CameraCompassBearingAnimator(previousCameraBearing, normalizedCameraBearing, cameraListeners)); - } - - private void updateAccuracyAnimators(float targetAccuracyRadius, float previousAccuracyRadius) { - createNewAnimator(ANIMATOR_LAYER_ACCURACY, - new LayerAccuracyAnimator(previousAccuracyRadius, targetAccuracyRadius, layerListeners)); - } - - private void updateZoomAnimator(float targetZoomLevel, float previousZoomLevel, - @Nullable MapboxMap.CancelableCallback cancelableCallback) { - createNewAnimator(ANIMATOR_ZOOM, - new ZoomAnimator(previousZoomLevel, targetZoomLevel, cameraListeners, cancelableCallback)); - } - - private void updateTiltAnimator(float targetTilt, float previousTiltLevel, - @Nullable MapboxMap.CancelableCallback cancelableCallback) { - createNewAnimator(ANIMATOR_TILT, - new TiltAnimator(previousTiltLevel, targetTilt, cameraListeners, cancelableCallback)); - } - - private long getAnimationDuration() { - long previousUpdateTimeStamp = locationUpdateTimestamp; - locationUpdateTimestamp = SystemClock.elapsedRealtime(); - - long animationDuration; - if (previousUpdateTimeStamp == 0) { - animationDuration = 0; - } else { - animationDuration = (long) ((locationUpdateTimestamp - previousUpdateTimeStamp) * 1.1f) - /*make animation slightly longer*/; - } - - animationDuration = Math.min(animationDuration, MAX_ANIMATION_DURATION_MS); - - return animationDuration; - } - - private float checkGpsNorth(boolean isGpsNorth, float targetCameraBearing) { - if (isGpsNorth) { - targetCameraBearing = 0; - } - return targetCameraBearing; - } - - private void playLocationAnimators(long duration) { - List locationAnimators = new ArrayList<>(); - locationAnimators.add(animatorMap.get(ANIMATOR_LAYER_LATLNG)); - locationAnimators.add(animatorMap.get(ANIMATOR_LAYER_GPS_BEARING)); - locationAnimators.add(animatorMap.get(ANIMATOR_CAMERA_LATLNG)); - locationAnimators.add(animatorMap.get(ANIMATOR_CAMERA_GPS_BEARING)); - AnimatorSet locationAnimatorSet = new AnimatorSet(); - locationAnimatorSet.playTogether(locationAnimators); - locationAnimatorSet.setInterpolator(new LinearInterpolator()); - locationAnimatorSet.setDuration(duration); - locationAnimatorSet.start(); - } - - private void playCompassAnimators(long duration) { - List compassAnimators = new ArrayList<>(); - compassAnimators.add(animatorMap.get(ANIMATOR_LAYER_COMPASS_BEARING)); - compassAnimators.add(animatorMap.get(ANIMATOR_CAMERA_COMPASS_BEARING)); - AnimatorSet compassAnimatorSet = new AnimatorSet(); - compassAnimatorSet.playTogether(compassAnimators); - compassAnimatorSet.setDuration(duration); - compassAnimatorSet.start(); - } - - private void playAccuracyAnimator(long duration) { - PluginAnimator animator = animatorMap.get(ANIMATOR_LAYER_ACCURACY); - animator.setDuration(duration); - animator.start(); - } - - private void playZoomAnimator(long duration) { - PluginAnimator animator = animatorMap.get(ANIMATOR_ZOOM); - animator.setDuration(duration); - animator.start(); - } - - private void playTiltAnimator(long duration) { - PluginAnimator animator = animatorMap.get(ANIMATOR_TILT); - animator.setDuration(duration); - animator.start(); - } - - private void playCameraLocationAnimators(long duration) { - List locationAnimators = new ArrayList<>(); - locationAnimators.add(animatorMap.get(ANIMATOR_CAMERA_LATLNG)); - locationAnimators.add(animatorMap.get(ANIMATOR_CAMERA_GPS_BEARING)); - AnimatorSet locationAnimatorSet = new AnimatorSet(); - locationAnimatorSet.playTogether(locationAnimators); - locationAnimatorSet.setInterpolator(new LinearInterpolator()); - locationAnimatorSet.setDuration(duration); - locationAnimatorSet.start(); - } - - void resetAllCameraAnimations(CameraPosition currentCameraPosition, boolean isGpsNorth) { - resetCameraCompassAnimation(currentCameraPosition); - resetCameraLocationAnimations(currentCameraPosition, isGpsNorth); - playCameraLocationAnimators(TRANSITION_ANIMATION_DURATION_MS); - } - - private void resetCameraLocationAnimations(CameraPosition currentCameraPosition, boolean isGpsNorth) { - resetCameraLatLngAnimation(currentCameraPosition); - resetCameraGpsBearingAnimation(currentCameraPosition, isGpsNorth); - } - - private void resetCameraLatLngAnimation(CameraPosition currentCameraPosition) { - CameraLatLngAnimator animator = (CameraLatLngAnimator) animatorMap.get(ANIMATOR_CAMERA_LATLNG); - if (animator == null) { - return; - } - - LatLng currentTarget = animator.getTarget(); - LatLng previousCameraTarget = currentCameraPosition.target; - createNewAnimator(ANIMATOR_CAMERA_LATLNG, - new CameraLatLngAnimator(previousCameraTarget, currentTarget, cameraListeners)); - } - - private void resetCameraGpsBearingAnimation(CameraPosition currentCameraPosition, boolean isGpsNorth) { - CameraGpsBearingAnimator animator = (CameraGpsBearingAnimator) animatorMap.get(ANIMATOR_CAMERA_GPS_BEARING); - if (animator == null) { - return; - } - - float currentTargetBearing = animator.getTarget(); - currentTargetBearing = checkGpsNorth(isGpsNorth, currentTargetBearing); - float previousCameraBearing = (float) currentCameraPosition.bearing; - float normalizedCameraBearing = Utils.shortestRotation(currentTargetBearing, previousCameraBearing); - createNewAnimator(ANIMATOR_CAMERA_GPS_BEARING, - new CameraGpsBearingAnimator(previousCameraBearing, normalizedCameraBearing, cameraListeners)); - } - - private void resetCameraCompassAnimation(CameraPosition currentCameraPosition) { - CameraCompassBearingAnimator animator = - (CameraCompassBearingAnimator) animatorMap.get(ANIMATOR_CAMERA_COMPASS_BEARING); - if (animator == null) { - return; - } - - float currentTargetBearing = animator.getTarget(); - float previousCameraBearing = (float) currentCameraPosition.bearing; - float normalizedCameraBearing = Utils.shortestRotation(currentTargetBearing, previousCameraBearing); - createNewAnimator(ANIMATOR_CAMERA_COMPASS_BEARING, - new CameraCompassBearingAnimator(previousCameraBearing, normalizedCameraBearing, cameraListeners)); - } - - private void createNewAnimator(@PluginAnimator.Type int animatorType, PluginAnimator animator) { - cancelAnimator(animatorType); - animatorMap.put(animatorType, animator); - } - - void cancelZoomAnimation() { - cancelAnimator(ANIMATOR_ZOOM); - } - - void cancelTiltAnimation() { - cancelAnimator(ANIMATOR_TILT); - } - - void cancelAllAnimations() { - for (@PluginAnimator.Type int animatorType : animatorMap.keySet()) { - cancelAnimator(animatorType); - } - } - - private void cancelAnimator(@PluginAnimator.Type int animatorType) { - PluginAnimator animator = animatorMap.get(animatorType); - if (animator != null) { - animator.cancel(); - animator.removeAllUpdateListeners(); - animator.removeAllListeners(); - animatorMap.put(animatorType, null); - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginFloatAnimator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginFloatAnimator.java deleted file mode 100644 index f99f17cd97..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginFloatAnimator.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -import android.animation.FloatEvaluator; -import android.animation.TypeEvaluator; - -import java.util.List; - -abstract class PluginFloatAnimator extends PluginAnimator { - PluginFloatAnimator(Float previous, Float target, List updateListeners) { - super(previous, target, updateListeners); - } - - @Override - TypeEvaluator provideEvaluator() { - return new FloatEvaluator(); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginLatLngAnimator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginLatLngAnimator.java deleted file mode 100644 index 29d73f8db7..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginLatLngAnimator.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -import android.animation.TypeEvaluator; - -import com.mapbox.mapboxsdk.geometry.LatLng; - -import java.util.List; - -abstract class PluginLatLngAnimator extends PluginAnimator { - - PluginLatLngAnimator(LatLng previous, LatLng target, List updateListeners) { - super(previous, target, updateListeners); - } - - @Override - TypeEvaluator provideEvaluator() { - return new LatLngEvaluator(); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/StaleStateManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/StaleStateManager.java deleted file mode 100644 index 9b6afd2d93..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/StaleStateManager.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -import android.os.Handler; - -/** - * Class controls the location layer stale state when the {@link android.location.Location} hasn't - * been updated in 'x' amount of time. {@link LocationLayerOptions#staleStateTimeout()} can be used to - * control the amount of time before the locations considered stale. - * {@link LocationLayerOptions#enableStaleState()} is available for disabling this behaviour. - */ -class StaleStateManager { - - private boolean isEnabled; - private final OnLocationStaleListener innerOnLocationStaleListeners; - private final Handler handler; - private boolean isStale = true; - private long delayTime; - - StaleStateManager(OnLocationStaleListener innerListener, LocationLayerOptions options) { - innerOnLocationStaleListeners = innerListener; - handler = new Handler(); - isEnabled = options.enableStaleState(); - delayTime = options.staleStateTimeout(); - } - - private Runnable staleStateRunnable = new Runnable() { - @Override - public void run() { - setState(true); - } - }; - - void setEnabled(boolean enabled) { - if (enabled) { - setState(isStale); - } else if (isEnabled) { - onStop(); - innerOnLocationStaleListeners.onStaleStateChange(false); - } - isEnabled = enabled; - } - - boolean isStale() { - return isStale; - } - - void updateLatestLocationTime() { - setState(false); - postTheCallback(); - } - - void setDelayTime(long delayTime) { - this.delayTime = delayTime; - postTheCallback(); - } - - void onStart() { - if (!isStale) { - postTheCallback(); - } - } - - void onStop() { - handler.removeCallbacksAndMessages(null); - } - - private void postTheCallback() { - handler.removeCallbacksAndMessages(null); - handler.postDelayed(staleStateRunnable, delayTime); - } - - private void setState(boolean stale) { - if (stale != isStale) { - isStale = stale; - if (isEnabled) { - innerOnLocationStaleListeners.onStaleStateChange(stale); - } - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/TiltAnimator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/TiltAnimator.java deleted file mode 100644 index 2a12622df5..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/TiltAnimator.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -import android.animation.ValueAnimator; -import android.support.annotation.Nullable; - -import com.mapbox.mapboxsdk.maps.MapboxMap; - -import java.util.List; - -class TiltAnimator extends MapboxCameraAnimatorAdapter { - TiltAnimator(Float previous, Float target, List updateListeners, - @Nullable MapboxMap.CancelableCallback cancelableCallback) { - super(previous, target, updateListeners, cancelableCallback); - } - - @Override - int provideAnimatorType() { - return ANIMATOR_TILT; - } - - @Override - public void onAnimationUpdate(ValueAnimator animation) { - for (OnCameraAnimationsValuesChangeListener listener : updateListeners) { - listener.onNewTiltValue((Float) animation.getAnimatedValue()); - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/Utils.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/Utils.java deleted file mode 100644 index 6a0bd94285..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/Utils.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.PorterDuff; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.location.Location; -import android.os.Build; -import android.support.annotation.ColorInt; -import android.support.annotation.DrawableRes; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.content.ContextCompat; - -import com.mapbox.mapboxsdk.maps.MapboxMap; - -public final class Utils { - - private Utils() { - // Class should not be initialized - } - - /** - * Util for finding the shortest path from the current icon rotated degree to the new degree. - * - * @param magneticHeading the new position of the rotation - * @param previousMagneticHeading the current position of the rotation - * @return the shortest degree of rotation possible - */ - public static float shortestRotation(float magneticHeading, float previousMagneticHeading) { - double diff = previousMagneticHeading - magneticHeading; - if (diff > 180.0f) { - magneticHeading += 360.0f; - } else if (diff < -180.0f) { - magneticHeading -= 360.f; - } - return magneticHeading; - } - - static Bitmap getBitmapFromDrawable(Drawable drawable) { - if (drawable instanceof BitmapDrawable) { - return ((BitmapDrawable) drawable).getBitmap(); - } else { - // width and height are equal for all assets since they are ovals. - Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), - drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); - drawable.draw(canvas); - return bitmap; - } - } - - static Bitmap generateShadow(Drawable drawable, float elevation) { - int width = drawable.getIntrinsicWidth(); - int height = drawable.getIntrinsicHeight(); - Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); - drawable.draw(canvas); - bitmap = Bitmap.createScaledBitmap(bitmap, - toEven(width + elevation), toEven(height + elevation), false); - return bitmap; - } - - static Drawable getDrawable(@NonNull Context context, @DrawableRes int drawableRes, - @ColorInt Integer tintColor) { - Drawable drawable = ContextCompat.getDrawable(context, drawableRes); - if (tintColor == null) { - return drawable; - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - drawable.setTint(tintColor); - } else { - drawable.mutate().setColorFilter(tintColor, PorterDuff.Mode.SRC_IN); - } - return drawable; - } - - static float calculateZoomLevelRadius(@NonNull MapboxMap mapboxMap, @Nullable Location location) { - if (location == null) { - return 0; - } - double metersPerPixel = mapboxMap.getProjection().getMetersPerPixelAtLatitude( - location.getLatitude()); - return (float) (location.getAccuracy() * (1 / metersPerPixel)); - } - - /** - * Casts the value to an even integer. - */ - private static int toEven(float value) { - int i = (int) (value + .5f); - if (i % 2 == 1) { - return i - 1; - } - return i; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/ZoomAnimator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/ZoomAnimator.java deleted file mode 100644 index a1b1d4bf81..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/ZoomAnimator.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -import android.animation.ValueAnimator; -import android.support.annotation.Nullable; - -import com.mapbox.mapboxsdk.maps.MapboxMap; - -import java.util.List; - -class ZoomAnimator extends MapboxCameraAnimatorAdapter { - - ZoomAnimator(Float previous, Float target, List updateListeners, - @Nullable MapboxMap.CancelableCallback cancelableCallback) { - super(previous, target, updateListeners, cancelableCallback); - } - - @Override - int provideAnimatorType() { - return ANIMATOR_ZOOM; - } - - @Override - public void onAnimationUpdate(ValueAnimator animation) { - for (OnCameraAnimationsValuesChangeListener listener : updateListeners) { - listener.onNewZoomValue((Float) animation.getAnimatedValue()); - } - } - -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/modes/CameraMode.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/modes/CameraMode.java deleted file mode 100644 index 5edffd00bd..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/modes/CameraMode.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer.modes; - -import android.location.Location; -import android.support.annotation.IntDef; - -import com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerPlugin; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Contains the variety of camera modes which determine how the camera will track - * the user location. - */ -public final class CameraMode { - - private CameraMode() { - // Class should not be initialized - } - - /** - * Determine the camera tracking behavior in the {@link LocationLayerPlugin}. - */ - @IntDef( {NONE, NONE_COMPASS, NONE_GPS, TRACKING, TRACKING_COMPASS, TRACKING_GPS, TRACKING_GPS_NORTH}) - @Retention(RetentionPolicy.SOURCE) - public @interface Mode { - } - - /** - * No camera tracking. - */ - public static final int NONE = 0x00000008; - - /** - * Camera does not track location, but does track compass bearing. - */ - public static final int NONE_COMPASS = 0x00000010; - - /** - * Camera does not track location, but does track GPS {@link Location} bearing. - */ - public static final int NONE_GPS = 0x00000016; - - /** - * Camera tracks the user location. - */ - public static final int TRACKING = 0x00000018; - - /** - * Camera tracks the user location, with bearing - * provided by a compass. - */ - public static final int TRACKING_COMPASS = 0x00000020; - - /** - * Camera tracks the user location, with bearing - * provided by a normalized {@link Location#getBearing()}. - */ - public static final int TRACKING_GPS = 0x00000022; - - /** - * Camera tracks the user location, with bearing - * always set to north (0). - */ - public static final int TRACKING_GPS_NORTH = 0x00000024; -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/modes/RenderMode.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/modes/RenderMode.java deleted file mode 100644 index 88db25beb7..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/modes/RenderMode.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer.modes; - -import android.support.annotation.IntDef; - -import com.mapbox.mapboxsdk.plugins.locationlayer.CompassEngine; -import com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerPlugin; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Contains the variety of ways the user location can be rendered on the map. - */ -public final class RenderMode { - - private RenderMode() { - // Class should not be initialized - } - - /** - * One of these constants should be used with {@link LocationLayerPlugin#setRenderMode(int)}. - * mode can be switched at anytime by calling the {@code setLocationLayerMode} method passing - * in the new mode you'd like the location layer to be in. - */ - @IntDef( {COMPASS, GPS, NORMAL}) - @Retention(RetentionPolicy.SOURCE) - public @interface Mode { - } - - /** - * Basic tracking is enabled, bearing ignored. - */ - public static final int NORMAL = 0x00000012; - - /** - * Tracking the user location with bearing considered - * from a {@link CompassEngine}. - */ - public static final int COMPASS = 0x00000004; - - /** - * Tracking the user location with bearing considered from {@link android.location.Location}. - */ - public static final int GPS = 0x00000008; -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/package-info.java deleted file mode 100644 index 0dc3ea41e5..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Contains the Mapbox Location layer plugin. - */ -package com.mapbox.mapboxsdk.plugins.locationlayer; \ No newline at end of file -- cgit v1.2.1