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 -
.../src/main/res-public/values/public.xml | 2 +-
.../src/main/res/values/attrs.xml | 2 +-
.../src/main/res/values/dimens.xml | 4 +-
.../src/main/res/values/styles.xml | 10 +-
.../mapboxsdk/location/CompassEngineTest.java | 64 +
.../location/LocationCameraControllerTest.java | 337 +++++
.../location/LocationComponentOptionsTest.java | 68 +
.../location/LocationLayerControllerTest.java | 466 ++++++
.../location/MapboxAnimatorCoordinatorTest.kt | 284 ++++
.../com/mapbox/mapboxsdk/location/UtilsTest.java | 28 +
.../plugins/locationlayer/CompassEngineTest.java | 64 -
.../locationlayer/LocationLayerCameraTest.java | 337 -----
.../locationlayer/LocationLayerOptionsTest.java | 68 -
.../plugins/locationlayer/LocationLayerTest.java | 466 ------
.../locationlayer/PluginAnimatorCoordinatorTest.kt | 284 ----
.../mapboxsdk/plugins/locationlayer/UtilsTest.java | 29 -
.../mapboxsdk/location/LocationComponentTest.kt | 1104 ++++++++++++++
.../location/LocationLayerControllerTest.kt | 360 +++++
.../location/utils/LocationComponentAction.kt | 40 +
.../mapboxsdk/location/utils/MapboxTestingUtils.kt | 105 ++
.../utils/OnMapFragmentReadyIdlingResource.kt | 39 +
.../location/utils/OnMapReadyIdlingResource.java | 63 +
.../location/utils/StyleChangeIdlingResource.kt | 46 +
.../locationlayer/LocationLayerPluginTest.kt | 1104 --------------
.../plugins/locationlayer/LocationLayerTest.kt | 360 -----
.../plugins/utils/LocationLayerPluginAction.kt | 40 -
.../mapboxsdk/plugins/utils/MapboxTestingUtils.kt | 105 --
.../utils/OnMapFragmentReadyIdlingResource.kt | 39 -
.../plugins/utils/OnMapReadyIdlingResource.java | 63 -
.../plugins/utils/StyleChangeIdlingResource.kt | 46 -
.../src/main/AndroidManifest.xml | 6 +-
.../activity/location/LocationFragmentActivity.kt | 171 +++
.../location/LocationLayerFragmentActivity.kt | 171 ---
.../location/LocationLayerMapChangeActivity.java | 131 --
.../location/LocationLayerModesActivity.java | 383 -----
.../location/LocationMapChangeActivity.java | 131 ++
.../activity/location/LocationModesActivity.java | 383 +++++
.../location/ManualLocationUpdatesActivity.java | 22 +-
.../src/main/res/menu/menu_location_mode.xml | 4 +-
.../src/main/res/values/descriptions.xml | 2 +-
.../src/main/res/values/styles.xml | 2 +-
115 files changed, 8709 insertions(+), 8709 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
create mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/CompassEngineTest.java
create mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationCameraControllerTest.java
create mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentOptionsTest.java
create mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationLayerControllerTest.java
create mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/MapboxAnimatorCoordinatorTest.kt
create mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/UtilsTest.java
delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/plugins/locationlayer/CompassEngineTest.java
delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerCameraTest.java
delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerOptionsTest.java
delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerTest.java
delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginAnimatorCoordinatorTest.kt
delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/plugins/locationlayer/UtilsTest.java
create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/LocationComponentTest.kt
create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/LocationLayerControllerTest.kt
create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/utils/LocationComponentAction.kt
create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/utils/MapboxTestingUtils.kt
create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/utils/OnMapFragmentReadyIdlingResource.kt
create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/utils/OnMapReadyIdlingResource.java
create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/utils/StyleChangeIdlingResource.kt
delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerPluginTest.kt
delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerTest.kt
delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/plugins/utils/LocationLayerPluginAction.kt
delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/plugins/utils/MapboxTestingUtils.kt
delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/plugins/utils/OnMapFragmentReadyIdlingResource.kt
delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/plugins/utils/OnMapReadyIdlingResource.java
delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/plugins/utils/StyleChangeIdlingResource.kt
create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationFragmentActivity.kt
delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationLayerFragmentActivity.kt
delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationLayerMapChangeActivity.java
delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationLayerModesActivity.java
create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationMapChangeActivity.java
create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationModesActivity.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
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml b/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml
index 789ba73f1a..b6f6abeba2 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml
@@ -108,7 +108,7 @@
-
+
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml
index 9206899dd6..62cc066bd6 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml
@@ -129,7 +129,7 @@
-
+
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml
index 9bf77d46d9..453079d195 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml
@@ -16,6 +16,6 @@
0.025dp
- 25dp
- 125dp
+ 25dp
+ 125dp
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/styles.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/styles.xml
index 40bf1540ab..ac29065f24 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/styles.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/styles.xml
@@ -1,7 +1,7 @@
-
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/CompassEngineTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/CompassEngineTest.java
new file mode 100644
index 0000000000..bc64379263
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/CompassEngineTest.java
@@ -0,0 +1,64 @@
+package com.mapbox.mapboxsdk.location;
+
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.view.WindowManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import static junit.framework.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class CompassEngineTest {
+
+ private LocationComponentCompassEngine compassEngine;
+
+ @Mock
+ private WindowManager windowManager;
+
+ @Mock
+ private SensorManager sensorManager;
+
+ @Before
+ public void setUp() throws Exception {
+ compassEngine = new LocationComponentCompassEngine(windowManager, sensorManager);
+ }
+
+ @Test
+ public void lastKnownCompassBearingAccuracyDefault() {
+ assertEquals("Last accuracy should match", compassEngine.getLastAccuracySensorStatus(), 0);
+ }
+
+ @Test
+ public void lastKnownCompassAccuracyStatusValue() {
+ Sensor sensor = mock(Sensor.class);
+ compassEngine.onAccuracyChanged(sensor, 2);
+ assertEquals("Last accuracy should match", compassEngine.getLastAccuracySensorStatus(), 2);
+ }
+
+ @Test
+ public void whenGyroscopeIsNull_fallbackToGravity() {
+ SensorManager sensorManager = mock(SensorManager.class);
+ when(sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)).thenReturn(null);
+ new LocationComponentCompassEngine(windowManager, sensorManager);
+
+ verify(sensorManager, times(1)).getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+ }
+
+ @Test
+ public void whenGyroscopeIsNull_fallbackToMagneticField() {
+ SensorManager sensorManager = mock(SensorManager.class);
+ when(sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)).thenReturn(null);
+ new LocationComponentCompassEngine(windowManager, sensorManager);
+
+ verify(sensorManager, times(1)).getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationCameraControllerTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationCameraControllerTest.java
new file mode 100644
index 0000000000..de0d67613e
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationCameraControllerTest.java
@@ -0,0 +1,337 @@
+package com.mapbox.mapboxsdk.location;
+
+import android.graphics.PointF;
+
+import com.mapbox.android.gestures.MoveGestureDetector;
+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.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.Projection;
+import com.mapbox.mapboxsdk.maps.UiSettings;
+
+import org.junit.Test;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class LocationCameraControllerTest {
+
+ @Test
+ public void setCameraMode_mapTransitionsAreCancelled() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ LocationCameraController camera = buildCamera(mapboxMap);
+ camera.initializeOptions(mock(LocationComponentOptions.class));
+
+ camera.setCameraMode(CameraMode.TRACKING_GPS);
+
+ verify(mapboxMap).cancelTransitions();
+ }
+
+ @Test
+ public void setCameraMode_gestureThresholdIsAdjusted() {
+ MoveGestureDetector moveGestureDetector = mock(MoveGestureDetector.class);
+ LocationCameraController camera = buildCamera(moveGestureDetector);
+ LocationComponentOptions options = mock(LocationComponentOptions.class);
+ float moveThreshold = 5f;
+ when(options.trackingInitialMoveThreshold()).thenReturn(moveThreshold);
+ camera.initializeOptions(options);
+
+ camera.setCameraMode(CameraMode.TRACKING_GPS);
+
+ verify(moveGestureDetector).setMoveThreshold(moveThreshold);
+ }
+
+ @Test
+ public void setCameraMode_gestureThresholdIsResetWhenNotTracking() {
+ MoveGestureDetector moveGestureDetector = mock(MoveGestureDetector.class);
+ LocationCameraController camera = buildCamera(moveGestureDetector);
+ camera.initializeOptions(mock(LocationComponentOptions.class));
+
+ camera.setCameraMode(CameraMode.NONE);
+
+ verify(moveGestureDetector).setMoveThreshold(0f);
+ }
+
+ @Test
+ public void setCameraMode_notTrackingAdjustsFocalPoint() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ when(mapboxMap.getUiSettings()).thenReturn(mock(UiSettings.class));
+ LocationCameraController camera = buildCamera(mapboxMap);
+ camera.initializeOptions(mock(LocationComponentOptions.class));
+
+ camera.setCameraMode(CameraMode.TRACKING_GPS);
+ camera.setCameraMode(CameraMode.NONE);
+
+ verify(mapboxMap.getUiSettings()).setFocalPoint(null);
+ }
+
+ @Test
+ public void setCameraMode_trackingChangeListenerCameraDismissedIsCalled() {
+ OnCameraTrackingChangedListener internalTrackingChangedListener = mock(OnCameraTrackingChangedListener.class);
+ LocationCameraController camera = buildCamera(internalTrackingChangedListener);
+ camera.initializeOptions(mock(LocationComponentOptions.class));
+
+ camera.setCameraMode(CameraMode.TRACKING_GPS);
+ camera.setCameraMode(CameraMode.NONE);
+
+ verify(internalTrackingChangedListener).onCameraTrackingDismissed();
+ }
+
+ @Test
+ public void setCameraMode_internalCameraTrackingChangeListenerIsCalled() {
+ OnCameraTrackingChangedListener internalTrackingChangedListener = mock(OnCameraTrackingChangedListener.class);
+ LocationCameraController camera = buildCamera(internalTrackingChangedListener);
+ camera.initializeOptions(mock(LocationComponentOptions.class));
+ int cameraMode = CameraMode.NONE;
+
+ camera.setCameraMode(cameraMode);
+
+ verify(internalTrackingChangedListener).onCameraTrackingChanged(cameraMode);
+ }
+
+ @Test
+ public void onNewLatLngValue_cameraModeTrackingUpdatesLatLng() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ when(mapboxMap.getUiSettings()).thenReturn(mock(UiSettings.class));
+ when(mapboxMap.getProjection()).thenReturn(mock(Projection.class));
+ LocationCameraController camera = buildCamera(mapboxMap);
+ camera.initializeOptions(mock(LocationComponentOptions.class));
+ camera.setCameraMode(CameraMode.TRACKING);
+ LatLng latLng = mock(LatLng.class);
+
+ camera.onNewLatLngValue(latLng);
+
+ verify(mapboxMap).moveCamera(any(CameraUpdate.class));
+ }
+
+ @Test
+ public void onNewLatLngValue_cameraModeTrackingGpsNorthUpdatesLatLng() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ when(mapboxMap.getUiSettings()).thenReturn(mock(UiSettings.class));
+ when(mapboxMap.getProjection()).thenReturn(mock(Projection.class));
+ LocationCameraController camera = buildCamera(mapboxMap);
+ camera.initializeOptions(mock(LocationComponentOptions.class));
+ camera.setCameraMode(CameraMode.TRACKING_GPS_NORTH);
+ LatLng latLng = mock(LatLng.class);
+
+ camera.onNewLatLngValue(latLng);
+
+ verify(mapboxMap).moveCamera(any(CameraUpdate.class));
+ }
+
+ @Test
+ public void onNewLatLngValue_cameraModeTrackingGpsUpdatesLatLng() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ when(mapboxMap.getUiSettings()).thenReturn(mock(UiSettings.class));
+ when(mapboxMap.getProjection()).thenReturn(mock(Projection.class));
+ LocationCameraController camera = buildCamera(mapboxMap);
+ camera.initializeOptions(mock(LocationComponentOptions.class));
+ camera.setCameraMode(CameraMode.TRACKING_GPS);
+ LatLng latLng = mock(LatLng.class);
+
+ camera.onNewLatLngValue(latLng);
+
+ verify(mapboxMap).moveCamera(any(CameraUpdate.class));
+ }
+
+ @Test
+ public void onNewLatLngValue_cameraModeTrackingCompassUpdatesLatLng() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ when(mapboxMap.getUiSettings()).thenReturn(mock(UiSettings.class));
+ when(mapboxMap.getProjection()).thenReturn(mock(Projection.class));
+ LocationCameraController camera = buildCamera(mapboxMap);
+ camera.initializeOptions(mock(LocationComponentOptions.class));
+ camera.setCameraMode(CameraMode.TRACKING_COMPASS);
+ LatLng latLng = mock(LatLng.class);
+
+ camera.onNewLatLngValue(latLng);
+
+ verify(mapboxMap).moveCamera(any(CameraUpdate.class));
+ }
+
+ @Test
+ public void onNewLatLngValue_cameraModeNoneIgnored() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ when(mapboxMap.getUiSettings()).thenReturn(mock(UiSettings.class));
+ when(mapboxMap.getProjection()).thenReturn(mock(Projection.class));
+ LocationCameraController camera = buildCamera(mapboxMap);
+ camera.initializeOptions(mock(LocationComponentOptions.class));
+ camera.setCameraMode(CameraMode.NONE);
+ LatLng latLng = mock(LatLng.class);
+
+ camera.onNewLatLngValue(latLng);
+
+ verify(mapboxMap, times(0)).moveCamera(any(CameraUpdate.class));
+ }
+
+ @Test
+ public void onNewLatLngValue_focalPointIsAdjusted() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ UiSettings uiSettings = mock(UiSettings.class);
+ when(mapboxMap.getUiSettings()).thenReturn(uiSettings);
+ Projection projection = mock(Projection.class);
+ PointF pointF = mock(PointF.class);
+ when(projection.toScreenLocation(any(LatLng.class))).thenReturn(pointF);
+ when(mapboxMap.getProjection()).thenReturn(projection);
+ LocationCameraController camera = buildCamera(mapboxMap);
+ camera.initializeOptions(mock(LocationComponentOptions.class));
+ camera.setCameraMode(CameraMode.TRACKING);
+ LatLng latLng = mock(LatLng.class);
+
+ camera.onNewLatLngValue(latLng);
+
+ verify(uiSettings).setFocalPoint(pointF);
+ }
+
+ @Test
+ public void onNewGpsBearingValue_cameraModeTrackingGpsUpdatesBearing() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ LocationCameraController camera = buildCamera(mapboxMap);
+ camera.initializeOptions(mock(LocationComponentOptions.class));
+ camera.setCameraMode(CameraMode.TRACKING_GPS);
+ float gpsBearing = 5f;
+
+ camera.onNewGpsBearingValue(gpsBearing);
+
+ verify(mapboxMap).moveCamera(any(CameraUpdate.class));
+ }
+
+ @Test
+ public void onNewGpsBearingValue_cameraModeNoneGpsUpdatesBearing() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ LocationCameraController camera = buildCamera(mapboxMap);
+ camera.initializeOptions(mock(LocationComponentOptions.class));
+ camera.setCameraMode(CameraMode.NONE_GPS);
+ float gpsBearing = 5f;
+
+ camera.onNewGpsBearingValue(gpsBearing);
+
+ verify(mapboxMap).moveCamera(any(CameraUpdate.class));
+ }
+
+ @Test
+ public void onNewGpsBearingValue_cameraModeTrackingNorthUpdatesBearing() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ CameraPosition cameraPosition = new CameraPosition.Builder().bearing(7d).build();
+ when(mapboxMap.getCameraPosition()).thenReturn(cameraPosition);
+ LocationCameraController camera = buildCamera(mapboxMap);
+ camera.initializeOptions(mock(LocationComponentOptions.class));
+ camera.setCameraMode(CameraMode.TRACKING_GPS_NORTH);
+ float gpsBearing = 5f;
+
+ camera.onNewGpsBearingValue(gpsBearing);
+
+ verify(mapboxMap).moveCamera(any(CameraUpdate.class));
+ }
+
+ @Test
+ public void onNewGpsBearingValue_cameraModeTrackingNorthBearingZeroIgnored() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ CameraPosition cameraPosition = new CameraPosition.Builder().bearing(0d).build();
+ when(mapboxMap.getCameraPosition()).thenReturn(cameraPosition);
+ LocationCameraController camera = buildCamera(mapboxMap);
+ camera.initializeOptions(mock(LocationComponentOptions.class));
+ camera.setCameraMode(CameraMode.TRACKING_GPS_NORTH);
+ float gpsBearing = 5f;
+
+ camera.onNewGpsBearingValue(gpsBearing);
+
+ verify(mapboxMap, times(0)).moveCamera(any(CameraUpdate.class));
+ }
+
+ @Test
+ public void onNewGpsBearingValue_cameraModeNoneIgnored() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ LocationCameraController camera = buildCamera(mapboxMap);
+ camera.initializeOptions(mock(LocationComponentOptions.class));
+ camera.setCameraMode(CameraMode.NONE);
+ float gpsBearing = 5f;
+
+ camera.onNewGpsBearingValue(gpsBearing);
+
+ verify(mapboxMap, times(0)).moveCamera(any(CameraUpdate.class));
+ }
+
+ @Test
+ public void onNewCompassBearingValue_cameraModeTrackingCompassUpdatesBearing() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ LocationCameraController camera = buildCamera(mapboxMap);
+ camera.initializeOptions(mock(LocationComponentOptions.class));
+ camera.setCameraMode(CameraMode.TRACKING_COMPASS);
+ float compassBearing = 5f;
+
+ camera.onNewCompassBearingValue(compassBearing);
+
+ verify(mapboxMap).moveCamera(any(CameraUpdate.class));
+ }
+
+ @Test
+ public void onNewCompassBearingValue_cameraModeNoneCompassUpdatesBearing() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ LocationCameraController camera = buildCamera(mapboxMap);
+ camera.initializeOptions(mock(LocationComponentOptions.class));
+ camera.setCameraMode(CameraMode.NONE_COMPASS);
+ float compassBearing = 5f;
+
+ camera.onNewCompassBearingValue(compassBearing);
+
+ verify(mapboxMap).moveCamera(any(CameraUpdate.class));
+ }
+
+ @Test
+ public void onNewCompassBearingValue_cameraModeNoneIgnored() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ LocationCameraController camera = buildCamera(mapboxMap);
+ camera.initializeOptions(mock(LocationComponentOptions.class));
+ camera.setCameraMode(CameraMode.NONE);
+ float compassBearing = 5f;
+
+ camera.onNewCompassBearingValue(compassBearing);
+
+ verify(mapboxMap, times(0)).moveCamera(any(CameraUpdate.class));
+ }
+
+ @Test
+ public void onNewZoomValue_cameraIsUpdated() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ LocationCameraController camera = buildCamera(mapboxMap);
+ camera.initializeOptions(mock(LocationComponentOptions.class));
+ camera.setCameraMode(CameraMode.TRACKING);
+ float zoom = 5f;
+
+ camera.onNewZoomValue(zoom);
+
+ verify(mapboxMap).moveCamera(any(CameraUpdate.class));
+ }
+
+ private LocationCameraController buildCamera(OnCameraTrackingChangedListener onCameraTrackingChangedListener) {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ when(mapboxMap.getUiSettings()).thenReturn(mock(UiSettings.class));
+ MoveGestureDetector moveGestureDetector = mock(MoveGestureDetector.class);
+ OnCameraMoveInvalidateListener onCameraMoveInvalidateListener = mock(OnCameraMoveInvalidateListener.class);
+ return new LocationCameraController(mapboxMap, moveGestureDetector,
+ onCameraTrackingChangedListener, onCameraMoveInvalidateListener);
+ }
+
+ private LocationCameraController buildCamera(MoveGestureDetector moveGestureDetector) {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ when(mapboxMap.getUiSettings()).thenReturn(mock(UiSettings.class));
+ OnCameraTrackingChangedListener onCameraTrackingChangedListener = mock(OnCameraTrackingChangedListener.class);
+ OnCameraMoveInvalidateListener onCameraMoveInvalidateListener = mock(OnCameraMoveInvalidateListener.class);
+ return new LocationCameraController(mapboxMap, moveGestureDetector,
+ onCameraTrackingChangedListener, onCameraMoveInvalidateListener);
+ }
+
+ private LocationCameraController buildCamera(MapboxMap mapboxMap) {
+ MoveGestureDetector moveGestureDetector = mock(MoveGestureDetector.class);
+ OnCameraTrackingChangedListener onCameraTrackingChangedListener = mock(OnCameraTrackingChangedListener.class);
+ OnCameraMoveInvalidateListener onCameraMoveInvalidateListener = mock(OnCameraMoveInvalidateListener.class);
+ return new LocationCameraController(mapboxMap, moveGestureDetector,
+ onCameraTrackingChangedListener, onCameraMoveInvalidateListener);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentOptionsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentOptionsTest.java
new file mode 100644
index 0000000000..4c25fa840d
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentOptionsTest.java
@@ -0,0 +1,68 @@
+package com.mapbox.mapboxsdk.location;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+
+import com.mapbox.mapboxsdk.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class LocationComponentOptionsTest {
+
+ @Mock
+ private Context context;
+ @Mock
+ private TypedArray array;
+ @Mock
+ private Resources resources;
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Before
+ public void setUp() throws Exception {
+ when(context.obtainStyledAttributes(R.style.mapbox_LocationComponent, R.styleable.mapbox_LocationComponent))
+ .thenReturn(array);
+ when(array.getResourceId(R.styleable.mapbox_LocationComponent_mapbox_foregroundDrawable, -1))
+ .thenReturn(R.drawable.mapbox_user_icon);
+ when(context.getResources()).thenReturn(resources);
+ }
+
+ @Test
+ public void sanity() throws Exception {
+ LocationComponentOptions locationComponentOptions = LocationComponentOptions.builder(context)
+ .accuracyAlpha(0.5f)
+ .build();
+ assertNotNull(locationComponentOptions);
+ }
+
+ @Test
+ public void passingOutOfRangeAccuracyAlpha_throwsException() throws Exception {
+ thrown.expect(IllegalArgumentException.class);
+ thrown.expectMessage("Accuracy alpha value must be between 0.0 and "
+ + "1.0.");
+ LocationComponentOptions.builder(context)
+ .accuracyAlpha(2f)
+ .build();
+ }
+
+ @Test
+ public void negativeElevation_causesExceptionToBeThrown() throws Exception {
+ thrown.expect(IllegalArgumentException.class);
+ thrown.expectMessage("Invalid shadow size -500.0. Must be >= 0");
+ LocationComponentOptions.builder(context)
+ .elevation(-500)
+ .build();
+ }
+}
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationLayerControllerTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationLayerControllerTest.java
new file mode 100644
index 0000000000..cb6dcd8fe5
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationLayerControllerTest.java
@@ -0,0 +1,466 @@
+package com.mapbox.mapboxsdk.location;
+
+import android.graphics.Bitmap;
+
+import com.google.gson.JsonElement;
+import com.mapbox.geojson.Feature;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.location.modes.RenderMode;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.style.layers.Layer;
+import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
+
+import org.junit.Test;
+
+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_RADIUS;
+import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_COMPASS_BEARING;
+import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_FOREGROUND_ICON_OFFSET;
+import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_GPS_BEARING;
+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 org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class LocationLayerControllerTest {
+
+ @Test
+ public void onInitialization_locationSourceIsAdded() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ LayerSourceProvider sourceProvider = buildLayerProvider();
+ GeoJsonSource locationSource = mock(GeoJsonSource.class);
+ when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource);
+ LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
+ LocationComponentOptions options = mock(LocationComponentOptions.class);
+
+ new LocationLayerController(mapboxMap, sourceProvider, buildFeatureProvider(options), bitmapProvider, options);
+
+ verify(mapboxMap).addSource(locationSource);
+ }
+
+ @Test
+ public void onInitialization_shadowLayerIsAdded() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ LayerSourceProvider sourceProvider = buildLayerProvider();
+ Layer shadowLayer = mock(Layer.class);
+ when(sourceProvider.generateLayer(SHADOW_LAYER)).thenReturn(shadowLayer);
+ GeoJsonSource locationSource = mock(GeoJsonSource.class);
+ when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource);
+ LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
+ LocationComponentOptions options = mock(LocationComponentOptions.class);
+
+ new LocationLayerController(mapboxMap, sourceProvider, buildFeatureProvider(options), bitmapProvider, options);
+
+ verify(mapboxMap).addLayerBelow(shadowLayer, BACKGROUND_LAYER);
+ }
+
+ @Test
+ public void onInitialization_backgroundLayerIsAdded() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ LayerSourceProvider sourceProvider = buildLayerProvider();
+ Layer backgroundLayer = mock(Layer.class);
+ when(sourceProvider.generateLayer(BACKGROUND_LAYER)).thenReturn(backgroundLayer);
+ GeoJsonSource locationSource = mock(GeoJsonSource.class);
+ when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource);
+ LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
+ LocationComponentOptions options = mock(LocationComponentOptions.class);
+
+ new LocationLayerController(mapboxMap, sourceProvider, buildFeatureProvider(options), bitmapProvider, options);
+
+ verify(mapboxMap).addLayerBelow(backgroundLayer, FOREGROUND_LAYER);
+ }
+
+ @Test
+ public void onInitialization_foregroundLayerIsAdded() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ LayerSourceProvider sourceProvider = buildLayerProvider();
+ Layer foregroundLayer = mock(Layer.class);
+ when(sourceProvider.generateLayer(FOREGROUND_LAYER)).thenReturn(foregroundLayer);
+ GeoJsonSource locationSource = mock(GeoJsonSource.class);
+ when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource);
+ LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
+ LocationComponentOptions options = mock(LocationComponentOptions.class);
+
+ new LocationLayerController(mapboxMap, sourceProvider, buildFeatureProvider(options), bitmapProvider, options);
+
+ verify(mapboxMap).addLayerBelow(foregroundLayer, BEARING_LAYER);
+ }
+
+ @Test
+ public void onInitialization_bearingLayerIsAdded() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ LayerSourceProvider sourceProvider = buildLayerProvider();
+ Layer bearingLayer = mock(Layer.class);
+ when(sourceProvider.generateLayer(BEARING_LAYER)).thenReturn(bearingLayer);
+ GeoJsonSource locationSource = mock(GeoJsonSource.class);
+ when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource);
+ LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
+ LocationComponentOptions options = mock(LocationComponentOptions.class);
+ String layerBelow = "layer-below";
+ when(options.layerBelow()).thenReturn(layerBelow);
+
+ new LocationLayerController(mapboxMap, sourceProvider, buildFeatureProvider(options), bitmapProvider, options);
+
+ verify(mapboxMap).addLayerBelow(bearingLayer, layerBelow);
+ }
+
+ @Test
+ public void onInitialization_accuracyLayerIsAdded() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ LayerSourceProvider sourceProvider = buildLayerProvider();
+ Layer accuracyLayer = mock(Layer.class);
+ when(sourceProvider.generateAccuracyLayer()).thenReturn(accuracyLayer);
+ GeoJsonSource locationSource = mock(GeoJsonSource.class);
+ when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource);
+ LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
+ LocationComponentOptions options = mock(LocationComponentOptions.class);
+
+ new LocationLayerController(mapboxMap, sourceProvider, buildFeatureProvider(options), bitmapProvider, options);
+
+ verify(mapboxMap).addLayerBelow(accuracyLayer, BACKGROUND_LAYER);
+ }
+
+ @Test
+ public void applyStyle_styleShadowWithValidElevation() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ LayerSourceProvider sourceProvider = buildLayerProvider();
+ when(sourceProvider.generateSource(any(Feature.class))).thenReturn(mock(GeoJsonSource.class));
+ LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
+ Bitmap bitmap = mock(Bitmap.class);
+ when(bitmapProvider.generateShadowBitmap(any(LocationComponentOptions.class))).thenReturn(bitmap);
+ LocationComponentOptions options = mock(LocationComponentOptions.class);
+ when(options.elevation()).thenReturn(2f);
+
+ // Style is applied on initialization
+ new LocationLayerController(mapboxMap, sourceProvider, buildFeatureProvider(options), bitmapProvider, options);
+
+ verify(mapboxMap).addImage(SHADOW_ICON, bitmap);
+ }
+
+ @Test
+ public void applyStyle_ignoreStyleShadowWithInvalidElevation() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ LayerSourceProvider sourceProvider = buildLayerProvider();
+ when(sourceProvider.generateSource(any(Feature.class))).thenReturn(mock(GeoJsonSource.class));
+ LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
+ Bitmap bitmap = mock(Bitmap.class);
+ when(bitmapProvider.generateShadowBitmap(any(LocationComponentOptions.class))).thenReturn(bitmap);
+ LocationComponentOptions options = mock(LocationComponentOptions.class);
+ when(options.elevation()).thenReturn(0f);
+
+ new LocationLayerController(mapboxMap, sourceProvider, buildFeatureProvider(options), bitmapProvider, options);
+
+ verify(mapboxMap, times(0)).addImage(SHADOW_ICON, bitmap);
+ }
+
+ @Test
+ public void applyStyle_styleForegroundFromOptions() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ LayerSourceProvider sourceProvider = buildLayerProvider();
+ when(sourceProvider.generateSource(any(Feature.class))).thenReturn(mock(GeoJsonSource.class));
+ LocationComponentOptions options = mock(LocationComponentOptions.class);
+ int drawableResId = 123;
+ int tintColor = 456;
+ when(options.foregroundDrawable()).thenReturn(drawableResId);
+ when(options.foregroundTintColor()).thenReturn(tintColor);
+ LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
+ Bitmap bitmap = mock(Bitmap.class);
+ when(bitmapProvider.generateBitmap(drawableResId, tintColor)).thenReturn(bitmap);
+
+ new LocationLayerController(mapboxMap, sourceProvider, buildFeatureProvider(options), bitmapProvider, options);
+
+ verify(mapboxMap).addImage(FOREGROUND_ICON, bitmap);
+ }
+
+ @Test
+ public void applyStyle_styleForegroundStaleFromOptions() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ LayerSourceProvider sourceProvider = buildLayerProvider();
+ when(sourceProvider.generateSource(any(Feature.class))).thenReturn(mock(GeoJsonSource.class));
+ LocationComponentOptions options = mock(LocationComponentOptions.class);
+ int drawableResId = 123;
+ int tintColor = 456;
+ when(options.foregroundDrawableStale()).thenReturn(drawableResId);
+ when(options.foregroundStaleTintColor()).thenReturn(tintColor);
+ LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
+ Bitmap bitmap = mock(Bitmap.class);
+ when(bitmapProvider.generateBitmap(drawableResId, tintColor)).thenReturn(bitmap);
+
+ new LocationLayerController(mapboxMap, sourceProvider, buildFeatureProvider(options), bitmapProvider, options);
+
+ verify(mapboxMap).addImage(FOREGROUND_STALE_ICON, bitmap);
+ }
+
+ @Test
+ public void applyStyle_styleBackgroundFromOptions() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ LayerSourceProvider sourceProvider = buildLayerProvider();
+ when(sourceProvider.generateSource(any(Feature.class))).thenReturn(mock(GeoJsonSource.class));
+ LocationComponentOptions options = mock(LocationComponentOptions.class);
+ int drawableResId = 123;
+ int tintColor = 456;
+ when(options.backgroundDrawable()).thenReturn(drawableResId);
+ when(options.backgroundTintColor()).thenReturn(tintColor);
+ LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
+ Bitmap bitmap = mock(Bitmap.class);
+ when(bitmapProvider.generateBitmap(drawableResId, tintColor)).thenReturn(bitmap);
+
+ new LocationLayerController(mapboxMap, sourceProvider, buildFeatureProvider(options), bitmapProvider, options);
+
+ verify(mapboxMap).addImage(BACKGROUND_ICON, bitmap);
+ }
+
+ @Test
+ public void applyStyle_styleBackgroundStaleFromOptions() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ LayerSourceProvider sourceProvider = buildLayerProvider();
+ when(sourceProvider.generateSource(any(Feature.class))).thenReturn(mock(GeoJsonSource.class));
+ LocationComponentOptions options = mock(LocationComponentOptions.class);
+ int drawableResId = 123;
+ int tintColor = 456;
+ when(options.backgroundDrawableStale()).thenReturn(drawableResId);
+ when(options.backgroundStaleTintColor()).thenReturn(tintColor);
+ LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
+ Bitmap bitmap = mock(Bitmap.class);
+ when(bitmapProvider.generateBitmap(drawableResId, tintColor)).thenReturn(bitmap);
+
+ new LocationLayerController(mapboxMap, sourceProvider, buildFeatureProvider(options), bitmapProvider, options);
+
+ verify(mapboxMap).addImage(BACKGROUND_STALE_ICON, bitmap);
+ }
+
+ @Test
+ public void applyStyle_styleBearingFromOptions() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ LayerSourceProvider sourceProvider = buildLayerProvider();
+ when(sourceProvider.generateSource(any(Feature.class))).thenReturn(mock(GeoJsonSource.class));
+ LocationComponentOptions options = mock(LocationComponentOptions.class);
+ int drawableResId = 123;
+ int tintColor = 456;
+ when(options.bearingDrawable()).thenReturn(drawableResId);
+ when(options.bearingTintColor()).thenReturn(tintColor);
+ LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
+ Bitmap bitmap = mock(Bitmap.class);
+ when(bitmapProvider.generateBitmap(drawableResId, tintColor)).thenReturn(bitmap);
+
+ new LocationLayerController(mapboxMap, sourceProvider, buildFeatureProvider(options), bitmapProvider, options);
+
+ verify(mapboxMap).addImage(BEARING_ICON, bitmap);
+ }
+
+ @Test
+ public void updateForegroundOffset_foregroundIconPropertyIsUpdated() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ LayerSourceProvider sourceProvider = buildLayerProvider();
+ GeoJsonSource locationSource = mock(GeoJsonSource.class);
+ when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource);
+ LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
+ LocationComponentOptions options = mock(LocationComponentOptions.class);
+ Feature locationFeature = mock(Feature.class);
+ LocationLayerController layer = new LocationLayerController(
+ mapboxMap, sourceProvider, buildFeatureProvider(locationFeature, options), bitmapProvider, options
+ );
+
+ layer.updateForegroundOffset(2d);
+
+ verify(locationFeature).addProperty(eq(PROPERTY_FOREGROUND_ICON_OFFSET), any(JsonElement.class));
+ }
+
+ @Test
+ public void updateForegroundOffset_shadowPropertyIsUpdated() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ LayerSourceProvider sourceProvider = buildLayerProvider();
+ GeoJsonSource locationSource = mock(GeoJsonSource.class);
+ when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource);
+ LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
+ LocationComponentOptions options = mock(LocationComponentOptions.class);
+ Feature locationFeature = mock(Feature.class);
+ LocationLayerController layer = new LocationLayerController(
+ mapboxMap, sourceProvider, buildFeatureProvider(locationFeature, options), bitmapProvider, options
+ );
+
+ layer.updateForegroundOffset(2d);
+
+ verify(locationFeature).addProperty(eq(PROPERTY_SHADOW_ICON_OFFSET), any(JsonElement.class));
+ }
+
+ @Test
+ public void onNewLatLngValue_locationFeatureIsUpdated() {
+ GeoJsonSource locationSource = mock(GeoJsonSource.class);
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ when(mapboxMap.getSourceAs(LOCATION_SOURCE)).thenReturn(locationSource);
+ LayerSourceProvider sourceProvider = buildLayerProvider();
+ when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource);
+ LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
+ LocationComponentOptions options = mock(LocationComponentOptions.class);
+ Feature locationFeature = mock(Feature.class);
+ LocationLayerController layer = new LocationLayerController(
+ mapboxMap, sourceProvider, buildFeatureProvider(locationFeature, options), bitmapProvider, options
+ );
+
+ layer.onNewLatLngValue(new LatLng());
+
+ // wanted twice (once for initialization)
+ verify(locationSource, times(2)).setGeoJson(locationFeature);
+ }
+
+ @Test
+ public void onNewGpsBearingValue_locationFeatureIsUpdated() {
+ GeoJsonSource locationSource = mock(GeoJsonSource.class);
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ when(mapboxMap.getSourceAs(LOCATION_SOURCE)).thenReturn(locationSource);
+ LayerSourceProvider sourceProvider = buildLayerProvider();
+ when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource);
+ LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
+ LocationComponentOptions options = mock(LocationComponentOptions.class);
+ Feature locationFeature = mock(Feature.class);
+ LocationLayerController layer = new LocationLayerController(
+ mapboxMap, sourceProvider, buildFeatureProvider(locationFeature, options), bitmapProvider, options
+ );
+ layer.setRenderMode(RenderMode.GPS);
+ float gpsBearing = 2f;
+
+ layer.onNewGpsBearingValue(gpsBearing);
+
+ verify(locationFeature).addNumberProperty(PROPERTY_GPS_BEARING, gpsBearing);
+ }
+
+ @Test
+ public void onNewGpsBearingValue_updateIgnoredWithInvalidRenderMode() {
+ GeoJsonSource locationSource = mock(GeoJsonSource.class);
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ when(mapboxMap.getSourceAs(LOCATION_SOURCE)).thenReturn(locationSource);
+ LayerSourceProvider sourceProvider = buildLayerProvider();
+ when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource);
+ LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
+ LocationComponentOptions options = mock(LocationComponentOptions.class);
+ Feature locationFeature = mock(Feature.class);
+ LocationLayerController layer = new LocationLayerController(
+ mapboxMap, sourceProvider, buildFeatureProvider(locationFeature, options), bitmapProvider, options
+ );
+ layer.setRenderMode(RenderMode.COMPASS);
+ float gpsBearing = 2f;
+
+ layer.onNewGpsBearingValue(gpsBearing);
+
+ verify(locationFeature, times(0)).addNumberProperty(PROPERTY_GPS_BEARING, gpsBearing);
+ }
+
+ @Test
+ public void onNewCompassBearingValue_locationFeatureIsUpdated() {
+ GeoJsonSource locationSource = mock(GeoJsonSource.class);
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ when(mapboxMap.getSourceAs(LOCATION_SOURCE)).thenReturn(locationSource);
+ LayerSourceProvider sourceProvider = buildLayerProvider();
+ when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource);
+ LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
+ LocationComponentOptions options = mock(LocationComponentOptions.class);
+ Feature locationFeature = mock(Feature.class);
+ LocationLayerController layer = new LocationLayerController(
+ mapboxMap, sourceProvider, buildFeatureProvider(locationFeature, options), bitmapProvider, options
+ );
+ layer.setRenderMode(RenderMode.COMPASS);
+ float compassBearing = 2f;
+
+ layer.onNewCompassBearingValue(compassBearing);
+
+ verify(locationFeature).addNumberProperty(PROPERTY_COMPASS_BEARING, compassBearing);
+ }
+
+ @Test
+ public void onNewCompassBearingValue_updateIgnoredWithInvalidRenderMode() {
+ GeoJsonSource locationSource = mock(GeoJsonSource.class);
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ when(mapboxMap.getSourceAs(LOCATION_SOURCE)).thenReturn(locationSource);
+ LayerSourceProvider sourceProvider = buildLayerProvider();
+ when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource);
+ LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
+ LocationComponentOptions options = mock(LocationComponentOptions.class);
+ Feature locationFeature = mock(Feature.class);
+ LocationLayerController layer = new LocationLayerController(
+ mapboxMap, sourceProvider, buildFeatureProvider(locationFeature, options), bitmapProvider, options
+ );
+ layer.setRenderMode(RenderMode.GPS);
+ float compassBearing = 2f;
+
+ layer.onNewCompassBearingValue(compassBearing);
+
+ verify(locationFeature, times(0)).addNumberProperty(PROPERTY_COMPASS_BEARING, compassBearing);
+ }
+
+ @Test
+ public void onNewAccuracyRadiusValue_locationFeatureIsUpdated() {
+ GeoJsonSource locationSource = mock(GeoJsonSource.class);
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ when(mapboxMap.getSourceAs(LOCATION_SOURCE)).thenReturn(locationSource);
+ LayerSourceProvider sourceProvider = buildLayerProvider();
+ when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource);
+ LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
+ LocationComponentOptions options = mock(LocationComponentOptions.class);
+ Feature locationFeature = mock(Feature.class);
+ LocationLayerController layer = new LocationLayerController(
+ mapboxMap, sourceProvider, buildFeatureProvider(locationFeature, options), bitmapProvider, options
+ );
+ float accuracyRadiusValue = 2f;
+
+ layer.onNewAccuracyRadiusValue(accuracyRadiusValue);
+
+ verify(locationFeature).addNumberProperty(PROPERTY_ACCURACY_RADIUS, accuracyRadiusValue);
+ }
+
+ @Test
+ public void onNewAccuracyRadiusValue_updateIgnoredWithInvalidRenderMode() {
+ GeoJsonSource locationSource = mock(GeoJsonSource.class);
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ when(mapboxMap.getSourceAs(LOCATION_SOURCE)).thenReturn(locationSource);
+ LayerSourceProvider sourceProvider = buildLayerProvider();
+ when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource);
+ LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
+ LocationComponentOptions options = mock(LocationComponentOptions.class);
+ Feature locationFeature = mock(Feature.class);
+ LocationLayerController layer = new LocationLayerController(
+ mapboxMap, sourceProvider, buildFeatureProvider(locationFeature, options), bitmapProvider, options
+ );
+ layer.setRenderMode(RenderMode.GPS);
+ float accuracyRadiusValue = 2f;
+
+ layer.onNewAccuracyRadiusValue(accuracyRadiusValue);
+
+ verify(locationFeature, times(0)).addNumberProperty(PROPERTY_ACCURACY_RADIUS, accuracyRadiusValue);
+ }
+
+ private LayerFeatureProvider buildFeatureProvider(LocationComponentOptions options) {
+ LayerFeatureProvider provider = mock(LayerFeatureProvider.class);
+ when(provider.generateLocationFeature(null, options)).thenReturn(mock(Feature.class));
+ return provider;
+ }
+
+ private LayerFeatureProvider buildFeatureProvider(Feature feature, LocationComponentOptions options) {
+ LayerFeatureProvider provider = mock(LayerFeatureProvider.class);
+ when(provider.generateLocationFeature(null, options)).thenReturn(feature);
+ return provider;
+ }
+
+ private LayerSourceProvider buildLayerProvider() {
+ LayerSourceProvider layerSourceProvider = mock(LayerSourceProvider.class);
+ when(layerSourceProvider.generateLayer(SHADOW_LAYER)).thenReturn(mock(Layer.class));
+ when(layerSourceProvider.generateLayer(BACKGROUND_LAYER)).thenReturn(mock(Layer.class));
+ when(layerSourceProvider.generateLayer(FOREGROUND_LAYER)).thenReturn(mock(Layer.class));
+ when(layerSourceProvider.generateLayer(BEARING_LAYER)).thenReturn(mock(Layer.class));
+ when(layerSourceProvider.generateAccuracyLayer()).thenReturn(mock(Layer.class));
+ return layerSourceProvider;
+ }
+}
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/MapboxAnimatorCoordinatorTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/MapboxAnimatorCoordinatorTest.kt
new file mode 100644
index 0000000000..fb4dcd580c
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/MapboxAnimatorCoordinatorTest.kt
@@ -0,0 +1,284 @@
+package com.mapbox.mapboxsdk.location
+
+import android.location.Location
+import com.mapbox.mapboxsdk.camera.CameraPosition
+import com.mapbox.mapboxsdk.geometry.LatLng
+import com.mapbox.mapboxsdk.location.LocationComponentConstants.DEFAULT_TRACKING_TILT_ANIM_DURATION
+import com.mapbox.mapboxsdk.location.LocationComponentConstants.DEFAULT_TRACKING_ZOOM_ANIM_DURATION
+import com.mapbox.mapboxsdk.location.MapboxAnimator.*
+import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class MapboxAnimatorCoordinatorTest {
+
+ private lateinit var locationAnimatorCoordinator: LocationAnimatorCoordinator
+ private val cameraPosition: CameraPosition = CameraPosition.DEFAULT
+
+ @Before
+ fun setUp() {
+ locationAnimatorCoordinator = LocationAnimatorCoordinator()
+ }
+
+ @Test
+ fun feedNewLocation_animatorsAreCreated() {
+ locationAnimatorCoordinator.feedNewLocation(Location(""), cameraPosition, false)
+
+ assertTrue(locationAnimatorCoordinator.animatorMap[ANIMATOR_CAMERA_LATLNG] != null)
+ assertTrue(locationAnimatorCoordinator.animatorMap[ANIMATOR_CAMERA_GPS_BEARING] != null)
+ assertTrue(locationAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_LATLNG] != null)
+ assertTrue(locationAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_GPS_BEARING] != null)
+ }
+
+ @Test
+ fun feedNewLocation_animatorValue() {
+ val location = Location("")
+ location.latitude = 51.0
+ location.longitude = 17.0
+ location.bearing = 35f
+ locationAnimatorCoordinator.feedNewLocation(location, cameraPosition, false)
+
+ val cameraLatLngTarget = locationAnimatorCoordinator.animatorMap[ANIMATOR_CAMERA_LATLNG]?.target as LatLng
+ assertEquals(cameraLatLngTarget.latitude, cameraLatLngTarget.latitude)
+
+ val layerLatLngTarget = locationAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_LATLNG]?.target as LatLng
+ assertEquals(layerLatLngTarget.latitude, layerLatLngTarget.latitude)
+
+ val cameraBearingTarget = locationAnimatorCoordinator.animatorMap[ANIMATOR_CAMERA_GPS_BEARING]?.target as Float
+ assertEquals(location.bearing, cameraBearingTarget)
+
+ val layerBearingTarget = locationAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_GPS_BEARING]?.target as Float
+ assertEquals(location.bearing, layerBearingTarget)
+ }
+
+ @Test
+ fun feedNewLocation_isNorth_animatorsAreCreated() {
+ val location = Location("")
+ location.latitude = 51.0
+ location.longitude = 17.0
+ location.bearing = 35f
+ locationAnimatorCoordinator.feedNewLocation(location, cameraPosition, false)
+
+ assertTrue(locationAnimatorCoordinator.animatorMap[ANIMATOR_CAMERA_LATLNG] != null)
+ assertTrue(locationAnimatorCoordinator.animatorMap[ANIMATOR_CAMERA_GPS_BEARING] != null)
+ assertTrue(locationAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_LATLNG] != null)
+ assertTrue(locationAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_GPS_BEARING] != null)
+ }
+
+ @Test
+ fun feedNewLocation_isNorth_animatorValue() {
+ val location = Location("")
+ location.latitude = 51.0
+ location.longitude = 17.0
+ location.bearing = 35f
+ locationAnimatorCoordinator.feedNewLocation(location, cameraPosition, true)
+
+ val cameraLatLngTarget = locationAnimatorCoordinator.animatorMap[ANIMATOR_CAMERA_LATLNG]?.target as LatLng
+ assertEquals(cameraLatLngTarget.latitude, cameraLatLngTarget.latitude)
+
+ val layerLatLngTarget = locationAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_LATLNG]?.target as LatLng
+ assertEquals(layerLatLngTarget.latitude, layerLatLngTarget.latitude)
+
+ val cameraBearingTarget = locationAnimatorCoordinator.animatorMap[ANIMATOR_CAMERA_GPS_BEARING]?.target as Float
+ assertEquals(0f, cameraBearingTarget)
+
+ val layerBearingTarget = locationAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_GPS_BEARING]?.target as Float
+ assertEquals(location.bearing, layerBearingTarget)
+ }
+
+ @Test
+ fun feedNewCompassBearing_animatorsAreCreated() {
+ locationAnimatorCoordinator.feedNewCompassBearing(77f, cameraPosition)
+
+ assertTrue(locationAnimatorCoordinator.animatorMap[ANIMATOR_CAMERA_COMPASS_BEARING] != null)
+ assertTrue(locationAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_COMPASS_BEARING] != null)
+ }
+
+ @Test
+ fun feedNewCompassBearing_animatorValue() {
+ val bearing = 77f
+ locationAnimatorCoordinator.feedNewCompassBearing(bearing, cameraPosition)
+
+ val cameraBearingTarget = locationAnimatorCoordinator.animatorMap[ANIMATOR_CAMERA_COMPASS_BEARING]?.target as Float
+ assertEquals(bearing, cameraBearingTarget)
+
+ val layerBearingTarget = locationAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_COMPASS_BEARING]?.target as Float
+ assertEquals(bearing, layerBearingTarget)
+ }
+
+ @Test
+ fun feedNewAccuracyRadius_animatorsCreated() {
+ locationAnimatorCoordinator.feedNewAccuracyRadius(150f, false)
+
+ assertTrue(locationAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_ACCURACY] != null)
+ }
+
+ @Test
+ fun feedNewAccuracyRadius_animatorValue() {
+ val accuracy = 150f
+ locationAnimatorCoordinator.feedNewAccuracyRadius(accuracy, false)
+
+ val layerAccuracy = locationAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_ACCURACY]?.target as Float
+ assertEquals(layerAccuracy, accuracy)
+
+ val animationDuration = locationAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_ACCURACY]?.duration as Long
+ assertEquals(LocationComponentConstants.ACCURACY_RADIUS_ANIMATION_DURATION, animationDuration)
+ }
+
+ @Test
+ fun feedNewAccuracyRadius_noAnimation_animatorsCreated() {
+ locationAnimatorCoordinator.feedNewAccuracyRadius(150f, true)
+
+ assertTrue(locationAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_ACCURACY] != null)
+ }
+
+ @Test
+ fun feedNewAccuracyRadius_noAnimation_animatorValue() {
+ val accuracy = 150f
+ locationAnimatorCoordinator.feedNewAccuracyRadius(accuracy, true)
+
+ val layerAccuracy = locationAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_ACCURACY]?.target as Float
+ assertEquals(layerAccuracy, accuracy)
+
+ val animationDuration = locationAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_ACCURACY]?.duration as Long
+ assertEquals(0L, animationDuration)
+ }
+
+ @Test
+ fun feedNewZoomLevel_animatorsCreated() {
+ locationAnimatorCoordinator.feedNewZoomLevel(
+ 15.0,
+ cameraPosition,
+ DEFAULT_TRACKING_ZOOM_ANIM_DURATION,
+ null
+ )
+
+ assertTrue(locationAnimatorCoordinator.animatorMap[ANIMATOR_ZOOM] != null)
+ }
+
+ @Test
+ fun feedNewZoomLevel_animatorValue() {
+ val zoom = 15.0f
+ locationAnimatorCoordinator.feedNewZoomLevel(
+ zoom.toDouble(),
+ cameraPosition,
+ DEFAULT_TRACKING_ZOOM_ANIM_DURATION,
+ null
+ )
+
+ val animationDuration = locationAnimatorCoordinator.animatorMap[ANIMATOR_ZOOM]?.duration as Long
+ assertEquals(DEFAULT_TRACKING_ZOOM_ANIM_DURATION, animationDuration)
+
+ val target = locationAnimatorCoordinator.animatorMap[ANIMATOR_ZOOM]?.target as Float
+ assertEquals(zoom, target)
+ }
+
+ @Test
+ fun feedNewTiltLevel_animatorsCreated() {
+ locationAnimatorCoordinator.feedNewTilt(
+ 30.0,
+ cameraPosition,
+ DEFAULT_TRACKING_TILT_ANIM_DURATION,
+ null
+ )
+
+ assertTrue(locationAnimatorCoordinator.animatorMap[ANIMATOR_TILT] != null)
+ }
+
+ @Test
+ fun feedNewTiltLevel_animatorValue() {
+ val tilt = 30.0f
+ locationAnimatorCoordinator.feedNewTilt(
+ tilt.toDouble(),
+ cameraPosition,
+ DEFAULT_TRACKING_TILT_ANIM_DURATION,
+ null
+ )
+
+ val animationDuration = locationAnimatorCoordinator.animatorMap[ANIMATOR_TILT]?.duration as Long
+ assertEquals(DEFAULT_TRACKING_TILT_ANIM_DURATION, animationDuration)
+
+ val target = locationAnimatorCoordinator.animatorMap[ANIMATOR_TILT]?.target as Float
+ assertEquals(tilt, target)
+ }
+
+ @Test
+ fun cancelAllAnimators() {
+ locationAnimatorCoordinator.feedNewLocation(Location(""), cameraPosition, true)
+ locationAnimatorCoordinator.cancelAllAnimations()
+
+ assertTrue(locationAnimatorCoordinator.animatorMap[ANIMATOR_CAMERA_LATLNG] == null)
+ }
+
+ @Test
+ fun cancelZoomAnimators() {
+ locationAnimatorCoordinator.feedNewZoomLevel(
+ 15.0,
+ cameraPosition,
+ DEFAULT_TRACKING_ZOOM_ANIM_DURATION,
+ null
+ )
+ locationAnimatorCoordinator.cancelZoomAnimation()
+
+ assertTrue(locationAnimatorCoordinator.animatorMap[ANIMATOR_ZOOM] == null)
+ }
+
+ @Test
+ fun cancelTiltAnimation() {
+ locationAnimatorCoordinator.feedNewTilt(
+ 30.0,
+ cameraPosition,
+ DEFAULT_TRACKING_TILT_ANIM_DURATION,
+ null
+ )
+
+ locationAnimatorCoordinator.cancelTiltAnimation()
+
+ assertTrue(locationAnimatorCoordinator.animatorMap[ANIMATOR_TILT] == null)
+ }
+
+ @Test
+ fun resetAllCameraAnimations_empty() {
+ locationAnimatorCoordinator.resetAllCameraAnimations(cameraPosition, false)
+ assertTrue(locationAnimatorCoordinator.animatorMap.isEmpty())
+ }
+
+ @Test
+ fun addLayerListener() {
+ val layerListener = Mockito.mock(OnLayerAnimationsValuesChangeListener::class.java)
+ locationAnimatorCoordinator.addLayerListener(layerListener)
+
+ assertTrue(locationAnimatorCoordinator.layerListeners.contains(layerListener))
+ }
+
+ @Test
+ fun removeLayerListener() {
+ val layerListener = Mockito.mock(OnLayerAnimationsValuesChangeListener::class.java)
+ locationAnimatorCoordinator.addLayerListener(layerListener)
+ locationAnimatorCoordinator.removeLayerListener(layerListener)
+
+ assertTrue(locationAnimatorCoordinator.layerListeners.isEmpty())
+ }
+
+ @Test
+ fun addCameraListener() {
+ val cameraListener = Mockito.mock(OnCameraAnimationsValuesChangeListener::class.java)
+ locationAnimatorCoordinator.addCameraListener(cameraListener)
+
+ assertTrue(locationAnimatorCoordinator.cameraListeners.contains(cameraListener))
+ }
+
+ @Test
+ fun removeCameraListener() {
+ val cameraListener = Mockito.mock(OnCameraAnimationsValuesChangeListener::class.java)
+ locationAnimatorCoordinator.addCameraListener(cameraListener)
+ locationAnimatorCoordinator.removeCameraListener(cameraListener)
+
+ assertTrue(locationAnimatorCoordinator.cameraListeners.isEmpty())
+ }
+}
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/UtilsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/UtilsTest.java
new file mode 100644
index 0000000000..bf5ee40f4a
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/UtilsTest.java
@@ -0,0 +1,28 @@
+package com.mapbox.mapboxsdk.location;
+
+import org.junit.Test;
+
+import static junit.framework.Assert.assertEquals;
+
+public final class UtilsTest {
+
+ @Test
+ public void shortestRotation_doesReturnValueDistanceQuickestToZero() throws Exception {
+ float value = Utils.shortestRotation(0, 181);
+ assertEquals(360f, value);
+ value = Utils.shortestRotation(0, 179);
+ assertEquals(0f, value);
+ value = Utils.shortestRotation(0, 180);
+ assertEquals(0f, value);
+ }
+
+ @Test
+ public void shortestRotation_doesReturnValueDistanceQuickestToFifty() throws Exception {
+ float value = Utils.shortestRotation(50, 231);
+ assertEquals(410f, value);
+ value = Utils.shortestRotation(50, 229);
+ assertEquals(50f, value);
+ value = Utils.shortestRotation(50, 180);
+ assertEquals(50f, value);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/plugins/locationlayer/CompassEngineTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/plugins/locationlayer/CompassEngineTest.java
deleted file mode 100644
index c69d2fc5fb..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/plugins/locationlayer/CompassEngineTest.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package com.mapbox.mapboxsdk.plugins.locationlayer;
-
-import android.hardware.Sensor;
-import android.hardware.SensorManager;
-import android.view.WindowManager;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
-
-import static junit.framework.Assert.assertEquals;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-@RunWith(MockitoJUnitRunner.class)
-public class CompassEngineTest {
-
- private LocationLayerCompassEngine compassEngine;
-
- @Mock
- private WindowManager windowManager;
-
- @Mock
- private SensorManager sensorManager;
-
- @Before
- public void setUp() throws Exception {
- compassEngine = new LocationLayerCompassEngine(windowManager, sensorManager);
- }
-
- @Test
- public void lastKnownCompassBearingAccuracyDefault() {
- assertEquals("Last accuracy should match", compassEngine.getLastAccuracySensorStatus(), 0);
- }
-
- @Test
- public void lastKnownCompassAccuracyStatusValue() {
- Sensor sensor = mock(Sensor.class);
- compassEngine.onAccuracyChanged(sensor, 2);
- assertEquals("Last accuracy should match", compassEngine.getLastAccuracySensorStatus(), 2);
- }
-
- @Test
- public void whenGyroscopeIsNull_fallbackToGravity() {
- SensorManager sensorManager = mock(SensorManager.class);
- when(sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)).thenReturn(null);
- new LocationLayerCompassEngine(windowManager, sensorManager);
-
- verify(sensorManager, times(1)).getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
- }
-
- @Test
- public void whenGyroscopeIsNull_fallbackToMagneticField() {
- SensorManager sensorManager = mock(SensorManager.class);
- when(sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)).thenReturn(null);
- new LocationLayerCompassEngine(windowManager, sensorManager);
-
- verify(sensorManager, times(1)).getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerCameraTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerCameraTest.java
deleted file mode 100644
index c73216f5d6..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerCameraTest.java
+++ /dev/null
@@ -1,337 +0,0 @@
-package com.mapbox.mapboxsdk.plugins.locationlayer;
-
-import android.graphics.PointF;
-
-import com.mapbox.android.gestures.MoveGestureDetector;
-import com.mapbox.mapboxsdk.camera.CameraPosition;
-import com.mapbox.mapboxsdk.camera.CameraUpdate;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.Projection;
-import com.mapbox.mapboxsdk.maps.UiSettings;
-import com.mapbox.mapboxsdk.plugins.locationlayer.modes.CameraMode;
-
-import org.junit.Test;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-public class LocationLayerCameraTest {
-
- @Test
- public void setCameraMode_mapTransitionsAreCancelled() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- LocationLayerCamera camera = buildCamera(mapboxMap);
- camera.initializeOptions(mock(LocationLayerOptions.class));
-
- camera.setCameraMode(CameraMode.TRACKING_GPS);
-
- verify(mapboxMap).cancelTransitions();
- }
-
- @Test
- public void setCameraMode_gestureThresholdIsAdjusted() {
- MoveGestureDetector moveGestureDetector = mock(MoveGestureDetector.class);
- LocationLayerCamera camera = buildCamera(moveGestureDetector);
- LocationLayerOptions options = mock(LocationLayerOptions.class);
- float moveThreshold = 5f;
- when(options.trackingInitialMoveThreshold()).thenReturn(moveThreshold);
- camera.initializeOptions(options);
-
- camera.setCameraMode(CameraMode.TRACKING_GPS);
-
- verify(moveGestureDetector).setMoveThreshold(moveThreshold);
- }
-
- @Test
- public void setCameraMode_gestureThresholdIsResetWhenNotTracking() {
- MoveGestureDetector moveGestureDetector = mock(MoveGestureDetector.class);
- LocationLayerCamera camera = buildCamera(moveGestureDetector);
- camera.initializeOptions(mock(LocationLayerOptions.class));
-
- camera.setCameraMode(CameraMode.NONE);
-
- verify(moveGestureDetector).setMoveThreshold(0f);
- }
-
- @Test
- public void setCameraMode_notTrackingAdjustsFocalPoint() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- when(mapboxMap.getUiSettings()).thenReturn(mock(UiSettings.class));
- LocationLayerCamera camera = buildCamera(mapboxMap);
- camera.initializeOptions(mock(LocationLayerOptions.class));
-
- camera.setCameraMode(CameraMode.TRACKING_GPS);
- camera.setCameraMode(CameraMode.NONE);
-
- verify(mapboxMap.getUiSettings()).setFocalPoint(null);
- }
-
- @Test
- public void setCameraMode_trackingChangeListenerCameraDismissedIsCalled() {
- OnCameraTrackingChangedListener internalTrackingChangedListener = mock(OnCameraTrackingChangedListener.class);
- LocationLayerCamera camera = buildCamera(internalTrackingChangedListener);
- camera.initializeOptions(mock(LocationLayerOptions.class));
-
- camera.setCameraMode(CameraMode.TRACKING_GPS);
- camera.setCameraMode(CameraMode.NONE);
-
- verify(internalTrackingChangedListener).onCameraTrackingDismissed();
- }
-
- @Test
- public void setCameraMode_internalCameraTrackingChangeListenerIsCalled() {
- OnCameraTrackingChangedListener internalTrackingChangedListener = mock(OnCameraTrackingChangedListener.class);
- LocationLayerCamera camera = buildCamera(internalTrackingChangedListener);
- camera.initializeOptions(mock(LocationLayerOptions.class));
- int cameraMode = CameraMode.NONE;
-
- camera.setCameraMode(cameraMode);
-
- verify(internalTrackingChangedListener).onCameraTrackingChanged(cameraMode);
- }
-
- @Test
- public void onNewLatLngValue_cameraModeTrackingUpdatesLatLng() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- when(mapboxMap.getUiSettings()).thenReturn(mock(UiSettings.class));
- when(mapboxMap.getProjection()).thenReturn(mock(Projection.class));
- LocationLayerCamera camera = buildCamera(mapboxMap);
- camera.initializeOptions(mock(LocationLayerOptions.class));
- camera.setCameraMode(CameraMode.TRACKING);
- LatLng latLng = mock(LatLng.class);
-
- camera.onNewLatLngValue(latLng);
-
- verify(mapboxMap).moveCamera(any(CameraUpdate.class));
- }
-
- @Test
- public void onNewLatLngValue_cameraModeTrackingGpsNorthUpdatesLatLng() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- when(mapboxMap.getUiSettings()).thenReturn(mock(UiSettings.class));
- when(mapboxMap.getProjection()).thenReturn(mock(Projection.class));
- LocationLayerCamera camera = buildCamera(mapboxMap);
- camera.initializeOptions(mock(LocationLayerOptions.class));
- camera.setCameraMode(CameraMode.TRACKING_GPS_NORTH);
- LatLng latLng = mock(LatLng.class);
-
- camera.onNewLatLngValue(latLng);
-
- verify(mapboxMap).moveCamera(any(CameraUpdate.class));
- }
-
- @Test
- public void onNewLatLngValue_cameraModeTrackingGpsUpdatesLatLng() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- when(mapboxMap.getUiSettings()).thenReturn(mock(UiSettings.class));
- when(mapboxMap.getProjection()).thenReturn(mock(Projection.class));
- LocationLayerCamera camera = buildCamera(mapboxMap);
- camera.initializeOptions(mock(LocationLayerOptions.class));
- camera.setCameraMode(CameraMode.TRACKING_GPS);
- LatLng latLng = mock(LatLng.class);
-
- camera.onNewLatLngValue(latLng);
-
- verify(mapboxMap).moveCamera(any(CameraUpdate.class));
- }
-
- @Test
- public void onNewLatLngValue_cameraModeTrackingCompassUpdatesLatLng() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- when(mapboxMap.getUiSettings()).thenReturn(mock(UiSettings.class));
- when(mapboxMap.getProjection()).thenReturn(mock(Projection.class));
- LocationLayerCamera camera = buildCamera(mapboxMap);
- camera.initializeOptions(mock(LocationLayerOptions.class));
- camera.setCameraMode(CameraMode.TRACKING_COMPASS);
- LatLng latLng = mock(LatLng.class);
-
- camera.onNewLatLngValue(latLng);
-
- verify(mapboxMap).moveCamera(any(CameraUpdate.class));
- }
-
- @Test
- public void onNewLatLngValue_cameraModeNoneIgnored() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- when(mapboxMap.getUiSettings()).thenReturn(mock(UiSettings.class));
- when(mapboxMap.getProjection()).thenReturn(mock(Projection.class));
- LocationLayerCamera camera = buildCamera(mapboxMap);
- camera.initializeOptions(mock(LocationLayerOptions.class));
- camera.setCameraMode(CameraMode.NONE);
- LatLng latLng = mock(LatLng.class);
-
- camera.onNewLatLngValue(latLng);
-
- verify(mapboxMap, times(0)).moveCamera(any(CameraUpdate.class));
- }
-
- @Test
- public void onNewLatLngValue_focalPointIsAdjusted() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- UiSettings uiSettings = mock(UiSettings.class);
- when(mapboxMap.getUiSettings()).thenReturn(uiSettings);
- Projection projection = mock(Projection.class);
- PointF pointF = mock(PointF.class);
- when(projection.toScreenLocation(any(LatLng.class))).thenReturn(pointF);
- when(mapboxMap.getProjection()).thenReturn(projection);
- LocationLayerCamera camera = buildCamera(mapboxMap);
- camera.initializeOptions(mock(LocationLayerOptions.class));
- camera.setCameraMode(CameraMode.TRACKING);
- LatLng latLng = mock(LatLng.class);
-
- camera.onNewLatLngValue(latLng);
-
- verify(uiSettings).setFocalPoint(pointF);
- }
-
- @Test
- public void onNewGpsBearingValue_cameraModeTrackingGpsUpdatesBearing() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- LocationLayerCamera camera = buildCamera(mapboxMap);
- camera.initializeOptions(mock(LocationLayerOptions.class));
- camera.setCameraMode(CameraMode.TRACKING_GPS);
- float gpsBearing = 5f;
-
- camera.onNewGpsBearingValue(gpsBearing);
-
- verify(mapboxMap).moveCamera(any(CameraUpdate.class));
- }
-
- @Test
- public void onNewGpsBearingValue_cameraModeNoneGpsUpdatesBearing() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- LocationLayerCamera camera = buildCamera(mapboxMap);
- camera.initializeOptions(mock(LocationLayerOptions.class));
- camera.setCameraMode(CameraMode.NONE_GPS);
- float gpsBearing = 5f;
-
- camera.onNewGpsBearingValue(gpsBearing);
-
- verify(mapboxMap).moveCamera(any(CameraUpdate.class));
- }
-
- @Test
- public void onNewGpsBearingValue_cameraModeTrackingNorthUpdatesBearing() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- CameraPosition cameraPosition = new CameraPosition.Builder().bearing(7d).build();
- when(mapboxMap.getCameraPosition()).thenReturn(cameraPosition);
- LocationLayerCamera camera = buildCamera(mapboxMap);
- camera.initializeOptions(mock(LocationLayerOptions.class));
- camera.setCameraMode(CameraMode.TRACKING_GPS_NORTH);
- float gpsBearing = 5f;
-
- camera.onNewGpsBearingValue(gpsBearing);
-
- verify(mapboxMap).moveCamera(any(CameraUpdate.class));
- }
-
- @Test
- public void onNewGpsBearingValue_cameraModeTrackingNorthBearingZeroIgnored() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- CameraPosition cameraPosition = new CameraPosition.Builder().bearing(0d).build();
- when(mapboxMap.getCameraPosition()).thenReturn(cameraPosition);
- LocationLayerCamera camera = buildCamera(mapboxMap);
- camera.initializeOptions(mock(LocationLayerOptions.class));
- camera.setCameraMode(CameraMode.TRACKING_GPS_NORTH);
- float gpsBearing = 5f;
-
- camera.onNewGpsBearingValue(gpsBearing);
-
- verify(mapboxMap, times(0)).moveCamera(any(CameraUpdate.class));
- }
-
- @Test
- public void onNewGpsBearingValue_cameraModeNoneIgnored() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- LocationLayerCamera camera = buildCamera(mapboxMap);
- camera.initializeOptions(mock(LocationLayerOptions.class));
- camera.setCameraMode(CameraMode.NONE);
- float gpsBearing = 5f;
-
- camera.onNewGpsBearingValue(gpsBearing);
-
- verify(mapboxMap, times(0)).moveCamera(any(CameraUpdate.class));
- }
-
- @Test
- public void onNewCompassBearingValue_cameraModeTrackingCompassUpdatesBearing() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- LocationLayerCamera camera = buildCamera(mapboxMap);
- camera.initializeOptions(mock(LocationLayerOptions.class));
- camera.setCameraMode(CameraMode.TRACKING_COMPASS);
- float compassBearing = 5f;
-
- camera.onNewCompassBearingValue(compassBearing);
-
- verify(mapboxMap).moveCamera(any(CameraUpdate.class));
- }
-
- @Test
- public void onNewCompassBearingValue_cameraModeNoneCompassUpdatesBearing() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- LocationLayerCamera camera = buildCamera(mapboxMap);
- camera.initializeOptions(mock(LocationLayerOptions.class));
- camera.setCameraMode(CameraMode.NONE_COMPASS);
- float compassBearing = 5f;
-
- camera.onNewCompassBearingValue(compassBearing);
-
- verify(mapboxMap).moveCamera(any(CameraUpdate.class));
- }
-
- @Test
- public void onNewCompassBearingValue_cameraModeNoneIgnored() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- LocationLayerCamera camera = buildCamera(mapboxMap);
- camera.initializeOptions(mock(LocationLayerOptions.class));
- camera.setCameraMode(CameraMode.NONE);
- float compassBearing = 5f;
-
- camera.onNewCompassBearingValue(compassBearing);
-
- verify(mapboxMap, times(0)).moveCamera(any(CameraUpdate.class));
- }
-
- @Test
- public void onNewZoomValue_cameraIsUpdated() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- LocationLayerCamera camera = buildCamera(mapboxMap);
- camera.initializeOptions(mock(LocationLayerOptions.class));
- camera.setCameraMode(CameraMode.TRACKING);
- float zoom = 5f;
-
- camera.onNewZoomValue(zoom);
-
- verify(mapboxMap).moveCamera(any(CameraUpdate.class));
- }
-
- private LocationLayerCamera buildCamera(OnCameraTrackingChangedListener onCameraTrackingChangedListener) {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- when(mapboxMap.getUiSettings()).thenReturn(mock(UiSettings.class));
- MoveGestureDetector moveGestureDetector = mock(MoveGestureDetector.class);
- OnCameraMoveInvalidateListener onCameraMoveInvalidateListener = mock(OnCameraMoveInvalidateListener.class);
- return new LocationLayerCamera(mapboxMap, moveGestureDetector,
- onCameraTrackingChangedListener, onCameraMoveInvalidateListener);
- }
-
- private LocationLayerCamera buildCamera(MoveGestureDetector moveGestureDetector) {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- when(mapboxMap.getUiSettings()).thenReturn(mock(UiSettings.class));
- OnCameraTrackingChangedListener onCameraTrackingChangedListener = mock(OnCameraTrackingChangedListener.class);
- OnCameraMoveInvalidateListener onCameraMoveInvalidateListener = mock(OnCameraMoveInvalidateListener.class);
- return new LocationLayerCamera(mapboxMap, moveGestureDetector,
- onCameraTrackingChangedListener, onCameraMoveInvalidateListener);
- }
-
- private LocationLayerCamera buildCamera(MapboxMap mapboxMap) {
- MoveGestureDetector moveGestureDetector = mock(MoveGestureDetector.class);
- OnCameraTrackingChangedListener onCameraTrackingChangedListener = mock(OnCameraTrackingChangedListener.class);
- OnCameraMoveInvalidateListener onCameraMoveInvalidateListener = mock(OnCameraMoveInvalidateListener.class);
- return new LocationLayerCamera(mapboxMap, moveGestureDetector,
- onCameraTrackingChangedListener, onCameraMoveInvalidateListener);
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerOptionsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerOptionsTest.java
deleted file mode 100644
index 5062a9b9cc..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerOptionsTest.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package com.mapbox.mapboxsdk.plugins.locationlayer;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-
-import com.mapbox.mapboxsdk.R;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
-
-import static org.junit.Assert.assertNotNull;
-import static org.mockito.Mockito.when;
-
-@RunWith(MockitoJUnitRunner.class)
-public class LocationLayerOptionsTest {
-
- @Mock
- private Context context;
- @Mock
- private TypedArray array;
- @Mock
- private Resources resources;
-
- @Rule
- public ExpectedException thrown = ExpectedException.none();
-
- @Before
- public void setUp() throws Exception {
- when(context.obtainStyledAttributes(R.style.mapbox_LocationLayer, R.styleable.mapbox_LocationLayer))
- .thenReturn(array);
- when(array.getResourceId(R.styleable.mapbox_LocationLayer_mapbox_foregroundDrawable, -1))
- .thenReturn(R.drawable.mapbox_user_icon);
- when(context.getResources()).thenReturn(resources);
- }
-
- @Test
- public void sanity() throws Exception {
- LocationLayerOptions locationLayerOptions = LocationLayerOptions.builder(context)
- .accuracyAlpha(0.5f)
- .build();
- assertNotNull(locationLayerOptions);
- }
-
- @Test
- public void passingOutOfRangeAccuracyAlpha_throwsException() throws Exception {
- thrown.expect(IllegalArgumentException.class);
- thrown.expectMessage("Location layer accuracy alpha value must be between 0.0 and "
- + "1.0.");
- LocationLayerOptions.builder(context)
- .accuracyAlpha(2f)
- .build();
- }
-
- @Test
- public void negativeElevation_causesExceptionToBeThrown() throws Exception {
- thrown.expect(IllegalArgumentException.class);
- thrown.expectMessage("Invalid shadow size -500.0. Must be >= 0");
- LocationLayerOptions.builder(context)
- .elevation(-500)
- .build();
- }
-}
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerTest.java
deleted file mode 100644
index 2a08855ab7..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerTest.java
+++ /dev/null
@@ -1,466 +0,0 @@
-package com.mapbox.mapboxsdk.plugins.locationlayer;
-
-import android.graphics.Bitmap;
-
-import com.google.gson.JsonElement;
-import com.mapbox.geojson.Feature;
-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.sources.GeoJsonSource;
-
-import org.junit.Test;
-
-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_RADIUS;
-import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_COMPASS_BEARING;
-import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_FOREGROUND_ICON_OFFSET;
-import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.PROPERTY_GPS_BEARING;
-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 org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-public class LocationLayerTest {
-
- @Test
- public void onInitialization_locationSourceIsAdded() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- LayerSourceProvider sourceProvider = buildLayerProvider();
- GeoJsonSource locationSource = mock(GeoJsonSource.class);
- when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource);
- LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
- LocationLayerOptions options = mock(LocationLayerOptions.class);
-
- new LocationLayer(mapboxMap, sourceProvider, buildFeatureProvider(options), bitmapProvider, options);
-
- verify(mapboxMap).addSource(locationSource);
- }
-
- @Test
- public void onInitialization_shadowLayerIsAdded() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- LayerSourceProvider sourceProvider = buildLayerProvider();
- Layer shadowLayer = mock(Layer.class);
- when(sourceProvider.generateLayer(SHADOW_LAYER)).thenReturn(shadowLayer);
- GeoJsonSource locationSource = mock(GeoJsonSource.class);
- when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource);
- LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
- LocationLayerOptions options = mock(LocationLayerOptions.class);
-
- new LocationLayer(mapboxMap, sourceProvider, buildFeatureProvider(options), bitmapProvider, options);
-
- verify(mapboxMap).addLayerBelow(shadowLayer, BACKGROUND_LAYER);
- }
-
- @Test
- public void onInitialization_backgroundLayerIsAdded() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- LayerSourceProvider sourceProvider = buildLayerProvider();
- Layer backgroundLayer = mock(Layer.class);
- when(sourceProvider.generateLayer(BACKGROUND_LAYER)).thenReturn(backgroundLayer);
- GeoJsonSource locationSource = mock(GeoJsonSource.class);
- when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource);
- LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
- LocationLayerOptions options = mock(LocationLayerOptions.class);
-
- new LocationLayer(mapboxMap, sourceProvider, buildFeatureProvider(options), bitmapProvider, options);
-
- verify(mapboxMap).addLayerBelow(backgroundLayer, FOREGROUND_LAYER);
- }
-
- @Test
- public void onInitialization_foregroundLayerIsAdded() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- LayerSourceProvider sourceProvider = buildLayerProvider();
- Layer foregroundLayer = mock(Layer.class);
- when(sourceProvider.generateLayer(FOREGROUND_LAYER)).thenReturn(foregroundLayer);
- GeoJsonSource locationSource = mock(GeoJsonSource.class);
- when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource);
- LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
- LocationLayerOptions options = mock(LocationLayerOptions.class);
-
- new LocationLayer(mapboxMap, sourceProvider, buildFeatureProvider(options), bitmapProvider, options);
-
- verify(mapboxMap).addLayerBelow(foregroundLayer, BEARING_LAYER);
- }
-
- @Test
- public void onInitialization_bearingLayerIsAdded() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- LayerSourceProvider sourceProvider = buildLayerProvider();
- Layer bearingLayer = mock(Layer.class);
- when(sourceProvider.generateLayer(BEARING_LAYER)).thenReturn(bearingLayer);
- GeoJsonSource locationSource = mock(GeoJsonSource.class);
- when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource);
- LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
- LocationLayerOptions options = mock(LocationLayerOptions.class);
- String layerBelow = "layer-below";
- when(options.layerBelow()).thenReturn(layerBelow);
-
- new LocationLayer(mapboxMap, sourceProvider, buildFeatureProvider(options), bitmapProvider, options);
-
- verify(mapboxMap).addLayerBelow(bearingLayer, layerBelow);
- }
-
- @Test
- public void onInitialization_accuracyLayerIsAdded() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- LayerSourceProvider sourceProvider = buildLayerProvider();
- Layer accuracyLayer = mock(Layer.class);
- when(sourceProvider.generateAccuracyLayer()).thenReturn(accuracyLayer);
- GeoJsonSource locationSource = mock(GeoJsonSource.class);
- when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource);
- LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
- LocationLayerOptions options = mock(LocationLayerOptions.class);
-
- new LocationLayer(mapboxMap, sourceProvider, buildFeatureProvider(options), bitmapProvider, options);
-
- verify(mapboxMap).addLayerBelow(accuracyLayer, BACKGROUND_LAYER);
- }
-
- @Test
- public void applyStyle_styleShadowWithValidElevation() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- LayerSourceProvider sourceProvider = buildLayerProvider();
- when(sourceProvider.generateSource(any(Feature.class))).thenReturn(mock(GeoJsonSource.class));
- LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
- Bitmap bitmap = mock(Bitmap.class);
- when(bitmapProvider.generateShadowBitmap(any(LocationLayerOptions.class))).thenReturn(bitmap);
- LocationLayerOptions options = mock(LocationLayerOptions.class);
- when(options.elevation()).thenReturn(2f);
-
- // Style is applied on initialization
- new LocationLayer(mapboxMap, sourceProvider, buildFeatureProvider(options), bitmapProvider, options);
-
- verify(mapboxMap).addImage(SHADOW_ICON, bitmap);
- }
-
- @Test
- public void applyStyle_ignoreStyleShadowWithInvalidElevation() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- LayerSourceProvider sourceProvider = buildLayerProvider();
- when(sourceProvider.generateSource(any(Feature.class))).thenReturn(mock(GeoJsonSource.class));
- LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
- Bitmap bitmap = mock(Bitmap.class);
- when(bitmapProvider.generateShadowBitmap(any(LocationLayerOptions.class))).thenReturn(bitmap);
- LocationLayerOptions options = mock(LocationLayerOptions.class);
- when(options.elevation()).thenReturn(0f);
-
- new LocationLayer(mapboxMap, sourceProvider, buildFeatureProvider(options), bitmapProvider, options);
-
- verify(mapboxMap, times(0)).addImage(SHADOW_ICON, bitmap);
- }
-
- @Test
- public void applyStyle_styleForegroundFromOptions() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- LayerSourceProvider sourceProvider = buildLayerProvider();
- when(sourceProvider.generateSource(any(Feature.class))).thenReturn(mock(GeoJsonSource.class));
- LocationLayerOptions options = mock(LocationLayerOptions.class);
- int drawableResId = 123;
- int tintColor = 456;
- when(options.foregroundDrawable()).thenReturn(drawableResId);
- when(options.foregroundTintColor()).thenReturn(tintColor);
- LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
- Bitmap bitmap = mock(Bitmap.class);
- when(bitmapProvider.generateBitmap(drawableResId, tintColor)).thenReturn(bitmap);
-
- new LocationLayer(mapboxMap, sourceProvider, buildFeatureProvider(options), bitmapProvider, options);
-
- verify(mapboxMap).addImage(FOREGROUND_ICON, bitmap);
- }
-
- @Test
- public void applyStyle_styleForegroundStaleFromOptions() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- LayerSourceProvider sourceProvider = buildLayerProvider();
- when(sourceProvider.generateSource(any(Feature.class))).thenReturn(mock(GeoJsonSource.class));
- LocationLayerOptions options = mock(LocationLayerOptions.class);
- int drawableResId = 123;
- int tintColor = 456;
- when(options.foregroundDrawableStale()).thenReturn(drawableResId);
- when(options.foregroundStaleTintColor()).thenReturn(tintColor);
- LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
- Bitmap bitmap = mock(Bitmap.class);
- when(bitmapProvider.generateBitmap(drawableResId, tintColor)).thenReturn(bitmap);
-
- new LocationLayer(mapboxMap, sourceProvider, buildFeatureProvider(options), bitmapProvider, options);
-
- verify(mapboxMap).addImage(FOREGROUND_STALE_ICON, bitmap);
- }
-
- @Test
- public void applyStyle_styleBackgroundFromOptions() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- LayerSourceProvider sourceProvider = buildLayerProvider();
- when(sourceProvider.generateSource(any(Feature.class))).thenReturn(mock(GeoJsonSource.class));
- LocationLayerOptions options = mock(LocationLayerOptions.class);
- int drawableResId = 123;
- int tintColor = 456;
- when(options.backgroundDrawable()).thenReturn(drawableResId);
- when(options.backgroundTintColor()).thenReturn(tintColor);
- LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
- Bitmap bitmap = mock(Bitmap.class);
- when(bitmapProvider.generateBitmap(drawableResId, tintColor)).thenReturn(bitmap);
-
- new LocationLayer(mapboxMap, sourceProvider, buildFeatureProvider(options), bitmapProvider, options);
-
- verify(mapboxMap).addImage(BACKGROUND_ICON, bitmap);
- }
-
- @Test
- public void applyStyle_styleBackgroundStaleFromOptions() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- LayerSourceProvider sourceProvider = buildLayerProvider();
- when(sourceProvider.generateSource(any(Feature.class))).thenReturn(mock(GeoJsonSource.class));
- LocationLayerOptions options = mock(LocationLayerOptions.class);
- int drawableResId = 123;
- int tintColor = 456;
- when(options.backgroundDrawableStale()).thenReturn(drawableResId);
- when(options.backgroundStaleTintColor()).thenReturn(tintColor);
- LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
- Bitmap bitmap = mock(Bitmap.class);
- when(bitmapProvider.generateBitmap(drawableResId, tintColor)).thenReturn(bitmap);
-
- new LocationLayer(mapboxMap, sourceProvider, buildFeatureProvider(options), bitmapProvider, options);
-
- verify(mapboxMap).addImage(BACKGROUND_STALE_ICON, bitmap);
- }
-
- @Test
- public void applyStyle_styleBearingFromOptions() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- LayerSourceProvider sourceProvider = buildLayerProvider();
- when(sourceProvider.generateSource(any(Feature.class))).thenReturn(mock(GeoJsonSource.class));
- LocationLayerOptions options = mock(LocationLayerOptions.class);
- int drawableResId = 123;
- int tintColor = 456;
- when(options.bearingDrawable()).thenReturn(drawableResId);
- when(options.bearingTintColor()).thenReturn(tintColor);
- LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
- Bitmap bitmap = mock(Bitmap.class);
- when(bitmapProvider.generateBitmap(drawableResId, tintColor)).thenReturn(bitmap);
-
- new LocationLayer(mapboxMap, sourceProvider, buildFeatureProvider(options), bitmapProvider, options);
-
- verify(mapboxMap).addImage(BEARING_ICON, bitmap);
- }
-
- @Test
- public void updateForegroundOffset_foregroundIconPropertyIsUpdated() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- LayerSourceProvider sourceProvider = buildLayerProvider();
- GeoJsonSource locationSource = mock(GeoJsonSource.class);
- when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource);
- LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
- LocationLayerOptions options = mock(LocationLayerOptions.class);
- Feature locationFeature = mock(Feature.class);
- LocationLayer layer = new LocationLayer(
- mapboxMap, sourceProvider, buildFeatureProvider(locationFeature, options), bitmapProvider, options
- );
-
- layer.updateForegroundOffset(2d);
-
- verify(locationFeature).addProperty(eq(PROPERTY_FOREGROUND_ICON_OFFSET), any(JsonElement.class));
- }
-
- @Test
- public void updateForegroundOffset_shadowPropertyIsUpdated() {
- MapboxMap mapboxMap = mock(MapboxMap.class);
- LayerSourceProvider sourceProvider = buildLayerProvider();
- GeoJsonSource locationSource = mock(GeoJsonSource.class);
- when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource);
- LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
- LocationLayerOptions options = mock(LocationLayerOptions.class);
- Feature locationFeature = mock(Feature.class);
- LocationLayer layer = new LocationLayer(
- mapboxMap, sourceProvider, buildFeatureProvider(locationFeature, options), bitmapProvider, options
- );
-
- layer.updateForegroundOffset(2d);
-
- verify(locationFeature).addProperty(eq(PROPERTY_SHADOW_ICON_OFFSET), any(JsonElement.class));
- }
-
- @Test
- public void onNewLatLngValue_locationFeatureIsUpdated() {
- GeoJsonSource locationSource = mock(GeoJsonSource.class);
- MapboxMap mapboxMap = mock(MapboxMap.class);
- when(mapboxMap.getSourceAs(LOCATION_SOURCE)).thenReturn(locationSource);
- LayerSourceProvider sourceProvider = buildLayerProvider();
- when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource);
- LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
- LocationLayerOptions options = mock(LocationLayerOptions.class);
- Feature locationFeature = mock(Feature.class);
- LocationLayer layer = new LocationLayer(
- mapboxMap, sourceProvider, buildFeatureProvider(locationFeature, options), bitmapProvider, options
- );
-
- layer.onNewLatLngValue(new LatLng());
-
- // wanted twice (once for initialization)
- verify(locationSource, times(2)).setGeoJson(locationFeature);
- }
-
- @Test
- public void onNewGpsBearingValue_locationFeatureIsUpdated() {
- GeoJsonSource locationSource = mock(GeoJsonSource.class);
- MapboxMap mapboxMap = mock(MapboxMap.class);
- when(mapboxMap.getSourceAs(LOCATION_SOURCE)).thenReturn(locationSource);
- LayerSourceProvider sourceProvider = buildLayerProvider();
- when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource);
- LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
- LocationLayerOptions options = mock(LocationLayerOptions.class);
- Feature locationFeature = mock(Feature.class);
- LocationLayer layer = new LocationLayer(
- mapboxMap, sourceProvider, buildFeatureProvider(locationFeature, options), bitmapProvider, options
- );
- layer.setRenderMode(RenderMode.GPS);
- float gpsBearing = 2f;
-
- layer.onNewGpsBearingValue(gpsBearing);
-
- verify(locationFeature).addNumberProperty(PROPERTY_GPS_BEARING, gpsBearing);
- }
-
- @Test
- public void onNewGpsBearingValue_updateIgnoredWithInvalidRenderMode() {
- GeoJsonSource locationSource = mock(GeoJsonSource.class);
- MapboxMap mapboxMap = mock(MapboxMap.class);
- when(mapboxMap.getSourceAs(LOCATION_SOURCE)).thenReturn(locationSource);
- LayerSourceProvider sourceProvider = buildLayerProvider();
- when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource);
- LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
- LocationLayerOptions options = mock(LocationLayerOptions.class);
- Feature locationFeature = mock(Feature.class);
- LocationLayer layer = new LocationLayer(
- mapboxMap, sourceProvider, buildFeatureProvider(locationFeature, options), bitmapProvider, options
- );
- layer.setRenderMode(RenderMode.COMPASS);
- float gpsBearing = 2f;
-
- layer.onNewGpsBearingValue(gpsBearing);
-
- verify(locationFeature, times(0)).addNumberProperty(PROPERTY_GPS_BEARING, gpsBearing);
- }
-
- @Test
- public void onNewCompassBearingValue_locationFeatureIsUpdated() {
- GeoJsonSource locationSource = mock(GeoJsonSource.class);
- MapboxMap mapboxMap = mock(MapboxMap.class);
- when(mapboxMap.getSourceAs(LOCATION_SOURCE)).thenReturn(locationSource);
- LayerSourceProvider sourceProvider = buildLayerProvider();
- when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource);
- LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
- LocationLayerOptions options = mock(LocationLayerOptions.class);
- Feature locationFeature = mock(Feature.class);
- LocationLayer layer = new LocationLayer(
- mapboxMap, sourceProvider, buildFeatureProvider(locationFeature, options), bitmapProvider, options
- );
- layer.setRenderMode(RenderMode.COMPASS);
- float compassBearing = 2f;
-
- layer.onNewCompassBearingValue(compassBearing);
-
- verify(locationFeature).addNumberProperty(PROPERTY_COMPASS_BEARING, compassBearing);
- }
-
- @Test
- public void onNewCompassBearingValue_updateIgnoredWithInvalidRenderMode() {
- GeoJsonSource locationSource = mock(GeoJsonSource.class);
- MapboxMap mapboxMap = mock(MapboxMap.class);
- when(mapboxMap.getSourceAs(LOCATION_SOURCE)).thenReturn(locationSource);
- LayerSourceProvider sourceProvider = buildLayerProvider();
- when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource);
- LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
- LocationLayerOptions options = mock(LocationLayerOptions.class);
- Feature locationFeature = mock(Feature.class);
- LocationLayer layer = new LocationLayer(
- mapboxMap, sourceProvider, buildFeatureProvider(locationFeature, options), bitmapProvider, options
- );
- layer.setRenderMode(RenderMode.GPS);
- float compassBearing = 2f;
-
- layer.onNewCompassBearingValue(compassBearing);
-
- verify(locationFeature, times(0)).addNumberProperty(PROPERTY_COMPASS_BEARING, compassBearing);
- }
-
- @Test
- public void onNewAccuracyRadiusValue_locationFeatureIsUpdated() {
- GeoJsonSource locationSource = mock(GeoJsonSource.class);
- MapboxMap mapboxMap = mock(MapboxMap.class);
- when(mapboxMap.getSourceAs(LOCATION_SOURCE)).thenReturn(locationSource);
- LayerSourceProvider sourceProvider = buildLayerProvider();
- when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource);
- LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
- LocationLayerOptions options = mock(LocationLayerOptions.class);
- Feature locationFeature = mock(Feature.class);
- LocationLayer layer = new LocationLayer(
- mapboxMap, sourceProvider, buildFeatureProvider(locationFeature, options), bitmapProvider, options
- );
- float accuracyRadiusValue = 2f;
-
- layer.onNewAccuracyRadiusValue(accuracyRadiusValue);
-
- verify(locationFeature).addNumberProperty(PROPERTY_ACCURACY_RADIUS, accuracyRadiusValue);
- }
-
- @Test
- public void onNewAccuracyRadiusValue_updateIgnoredWithInvalidRenderMode() {
- GeoJsonSource locationSource = mock(GeoJsonSource.class);
- MapboxMap mapboxMap = mock(MapboxMap.class);
- when(mapboxMap.getSourceAs(LOCATION_SOURCE)).thenReturn(locationSource);
- LayerSourceProvider sourceProvider = buildLayerProvider();
- when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource);
- LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
- LocationLayerOptions options = mock(LocationLayerOptions.class);
- Feature locationFeature = mock(Feature.class);
- LocationLayer layer = new LocationLayer(
- mapboxMap, sourceProvider, buildFeatureProvider(locationFeature, options), bitmapProvider, options
- );
- layer.setRenderMode(RenderMode.GPS);
- float accuracyRadiusValue = 2f;
-
- layer.onNewAccuracyRadiusValue(accuracyRadiusValue);
-
- verify(locationFeature, times(0)).addNumberProperty(PROPERTY_ACCURACY_RADIUS, accuracyRadiusValue);
- }
-
- private LayerFeatureProvider buildFeatureProvider(LocationLayerOptions options) {
- LayerFeatureProvider provider = mock(LayerFeatureProvider.class);
- when(provider.generateLocationFeature(null, options)).thenReturn(mock(Feature.class));
- return provider;
- }
-
- private LayerFeatureProvider buildFeatureProvider(Feature feature, LocationLayerOptions options) {
- LayerFeatureProvider provider = mock(LayerFeatureProvider.class);
- when(provider.generateLocationFeature(null, options)).thenReturn(feature);
- return provider;
- }
-
- private LayerSourceProvider buildLayerProvider() {
- LayerSourceProvider layerSourceProvider = mock(LayerSourceProvider.class);
- when(layerSourceProvider.generateLayer(SHADOW_LAYER)).thenReturn(mock(Layer.class));
- when(layerSourceProvider.generateLayer(BACKGROUND_LAYER)).thenReturn(mock(Layer.class));
- when(layerSourceProvider.generateLayer(FOREGROUND_LAYER)).thenReturn(mock(Layer.class));
- when(layerSourceProvider.generateLayer(BEARING_LAYER)).thenReturn(mock(Layer.class));
- when(layerSourceProvider.generateAccuracyLayer()).thenReturn(mock(Layer.class));
- return layerSourceProvider;
- }
-}
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginAnimatorCoordinatorTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginAnimatorCoordinatorTest.kt
deleted file mode 100644
index 2d7dc5b1a2..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginAnimatorCoordinatorTest.kt
+++ /dev/null
@@ -1,284 +0,0 @@
-package com.mapbox.mapboxsdk.plugins.locationlayer
-
-import android.location.Location
-import com.mapbox.mapboxsdk.camera.CameraPosition
-import com.mapbox.mapboxsdk.geometry.LatLng
-import com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.DEFAULT_TRACKING_TILT_ANIM_DURATION
-import com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.DEFAULT_TRACKING_ZOOM_ANIM_DURATION
-import com.mapbox.mapboxsdk.plugins.locationlayer.PluginAnimator.*
-import junit.framework.Assert.assertEquals
-import junit.framework.Assert.assertTrue
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mockito
-import org.robolectric.RobolectricTestRunner
-
-@RunWith(RobolectricTestRunner::class)
-class PluginAnimatorCoordinatorTest {
-
- private lateinit var pluginAnimatorCoordinator: PluginAnimatorCoordinator
- private val cameraPosition: CameraPosition = CameraPosition.DEFAULT
-
- @Before
- fun setUp() {
- pluginAnimatorCoordinator = PluginAnimatorCoordinator()
- }
-
- @Test
- fun feedNewLocation_animatorsAreCreated() {
- pluginAnimatorCoordinator.feedNewLocation(Location(""), cameraPosition, false)
-
- assertTrue(pluginAnimatorCoordinator.animatorMap[ANIMATOR_CAMERA_LATLNG] != null)
- assertTrue(pluginAnimatorCoordinator.animatorMap[ANIMATOR_CAMERA_GPS_BEARING] != null)
- assertTrue(pluginAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_LATLNG] != null)
- assertTrue(pluginAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_GPS_BEARING] != null)
- }
-
- @Test
- fun feedNewLocation_animatorValue() {
- val location = Location("")
- location.latitude = 51.0
- location.longitude = 17.0
- location.bearing = 35f
- pluginAnimatorCoordinator.feedNewLocation(location, cameraPosition, false)
-
- val cameraLatLngTarget = pluginAnimatorCoordinator.animatorMap[ANIMATOR_CAMERA_LATLNG]?.target as LatLng
- assertEquals(cameraLatLngTarget.latitude, cameraLatLngTarget.latitude)
-
- val layerLatLngTarget = pluginAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_LATLNG]?.target as LatLng
- assertEquals(layerLatLngTarget.latitude, layerLatLngTarget.latitude)
-
- val cameraBearingTarget = pluginAnimatorCoordinator.animatorMap[ANIMATOR_CAMERA_GPS_BEARING]?.target as Float
- assertEquals(location.bearing, cameraBearingTarget)
-
- val layerBearingTarget = pluginAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_GPS_BEARING]?.target as Float
- assertEquals(location.bearing, layerBearingTarget)
- }
-
- @Test
- fun feedNewLocation_isNorth_animatorsAreCreated() {
- val location = Location("")
- location.latitude = 51.0
- location.longitude = 17.0
- location.bearing = 35f
- pluginAnimatorCoordinator.feedNewLocation(location, cameraPosition, false)
-
- assertTrue(pluginAnimatorCoordinator.animatorMap[ANIMATOR_CAMERA_LATLNG] != null)
- assertTrue(pluginAnimatorCoordinator.animatorMap[ANIMATOR_CAMERA_GPS_BEARING] != null)
- assertTrue(pluginAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_LATLNG] != null)
- assertTrue(pluginAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_GPS_BEARING] != null)
- }
-
- @Test
- fun feedNewLocation_isNorth_animatorValue() {
- val location = Location("")
- location.latitude = 51.0
- location.longitude = 17.0
- location.bearing = 35f
- pluginAnimatorCoordinator.feedNewLocation(location, cameraPosition, true)
-
- val cameraLatLngTarget = pluginAnimatorCoordinator.animatorMap[ANIMATOR_CAMERA_LATLNG]?.target as LatLng
- assertEquals(cameraLatLngTarget.latitude, cameraLatLngTarget.latitude)
-
- val layerLatLngTarget = pluginAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_LATLNG]?.target as LatLng
- assertEquals(layerLatLngTarget.latitude, layerLatLngTarget.latitude)
-
- val cameraBearingTarget = pluginAnimatorCoordinator.animatorMap[ANIMATOR_CAMERA_GPS_BEARING]?.target as Float
- assertEquals(0f, cameraBearingTarget)
-
- val layerBearingTarget = pluginAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_GPS_BEARING]?.target as Float
- assertEquals(location.bearing, layerBearingTarget)
- }
-
- @Test
- fun feedNewCompassBearing_animatorsAreCreated() {
- pluginAnimatorCoordinator.feedNewCompassBearing(77f, cameraPosition)
-
- assertTrue(pluginAnimatorCoordinator.animatorMap[ANIMATOR_CAMERA_COMPASS_BEARING] != null)
- assertTrue(pluginAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_COMPASS_BEARING] != null)
- }
-
- @Test
- fun feedNewCompassBearing_animatorValue() {
- val bearing = 77f
- pluginAnimatorCoordinator.feedNewCompassBearing(bearing, cameraPosition)
-
- val cameraBearingTarget = pluginAnimatorCoordinator.animatorMap[ANIMATOR_CAMERA_COMPASS_BEARING]?.target as Float
- assertEquals(bearing, cameraBearingTarget)
-
- val layerBearingTarget = pluginAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_COMPASS_BEARING]?.target as Float
- assertEquals(bearing, layerBearingTarget)
- }
-
- @Test
- fun feedNewAccuracyRadius_animatorsCreated() {
- pluginAnimatorCoordinator.feedNewAccuracyRadius(150f, false)
-
- assertTrue(pluginAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_ACCURACY] != null)
- }
-
- @Test
- fun feedNewAccuracyRadius_animatorValue() {
- val accuracy = 150f
- pluginAnimatorCoordinator.feedNewAccuracyRadius(accuracy, false)
-
- val layerAccuracy = pluginAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_ACCURACY]?.target as Float
- assertEquals(layerAccuracy, accuracy)
-
- val animationDuration = pluginAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_ACCURACY]?.duration as Long
- assertEquals(LocationLayerConstants.ACCURACY_RADIUS_ANIMATION_DURATION, animationDuration)
- }
-
- @Test
- fun feedNewAccuracyRadius_noAnimation_animatorsCreated() {
- pluginAnimatorCoordinator.feedNewAccuracyRadius(150f, true)
-
- assertTrue(pluginAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_ACCURACY] != null)
- }
-
- @Test
- fun feedNewAccuracyRadius_noAnimation_animatorValue() {
- val accuracy = 150f
- pluginAnimatorCoordinator.feedNewAccuracyRadius(accuracy, true)
-
- val layerAccuracy = pluginAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_ACCURACY]?.target as Float
- assertEquals(layerAccuracy, accuracy)
-
- val animationDuration = pluginAnimatorCoordinator.animatorMap[ANIMATOR_LAYER_ACCURACY]?.duration as Long
- assertEquals(0L, animationDuration)
- }
-
- @Test
- fun feedNewZoomLevel_animatorsCreated() {
- pluginAnimatorCoordinator.feedNewZoomLevel(
- 15.0,
- cameraPosition,
- DEFAULT_TRACKING_ZOOM_ANIM_DURATION,
- null
- )
-
- assertTrue(pluginAnimatorCoordinator.animatorMap[ANIMATOR_ZOOM] != null)
- }
-
- @Test
- fun feedNewZoomLevel_animatorValue() {
- val zoom = 15.0f
- pluginAnimatorCoordinator.feedNewZoomLevel(
- zoom.toDouble(),
- cameraPosition,
- DEFAULT_TRACKING_ZOOM_ANIM_DURATION,
- null
- )
-
- val animationDuration = pluginAnimatorCoordinator.animatorMap[ANIMATOR_ZOOM]?.duration as Long
- assertEquals(DEFAULT_TRACKING_ZOOM_ANIM_DURATION, animationDuration)
-
- val target = pluginAnimatorCoordinator.animatorMap[ANIMATOR_ZOOM]?.target as Float
- assertEquals(zoom, target)
- }
-
- @Test
- fun feedNewTiltLevel_animatorsCreated() {
- pluginAnimatorCoordinator.feedNewTilt(
- 30.0,
- cameraPosition,
- DEFAULT_TRACKING_TILT_ANIM_DURATION,
- null
- )
-
- assertTrue(pluginAnimatorCoordinator.animatorMap[ANIMATOR_TILT] != null)
- }
-
- @Test
- fun feedNewTiltLevel_animatorValue() {
- val tilt = 30.0f
- pluginAnimatorCoordinator.feedNewTilt(
- tilt.toDouble(),
- cameraPosition,
- DEFAULT_TRACKING_TILT_ANIM_DURATION,
- null
- )
-
- val animationDuration = pluginAnimatorCoordinator.animatorMap[ANIMATOR_TILT]?.duration as Long
- assertEquals(DEFAULT_TRACKING_TILT_ANIM_DURATION, animationDuration)
-
- val target = pluginAnimatorCoordinator.animatorMap[ANIMATOR_TILT]?.target as Float
- assertEquals(tilt, target)
- }
-
- @Test
- fun cancelAllAnimators() {
- pluginAnimatorCoordinator.feedNewLocation(Location(""), cameraPosition, true)
- pluginAnimatorCoordinator.cancelAllAnimations()
-
- assertTrue(pluginAnimatorCoordinator.animatorMap[ANIMATOR_CAMERA_LATLNG] == null)
- }
-
- @Test
- fun cancelZoomAnimators() {
- pluginAnimatorCoordinator.feedNewZoomLevel(
- 15.0,
- cameraPosition,
- DEFAULT_TRACKING_ZOOM_ANIM_DURATION,
- null
- )
- pluginAnimatorCoordinator.cancelZoomAnimation()
-
- assertTrue(pluginAnimatorCoordinator.animatorMap[ANIMATOR_ZOOM] == null)
- }
-
- @Test
- fun cancelTiltAnimation() {
- pluginAnimatorCoordinator.feedNewTilt(
- 30.0,
- cameraPosition,
- DEFAULT_TRACKING_TILT_ANIM_DURATION,
- null
- )
-
- pluginAnimatorCoordinator.cancelTiltAnimation()
-
- assertTrue(pluginAnimatorCoordinator.animatorMap[ANIMATOR_TILT] == null)
- }
-
- @Test
- fun resetAllCameraAnimations_empty() {
- pluginAnimatorCoordinator.resetAllCameraAnimations(cameraPosition, false)
- assertTrue(pluginAnimatorCoordinator.animatorMap.isEmpty())
- }
-
- @Test
- fun addLayerListener() {
- val layerListener = Mockito.mock(OnLayerAnimationsValuesChangeListener::class.java)
- pluginAnimatorCoordinator.addLayerListener(layerListener)
-
- assertTrue(pluginAnimatorCoordinator.layerListeners.contains(layerListener))
- }
-
- @Test
- fun removeLayerListener() {
- val layerListener = Mockito.mock(OnLayerAnimationsValuesChangeListener::class.java)
- pluginAnimatorCoordinator.addLayerListener(layerListener)
- pluginAnimatorCoordinator.removeLayerListener(layerListener)
-
- assertTrue(pluginAnimatorCoordinator.layerListeners.isEmpty())
- }
-
- @Test
- fun addCameraListener() {
- val cameraListener = Mockito.mock(OnCameraAnimationsValuesChangeListener::class.java)
- pluginAnimatorCoordinator.addCameraListener(cameraListener)
-
- assertTrue(pluginAnimatorCoordinator.cameraListeners.contains(cameraListener))
- }
-
- @Test
- fun removeCameraListener() {
- val cameraListener = Mockito.mock(OnCameraAnimationsValuesChangeListener::class.java)
- pluginAnimatorCoordinator.addCameraListener(cameraListener)
- pluginAnimatorCoordinator.removeCameraListener(cameraListener)
-
- assertTrue(pluginAnimatorCoordinator.cameraListeners.isEmpty())
- }
-}
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/plugins/locationlayer/UtilsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/plugins/locationlayer/UtilsTest.java
deleted file mode 100644
index 9caad6c6a1..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/plugins/locationlayer/UtilsTest.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.mapbox.mapboxsdk.plugins.locationlayer;
-
-import org.junit.Test;
-
-import static junit.framework.Assert.assertEquals;
-
-public final class UtilsTest {
-
-
- @Test
- public void shortestRotation_doesReturnValueDistanceQuickestToZero() throws Exception {
- float value = Utils.shortestRotation(0, 181);
- assertEquals(360f, value);
- value = Utils.shortestRotation(0, 179);
- assertEquals(0f, value);
- value = Utils.shortestRotation(0, 180);
- assertEquals(0f, value);
- }
-
- @Test
- public void shortestRotation_doesReturnValueDistanceQuickestToFifty() throws Exception {
- float value = Utils.shortestRotation(50, 231);
- assertEquals(410f, value);
- value = Utils.shortestRotation(50, 229);
- assertEquals(50f, value);
- value = Utils.shortestRotation(50, 180);
- assertEquals(50f, value);
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/LocationComponentTest.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/LocationComponentTest.kt
new file mode 100644
index 0000000000..1d7c0c404b
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/LocationComponentTest.kt
@@ -0,0 +1,1104 @@
+package com.mapbox.mapboxsdk.location
+
+import android.Manifest
+import android.R
+import android.content.Context
+import android.graphics.Color
+import android.graphics.RectF
+import android.location.Location
+import android.support.test.espresso.Espresso.onView
+import android.support.test.espresso.IdlingRegistry
+import android.support.test.espresso.UiController
+import android.support.test.espresso.assertion.ViewAssertions.matches
+import android.support.test.espresso.matcher.ViewMatchers.*
+import android.support.test.rule.GrantPermissionRule
+import android.support.test.runner.AndroidJUnit4
+import android.support.v4.content.ContextCompat
+import com.mapbox.geojson.Point
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory
+import com.mapbox.mapboxsdk.constants.Style
+import com.mapbox.mapboxsdk.geometry.LatLng
+import com.mapbox.mapboxsdk.maps.MapboxMap
+import com.mapbox.mapboxsdk.location.LocationComponentConstants.*
+import com.mapbox.mapboxsdk.location.modes.CameraMode
+import com.mapbox.mapboxsdk.location.modes.RenderMode
+import com.mapbox.mapboxsdk.location.utils.*
+import com.mapbox.mapboxsdk.location.utils.MapboxTestingUtils.Companion.MAPBOX_HEAVY_STYLE
+import com.mapbox.mapboxsdk.location.utils.MapboxTestingUtils.Companion.MAP_CONNECTION_DELAY
+import com.mapbox.mapboxsdk.location.utils.MapboxTestingUtils.Companion.MAP_RENDER_DELAY
+import com.mapbox.mapboxsdk.location.utils.MapboxTestingUtils.Companion.pushSourceUpdates
+import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest
+import com.mapbox.mapboxsdk.testapp.activity.SingleActivity
+import com.mapbox.mapboxsdk.utils.ColorUtils
+import org.hamcrest.CoreMatchers.*
+import org.junit.*
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class LocationComponentTest : BaseActivityTest() {
+
+ @Rule
+ @JvmField
+ val permissionRule: GrantPermissionRule = GrantPermissionRule.grant(Manifest.permission.ACCESS_FINE_LOCATION)
+
+ override fun getActivityClass(): Class<*> {
+ return SingleActivity::class.java
+ }
+
+ private lateinit var styleChangeIdlingResource: StyleChangeIdlingResource
+ private val location: Location by lazy {
+ val initLocation = Location("")
+ initLocation.latitude = 15.0
+ initLocation.longitude = 17.0
+ initLocation.bearing = 10f
+ initLocation.accuracy = 150f
+ initLocation
+ }
+
+ @Before
+ override fun beforeTest() {
+ super.beforeTest()
+ styleChangeIdlingResource = StyleChangeIdlingResource()
+ IdlingRegistry.getInstance().register(styleChangeIdlingResource)
+ }
+
+ @Test
+ fun locationComponent_initializesLocationEngineCorrectlyWhenOnesNotProvided() {
+ validateTestSetup()
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context)
+
+ val locationEngine = component.locationEngine
+ assertThat(locationEngine, notNullValue())
+
+ uiController.loopMainThreadForAtLeast(MAP_CONNECTION_DELAY)
+ assertThat(locationEngine?.isConnected, `is`(true))
+ }
+ }
+
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun locationComponent_initializesLocationEngineCorrectlyWhenOnesNotProvidedButHasOptions() {
+ validateTestSetup()
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(
+ context,
+ LocationComponentOptions.builder(context)
+ .staleStateTimeout(200)
+ .enableStaleState(false)
+ .accuracyAlpha(.5f)
+ .accuracyColor(Color.BLUE)
+ .build())
+
+ val locationEngine = component.locationEngine
+ val componentOptions = component.locationComponentOptions
+
+ assertThat(locationEngine, notNullValue())
+ assertThat(componentOptions, notNullValue())
+
+ uiController.loopMainThreadForAtLeast(MAP_CONNECTION_DELAY)
+ assertThat(locationEngine?.isConnected, `is`(true))
+ assertThat(componentOptions?.accuracyAlpha(), `is`(.5f))
+ assertThat(componentOptions?.accuracyColor(), `is`(Color.BLUE))
+ }
+ }
+
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun locationComponent_doesntInitializeEngineWhenNullProvided() {
+ validateTestSetup()
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(
+ null,
+ LocationComponentOptions.builder(context)
+ .staleStateTimeout(200)
+ .enableStaleState(false)
+ .accuracyAlpha(.5f)
+ .accuracyColor(Color.BLUE)
+ .build())
+
+ val locationEngine = component.locationEngine
+ val componentOptions = component.locationComponentOptions
+
+ assertThat(locationEngine, nullValue())
+ assertThat(componentOptions, notNullValue())
+
+ uiController.loopMainThreadForAtLeast(MAP_CONNECTION_DELAY)
+ assertThat(componentOptions?.accuracyAlpha(), `is`(.5f))
+ assertThat(componentOptions?.accuracyColor(), `is`(Color.BLUE))
+ }
+ }
+
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun settingMapStyleImmediatelyBeforeLoadingComponent_doesStillLoadLayersProperly() {
+ validateTestSetup()
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ mapboxMap.setStyle(Style.LIGHT)
+ component.activateLocationComponent(context, false)
+ component.forceLocationUpdate(location)
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
+
+ assertThat(component.renderMode, `is`(equalTo(RenderMode.NORMAL)))
+ assertThat(mapboxMap.isLayerVisible(FOREGROUND_LAYER), `is`(true))
+ assertThat(mapboxMap.isLayerVisible(BACKGROUND_LAYER), `is`(true))
+ assertThat(mapboxMap.isLayerVisible(SHADOW_LAYER), `is`(true))
+ assertThat(mapboxMap.isLayerVisible(ACCURACY_LAYER), `is`(true))
+ assertThat(mapboxMap.isLayerVisible(BEARING_LAYER), `is`(false))
+ }
+ }
+
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun locationComponent_doesntShowUntilFirstLocationFix() {
+ validateTestSetup()
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+
+ // Source should be present but empty
+ val mapView = (rule.activity as SingleActivity).mapView
+ assertThat(mapboxMap.queryRenderedFeatures(
+ RectF(0f, 0f, mapView.width.toFloat(), mapView.height.toFloat()), FOREGROUND_LAYER)
+ .isEmpty(), `is`(true))
+
+ // Force the first location update
+ component.forceLocationUpdate(location)
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
+
+ // Check if the puck is visible
+ assertThat(mapboxMap.queryRenderedFeatures(location, FOREGROUND_LAYER).isEmpty(), `is`(false))
+ }
+ }
+ executeComponentTest(componentAction)
+ }
+
+ //
+ // Location Layer Options
+ //
+
+ @Test
+ fun locationComponentOptions_disablingStaleStateDoesWorkCorrectly() {
+ validateTestSetup()
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context,
+ LocationComponentOptions.builder(context)
+ .staleStateTimeout(200)
+ .enableStaleState(false)
+ .build())
+
+ component.forceLocationUpdate(location)
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
+ uiController.loopMainThreadForAtLeast(200)
+ uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY)
+
+ mapboxMap.querySourceFeatures(LOCATION_SOURCE).also {
+ it.forEach {
+ assertThat(it.getBooleanProperty(PROPERTY_LOCATION_STALE), `is`(false))
+ }
+ }
+ }
+ }
+
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun locationComponentOptions_loadsForegroundBitmapFromNameOption() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context,
+ LocationComponentOptions.builder(context)
+ .foregroundName("custom-foreground-bitmap")
+ .backgroundName("custom-background-bitmap")
+ .foregroundStaleName("custom-foreground-stale-bitmap")
+ .backgroundStaleName("custom-background-stale-bitmap")
+ .bearingName("custom-bearing-bitmap")
+ .build())
+
+ val foregroundDrawable = ContextCompat.getDrawable(context, R.drawable.ic_media_play)
+ foregroundDrawable?.let {
+ mapboxMap.addImageFromDrawable("custom-foreground-bitmap", it)
+ mapboxMap.addImageFromDrawable("custom-background-bitmap", it)
+ mapboxMap.addImageFromDrawable("custom-foreground-stale-bitmap", it)
+ mapboxMap.addImageFromDrawable("custom-background-stale-bitmap", it)
+ mapboxMap.addImageFromDrawable("custom-bearing-bitmap", it)
+ }
+
+ component.forceLocationUpdate(location)
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
+ assertThat(mapboxMap.queryRenderedFeatures(location, FOREGROUND_LAYER).isEmpty(), `is`(false))
+
+ val feature = mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0]
+ assertThat(feature.getStringProperty(PROPERTY_FOREGROUND_ICON), `is`(equalTo("custom-foreground-bitmap")))
+ assertThat(feature.getStringProperty(PROPERTY_BACKGROUND_ICON), `is`(equalTo("custom-background-bitmap")))
+ assertThat(feature.getStringProperty(PROPERTY_FOREGROUND_STALE_ICON), `is`(equalTo("custom-foreground-stale-bitmap")))
+ assertThat(feature.getStringProperty(PROPERTY_BACKGROUND_STALE_ICON), `is`(equalTo("custom-background-stale-bitmap")))
+ assertThat(feature.getStringProperty(PROPERTY_BEARING_ICON), `is`(equalTo("custom-bearing-bitmap")))
+ }
+ }
+
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun locationComponentOptions_loadsGpsNameWithGpsRenderMode() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context,
+ LocationComponentOptions.builder(context)
+ .foregroundName("custom-foreground-bitmap")
+ .gpsName("custom-gps-bitmap")
+ .build())
+
+ component.renderMode = RenderMode.GPS
+ component.forceLocationUpdate(location)
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
+ val foregroundDrawable = ContextCompat.getDrawable(context, R.drawable.ic_media_play)
+ foregroundDrawable?.let {
+ mapboxMap.addImageFromDrawable("custom-foreground-bitmap", it)
+ mapboxMap.addImageFromDrawable("custom-gps-bitmap", it)
+ }
+
+ val foregroundId = mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].getStringProperty(PROPERTY_FOREGROUND_ICON)
+ assertThat(foregroundId, `is`(equalTo("custom-gps-bitmap")))
+ }
+ }
+
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun locationComponentOptions_customIconNameRevertsToDefault() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context,
+ LocationComponentOptions.builder(context)
+ .foregroundName("custom-foreground-bitmap")
+ .gpsName("custom-gps-bitmap")
+ .build())
+
+ component.renderMode = RenderMode.GPS
+ component.forceLocationUpdate(location)
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
+
+ val foregroundId = mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].getStringProperty(PROPERTY_FOREGROUND_ICON)
+ assertThat(foregroundId, `is`(equalTo("custom-gps-bitmap")))
+
+ component.applyStyle(LocationComponentOptions.builder(context).build())
+ uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY)
+
+ val revertedForegroundId = mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].getStringProperty(PROPERTY_FOREGROUND_ICON)
+ assertThat(revertedForegroundId, `is`(equalTo(FOREGROUND_ICON)))
+ }
+ }
+
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun locationComponentOptions_customGpsIconNameChangeBackWithMode() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+
+ component.activateLocationComponent(context,
+ LocationComponentOptions.builder(context)
+ .gpsName("custom-gps-bitmap")
+ .build())
+
+ component.renderMode = RenderMode.GPS
+ component.forceLocationUpdate(location)
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
+
+ val foregroundId = mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].getStringProperty(PROPERTY_FOREGROUND_ICON)
+ assertThat(foregroundId, `is`(equalTo("custom-gps-bitmap")))
+
+ component.renderMode = RenderMode.NORMAL
+ uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY)
+
+ val revertedForegroundId = mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].getStringProperty(PROPERTY_FOREGROUND_ICON)
+ assertThat(revertedForegroundId, `is`(equalTo(FOREGROUND_ICON)))
+ }
+ }
+
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun stillStaleAfterResuming() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context,
+ LocationComponentOptions.builder(context)
+ .staleStateTimeout(200)
+ .build())
+
+ component.forceLocationUpdate(location)
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
+ uiController.loopMainThreadForAtLeast(250) // engaging stale state
+ uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY)
+ assertThat(mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].getBooleanProperty(PROPERTY_LOCATION_STALE), `is`(true))
+
+ component.onStop()
+ component.onStart()
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
+
+ assertThat(mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].getBooleanProperty(PROPERTY_LOCATION_STALE), `is`(true))
+ assertThat(mapboxMap.isLayerVisible(ACCURACY_LAYER), `is`(false))
+ }
+ }
+
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun stillNotStaleAfterResuming() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.forceLocationUpdate(location)
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
+ assertThat(mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].getBooleanProperty(PROPERTY_LOCATION_STALE), `is`(false))
+
+ component.onStop()
+ component.onStart()
+ uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY)
+
+ assertThat(mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].getBooleanProperty(PROPERTY_LOCATION_STALE), `is`(false))
+ assertThat(mapboxMap.isLayerVisible(ACCURACY_LAYER), `is`(true))
+ }
+ }
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun locationComponentOptions_accuracyRingWithColor() {
+ val color = Color.parseColor("#4A90E2")
+ val rgbaColor = ColorUtils.colorToRgbaString(color)
+
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context,
+ LocationComponentOptions.builder(context)
+ .accuracyColor(color)
+ .build())
+
+ component.forceLocationUpdate(location)
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
+
+ // Check that the source property changes correctly
+ mapboxMap.querySourceFeatures(LOCATION_SOURCE).also {
+ it.forEach {
+ assertThat(it.getStringProperty(PROPERTY_ACCURACY_COLOR), `is`(equalTo(rgbaColor)))
+ }
+ }
+ }
+ }
+
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun forceLocationUpdate_doesMoveLocationLayerIconToCorrectPosition() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.forceLocationUpdate(location)
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
+
+ val point: Point = mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].geometry() as Point
+
+ assertThat(component.locationEngine, nullValue())
+ assertEquals(point.latitude(), location.latitude, 0.1)
+ assertEquals(point.longitude(), location.longitude, 0.1)
+ }
+ }
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun disablingComponentHidesPuck() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.forceLocationUpdate(location)
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
+
+ val point: Point = mapboxMap.queryRenderedFeatures(location, FOREGROUND_LAYER)[0].geometry() as Point
+ assertEquals(point.latitude(), location.latitude, 0.1)
+ assertEquals(point.longitude(), location.longitude, 0.1)
+
+ component.deactivateLocationComponent()
+ uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY)
+ assertThat(mapboxMap.queryRenderedFeatures(location, FOREGROUND_LAYER).isEmpty(), `is`(true))
+ }
+ }
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun disablingComponentAndChangingStyleAllowsToEnableAgain() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.forceLocationUpdate(location)
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
+
+ component.deactivateLocationComponent()
+ mapboxMap.setStyle(Style.LIGHT)
+
+ component.activateLocationComponent(context, false)
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
+ assertThat(mapboxMap.isLayerVisible(FOREGROUND_LAYER), `is`(true))
+ }
+ }
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun lifecycle_isDisabledOnStart() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ assertThat(component.isLocationLayerEnabled, `is`(false))
+ component.onStop()
+ component.onStart()
+ assertThat(component.isLocationLayerEnabled, `is`(false))
+ component.activateLocationComponent(context, false)
+ assertThat(component.isLocationLayerEnabled, `is`(true))
+ }
+ }
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun lifecycle_keepsEnabledWhenStoppedAndStarted() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ assertThat(component.isLocationLayerEnabled, `is`(true))
+ component.onStop()
+ component.onStart()
+ assertThat(component.isLocationLayerEnabled, `is`(true))
+ }
+ }
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun lifecycle_keepsDisabledWhenStoppedAndStarted() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.deactivateLocationComponent()
+ assertThat(component.isLocationLayerEnabled, `is`(false))
+ component.onStop()
+ component.onStart()
+ assertThat(component.isLocationLayerEnabled, `is`(false))
+ }
+ }
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun lifecycle_ableToChangeStyleAfterResuming() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+
+ component.onStop()
+ component.onStart()
+
+ mapboxMap.setStyle(Style.DARK)
+ uiController.loopMainThreadForAtLeast(MAP_CONNECTION_DELAY)
+ }
+ }
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun lifecycle_interruptedDuringStyleChange() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ mapboxMap.setStyle(Style.DARK)
+ component.onStop()
+ component.onStart()
+ uiController.loopMainThreadForAtLeast(MAP_CONNECTION_DELAY)
+ }
+ }
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun lifecycle_forceLocationUpdateAfterStopped() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.onStop()
+ component.forceLocationUpdate(location)
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
+
+ assertThat(mapboxMap.querySourceFeatures(LOCATION_SOURCE).isEmpty(), `is`(true))
+ }
+ }
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun lifecycle_acceptAndReuseLocationUpdatesBeforeLayerStarted() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.onStop()
+ component.forceLocationUpdate(location)
+ component.onStart()
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
+
+ val point: Point = mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].geometry() as Point
+ assertEquals(point.latitude(), location.latitude, 0.1)
+ assertEquals(point.longitude(), location.longitude, 0.1)
+ }
+ }
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun lifecycle_lifecycleChangeRightAfterStyleReload() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.forceLocationUpdate(location)
+ mapboxMap.setStyle(Style.LIGHT)
+ component.onStop()
+ uiController.loopMainThreadForAtLeast(MAP_CONNECTION_DELAY)
+ component.onStart()
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
+
+ val point: Point = mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].geometry() as Point
+ assertEquals(point.latitude(), location.latitude, 0.1)
+ assertEquals(point.longitude(), location.longitude, 0.1)
+
+ assertThat(mapboxMap.isLayerVisible(FOREGROUND_LAYER), `is`(true))
+ assertThat(mapboxMap.isLayerVisible(BACKGROUND_LAYER), `is`(true))
+ assertThat(mapboxMap.isLayerVisible(SHADOW_LAYER), `is`(true))
+ assertThat(mapboxMap.isLayerVisible(ACCURACY_LAYER), `is`(true))
+ assertThat(mapboxMap.isLayerVisible(BEARING_LAYER), `is`(false))
+ }
+ }
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun mapChange_settingComponentStyle() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ styleChangeIdlingResource.waitForStyle((rule.activity as SingleActivity).mapView, mapboxMap, MAPBOX_HEAVY_STYLE)
+ val options = LocationComponentOptions.builder(context)
+ .accuracyColor(Color.RED)
+ .build()
+
+ pushSourceUpdates(styleChangeIdlingResource) {
+ component.applyStyle(options)
+ }
+
+ uiController.loopMainThreadForAtLeast(MAP_CONNECTION_DELAY)
+ }
+ }
+ executeComponentTest(componentAction)
+
+ // Waiting for style to finish loading while pushing updates
+ onView(withId(R.id.content)).check(matches(isDisplayed()))
+ }
+
+ @Test
+ fun mapChange_forcingLocation() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ styleChangeIdlingResource.waitForStyle((rule.activity as SingleActivity).mapView, mapboxMap, MAPBOX_HEAVY_STYLE)
+
+ pushSourceUpdates(styleChangeIdlingResource) {
+ component.forceLocationUpdate(location)
+ }
+
+ uiController.loopMainThreadForAtLeast(MAP_CONNECTION_DELAY)
+ }
+ }
+ executeComponentTest(componentAction)
+
+ // Waiting for style to finish loading while pushing updates
+ onView(withId(R.id.content)).check(matches(isDisplayed()))
+ }
+
+ @Test
+ fun mapChange_settingMapStyleBeforeComponentCreation() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ styleChangeIdlingResource.waitForStyle((rule.activity as SingleActivity).mapView, mapboxMap, MAPBOX_HEAVY_STYLE)
+ component.activateLocationComponent(context, false)
+
+ val options = LocationComponentOptions.builder(context)
+ .accuracyColor(Color.RED)
+ .build()
+
+ pushSourceUpdates(styleChangeIdlingResource) {
+ component.forceLocationUpdate(location)
+ component.applyStyle(options)
+ }
+ }
+ }
+ executeComponentTest(componentAction)
+
+ // Waiting for style to finish loading while pushing updates
+ onView(withId(R.id.content)).check(matches(isDisplayed()))
+ }
+
+ @Test
+ fun animators_layerBearingCorrect() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.renderMode = RenderMode.GPS
+ location.bearing = 77f
+ component.forceLocationUpdate(location)
+ uiController.loopMainThreadForAtLeast(MAX_ANIMATION_DURATION_MS + MAP_RENDER_DELAY)
+ assertEquals(77.0, mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].getNumberProperty(PROPERTY_GPS_BEARING) as Double, 0.1)
+
+ location.bearing = 92f
+ component.forceLocationUpdate(location)
+ uiController.loopMainThreadForAtLeast(MAX_ANIMATION_DURATION_MS + MAP_RENDER_DELAY) // Waiting for the animation to finish
+ assertEquals(92.0, mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].getNumberProperty(PROPERTY_GPS_BEARING) as Double, 0.1)
+ }
+ }
+
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun animators_cameraLatLngBearingCorrect() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.cameraMode = CameraMode.TRACKING_GPS
+ location.bearing = 77f
+ component.forceLocationUpdate(location)
+ uiController.loopMainThreadForAtLeast(MAX_ANIMATION_DURATION_MS + MAP_RENDER_DELAY)
+ assertEquals(77.0, mapboxMap.cameraPosition.bearing, 0.1)
+ assertEquals(location.latitude, mapboxMap.cameraPosition.target.latitude, 0.1)
+ assertEquals(location.longitude, mapboxMap.cameraPosition.target.longitude, 0.1)
+
+ location.bearing = 92f
+ location.latitude = 30.0
+ location.longitude = 35.0
+ component.forceLocationUpdate(location)
+ uiController.loopMainThreadForAtLeast(MAX_ANIMATION_DURATION_MS + MAP_RENDER_DELAY) // Waiting for the animation to finish
+ assertEquals(92.0, mapboxMap.cameraPosition.bearing, 0.1)
+ assertEquals(location.latitude, mapboxMap.cameraPosition.target.latitude, 0.1)
+ assertEquals(location.longitude, mapboxMap.cameraPosition.target.longitude, 0.1)
+ }
+ }
+
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun animators_cameraBearingCorrect() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.cameraMode = CameraMode.NONE_GPS
+ val latitude = mapboxMap.cameraPosition.target.latitude
+ val longitude = mapboxMap.cameraPosition.target.longitude
+
+ location.bearing = 77f
+ component.forceLocationUpdate(location)
+ uiController.loopMainThreadForAtLeast(MAX_ANIMATION_DURATION_MS + MAP_RENDER_DELAY)
+ assertEquals(77.0, mapboxMap.cameraPosition.bearing, 0.1)
+ assertEquals(latitude, mapboxMap.cameraPosition.target.latitude, 0.1)
+ assertEquals(longitude, mapboxMap.cameraPosition.target.longitude, 0.1)
+
+ location.bearing = 92f
+ location.latitude = 30.0
+ location.longitude = 35.0
+ component.forceLocationUpdate(location)
+ uiController.loopMainThreadForAtLeast(MAX_ANIMATION_DURATION_MS + MAP_RENDER_DELAY)
+ assertEquals(92.0, mapboxMap.cameraPosition.bearing, 0.1)
+ assertEquals(latitude, mapboxMap.cameraPosition.target.latitude, 0.1)
+ assertEquals(longitude, mapboxMap.cameraPosition.target.longitude, 0.1)
+ }
+ }
+
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun animators_cameraNoneCorrect() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.cameraMode = CameraMode.NONE
+ val latitude = mapboxMap.cameraPosition.target.latitude
+ val longitude = mapboxMap.cameraPosition.target.longitude
+ val bearing = mapboxMap.cameraPosition.bearing
+
+ location.bearing = 77f
+ component.forceLocationUpdate(location)
+ uiController.loopMainThreadForAtLeast(MAX_ANIMATION_DURATION_MS + MAP_RENDER_DELAY)
+ assertEquals(bearing, mapboxMap.cameraPosition.bearing, 0.1)
+ assertEquals(latitude, mapboxMap.cameraPosition.target.latitude, 0.1)
+ assertEquals(longitude, mapboxMap.cameraPosition.target.longitude, 0.1)
+
+ location.bearing = 92f
+ location.latitude = 30.0
+ location.longitude = 35.0
+ component.forceLocationUpdate(location)
+ uiController.loopMainThreadForAtLeast(MAX_ANIMATION_DURATION_MS + MAP_RENDER_DELAY) // Waiting for the animation to finish
+ assertEquals(bearing, mapboxMap.cameraPosition.bearing, 0.1)
+ assertEquals(latitude, mapboxMap.cameraPosition.target.latitude, 0.1)
+ assertEquals(longitude, mapboxMap.cameraPosition.target.longitude, 0.1)
+ }
+ }
+
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun animators_focalPointAdjustment() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.cameraMode = CameraMode.TRACKING
+ component.cameraMode = CameraMode.NONE
+ component.forceLocationUpdate(location)
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
+
+ assertThat(mapboxMap.uiSettings.focalPoint, nullValue())
+ }
+ }
+
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun animators_dontZoomWhileNotTracking() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.cameraMode = CameraMode.NONE
+ val zoom = mapboxMap.cameraPosition.zoom
+ component.zoomWhileTracking(10.0)
+ uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_ZOOM_ANIM_DURATION)
+
+ assertEquals(zoom, mapboxMap.cameraPosition.zoom, 0.1)
+ }
+ }
+
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun animators_zoomWhileTracking() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.cameraMode = CameraMode.TRACKING
+ component.zoomWhileTracking(10.0)
+ uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_ZOOM_ANIM_DURATION)
+
+ assertEquals(10.0, mapboxMap.cameraPosition.zoom, 0.1)
+ }
+ }
+
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ @Ignore
+ fun animators_zoomWhileTrackingCanceledOnModeChange() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.cameraMode = CameraMode.TRACKING
+ component.zoomWhileTracking(15.0)
+ uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_ZOOM_ANIM_DURATION / 2)
+ component.cameraMode = CameraMode.NONE
+ uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_ZOOM_ANIM_DURATION / 2)
+
+ assertEquals(15.0 / 2.0, mapboxMap.cameraPosition.zoom, 3.0)
+ }
+ }
+
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun animators_dontZoomWhileStopped() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+
+ component.cameraMode = CameraMode.TRACKING
+ uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY)
+ val zoom = mapboxMap.cameraPosition.zoom
+
+ component.onStop()
+ component.zoomWhileTracking(10.0)
+ uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_ZOOM_ANIM_DURATION)
+
+ assertEquals(zoom, mapboxMap.cameraPosition.zoom, 0.1)
+ }
+ }
+
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ @Ignore
+ fun animators_cancelZoomWhileTracking() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.cameraMode = CameraMode.TRACKING
+ component.zoomWhileTracking(15.0)
+ uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_ZOOM_ANIM_DURATION / 2)
+ component.cancelZoomWhileTrackingAnimation()
+ uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_ZOOM_ANIM_DURATION / 2)
+
+ assertEquals(15.0 / 2.0, mapboxMap.cameraPosition.zoom, 3.0)
+ }
+ }
+
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun animators_dontTiltWhileNotTracking() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.cameraMode = CameraMode.NONE
+ val tilt = mapboxMap.cameraPosition.tilt
+ component.tiltWhileTracking(30.0)
+ uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_TILT_ANIM_DURATION)
+
+ assertEquals(tilt, mapboxMap.cameraPosition.tilt, 0.1)
+ }
+ }
+
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun animators_tiltWhileTracking() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.cameraMode = CameraMode.TRACKING
+ component.tiltWhileTracking(30.0)
+ uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_TILT_ANIM_DURATION)
+
+ assertEquals(30.0, mapboxMap.cameraPosition.tilt, 0.1)
+ }
+ }
+
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ @Ignore
+ fun animators_tiltWhileTrackingCanceledOnModeChange() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.cameraMode = CameraMode.TRACKING
+ component.tiltWhileTracking(30.0)
+ uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_TILT_ANIM_DURATION / 2)
+ component.cameraMode = CameraMode.NONE
+ uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_TILT_ANIM_DURATION / 2)
+
+ assertEquals(30.0 / 2.0, mapboxMap.cameraPosition.tilt, 3.0)
+ }
+ }
+
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun animators_dontTiltWhileStopped() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.cameraMode = CameraMode.TRACKING
+ val tilt = mapboxMap.cameraPosition.tilt
+
+ component.onStop()
+ component.tiltWhileTracking(30.0)
+ uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_TILT_ANIM_DURATION)
+
+ assertEquals(tilt, mapboxMap.cameraPosition.tilt, 0.1)
+ }
+ }
+
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ @Ignore
+ fun animators_cancelTiltWhileTracking() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.cameraMode = CameraMode.TRACKING
+ component.tiltWhileTracking(30.0)
+ uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_TILT_ANIM_DURATION / 2)
+ component.cancelTiltWhileTrackingAnimation()
+ uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_TILT_ANIM_DURATION / 2)
+
+ assertEquals(30.0 / 2.0, mapboxMap.cameraPosition.tilt, 3.0)
+ }
+ }
+
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun cameraPositionAdjustedToTrackingModeWhenComponentEnabled() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.cameraMode = CameraMode.TRACKING_GPS
+ component.forceLocationUpdate(location)
+ component.deactivateLocationComponent()
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLng(LatLng(51.0, 17.0)))
+ mapboxMap.moveCamera(CameraUpdateFactory.bearingTo(90.0))
+ component.activateLocationComponent(context, false)
+ uiController.loopMainThreadForAtLeast(MAX_ANIMATION_DURATION_MS + MAP_RENDER_DELAY)
+
+ assertEquals(location.bearing.toDouble(), mapboxMap.cameraPosition.bearing, 0.1)
+ assertEquals(location.latitude, mapboxMap.cameraPosition.target.latitude, 0.1)
+ assertEquals(location.longitude, mapboxMap.cameraPosition.target.longitude, 0.1)
+ }
+ }
+
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun compassEngine_onComponentInitializedDefaultIsProvided() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ assertTrue(component.compassEngine is LocationComponentCompassEngine)
+ }
+ }
+
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun compassEngine_changesWhenNewProvided() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ val engine: CompassEngine = object : CompassEngine {
+ override fun addCompassListener(compassListener: CompassListener) {
+ }
+
+ override fun removeCompassListener(compassListener: CompassListener) {
+ }
+
+ override fun getLastHeading(): Float {
+ return 0f
+ }
+
+ override fun getLastAccuracySensorStatus(): Int {
+ return 0
+ }
+
+ override fun onStart() {
+ }
+
+ override fun onStop() {
+ }
+ }
+
+ component.compassEngine = engine
+ assertThat(component.compassEngine, notNullValue())
+ assertThat(component.compassEngine, `is`(equalTo(engine)))
+ }
+ }
+
+ executeComponentTest(componentAction)
+ }
+
+ @After
+ override fun afterTest() {
+ super.afterTest()
+ IdlingRegistry.getInstance().unregister(styleChangeIdlingResource)
+ }
+
+ private fun executeComponentTest(listener: LocationComponentAction.OnPerformLocationComponentAction) {
+ onView(withId(R.id.content)).perform(LocationComponentAction(mapboxMap, listener))
+ }
+}
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/LocationLayerControllerTest.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/LocationLayerControllerTest.kt
new file mode 100644
index 0000000000..c50d1817fd
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/LocationLayerControllerTest.kt
@@ -0,0 +1,360 @@
+package com.mapbox.mapboxsdk.location
+
+import android.Manifest
+import android.R
+import android.content.Context
+import android.location.Location
+import android.support.test.espresso.Espresso.onView
+import android.support.test.espresso.IdlingRegistry
+import android.support.test.espresso.UiController
+import android.support.test.espresso.assertion.ViewAssertions.matches
+import android.support.test.espresso.matcher.ViewMatchers.isDisplayed
+import android.support.test.espresso.matcher.ViewMatchers.withId
+import android.support.test.rule.GrantPermissionRule
+import android.support.test.rule.GrantPermissionRule.grant
+import android.support.test.runner.AndroidJUnit4
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory
+import com.mapbox.mapboxsdk.constants.Style
+import com.mapbox.mapboxsdk.geometry.LatLng
+import com.mapbox.mapboxsdk.maps.MapboxMap
+import com.mapbox.mapboxsdk.location.LocationComponentConstants.*
+import com.mapbox.mapboxsdk.location.modes.RenderMode
+import com.mapbox.mapboxsdk.location.utils.*
+import com.mapbox.mapboxsdk.location.utils.MapboxTestingUtils.Companion.MAPBOX_HEAVY_STYLE
+import com.mapbox.mapboxsdk.location.utils.MapboxTestingUtils.Companion.MAP_CONNECTION_DELAY
+import com.mapbox.mapboxsdk.location.utils.MapboxTestingUtils.Companion.MAP_RENDER_DELAY
+import com.mapbox.mapboxsdk.location.utils.MapboxTestingUtils.Companion.pushSourceUpdates
+import com.mapbox.mapboxsdk.style.sources.GeoJsonSource
+import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest
+import com.mapbox.mapboxsdk.testapp.activity.SingleActivity
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.CoreMatchers.notNullValue
+import org.hamcrest.Matchers.equalTo
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class LocationLayerControllerTest : BaseActivityTest() {
+
+ @Rule
+ @JvmField
+ val permissionRule: GrantPermissionRule = grant(Manifest.permission.ACCESS_FINE_LOCATION)
+
+ override fun getActivityClass(): Class<*> {
+ return SingleActivity::class.java
+ }
+
+ private lateinit var styleChangeIdlingResource: StyleChangeIdlingResource
+ private val location: Location by lazy {
+ val initLocation = Location("")
+ initLocation.latitude = 15.0
+ initLocation.longitude = 17.0
+ initLocation.bearing = 10f
+ initLocation.accuracy = 150f
+ initLocation
+ }
+
+ @Before
+ override fun beforeTest() {
+ super.beforeTest()
+ styleChangeIdlingResource = StyleChangeIdlingResource()
+ IdlingRegistry.getInstance().register(styleChangeIdlingResource)
+ }
+
+ //
+ // Location Source
+ //
+
+ @Test
+ fun renderModeNormal_sourceDoesGetAdded() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.renderMode = RenderMode.NORMAL
+ uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY)
+ assertThat(mapboxMap.getSource(LOCATION_SOURCE), notNullValue())
+ }
+ }
+ executeComponentTest(componentAction)
+ }
+
+ //
+ // Location Layers
+ //
+
+ @Test
+ fun renderModeNormal_trackingNormalLayersDoGetAdded() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.renderMode = RenderMode.NORMAL
+ component.forceLocationUpdate(location)
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
+ assertThat(mapboxMap.isLayerVisible(FOREGROUND_LAYER), `is`(true))
+ assertThat(mapboxMap.isLayerVisible(BACKGROUND_LAYER), `is`(true))
+ assertThat(mapboxMap.isLayerVisible(SHADOW_LAYER), `is`(true))
+ assertThat(mapboxMap.isLayerVisible(ACCURACY_LAYER), `is`(true))
+ assertThat(mapboxMap.isLayerVisible(BEARING_LAYER), `is`(false))
+ }
+ }
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun renderModeCompass_bearingLayersDoGetAdded() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.renderMode = RenderMode.COMPASS
+ component.forceLocationUpdate(location)
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
+ assertThat(mapboxMap.isLayerVisible(FOREGROUND_LAYER), `is`(true))
+ assertThat(mapboxMap.isLayerVisible(BACKGROUND_LAYER), `is`(true))
+ assertThat(mapboxMap.isLayerVisible(SHADOW_LAYER), `is`(true))
+ assertThat(mapboxMap.isLayerVisible(ACCURACY_LAYER), `is`(true))
+ assertThat(mapboxMap.isLayerVisible(BEARING_LAYER), `is`(true))
+ }
+ }
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun renderModeGps_navigationLayersDoGetAdded() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.renderMode = RenderMode.GPS
+ component.forceLocationUpdate(location)
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
+ assertThat(mapboxMap.isLayerVisible(FOREGROUND_LAYER), `is`(true))
+ assertThat(mapboxMap.isLayerVisible(BACKGROUND_LAYER), `is`(true))
+ assertThat(mapboxMap.isLayerVisible(SHADOW_LAYER), `is`(false))
+ assertThat(mapboxMap.isLayerVisible(ACCURACY_LAYER), `is`(false))
+ assertThat(mapboxMap.isLayerVisible(BEARING_LAYER), `is`(false))
+ }
+ }
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun dontShowPuckWhenRenderModeSetAndComponentDisabled() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.forceLocationUpdate(location)
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
+ component.deactivateLocationComponent()
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER, shouldDisappear = true)
+ component.renderMode = RenderMode.GPS
+ uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY)
+ assertThat(mapboxMap.isLayerVisible(FOREGROUND_LAYER), `is`(false))
+ assertThat(mapboxMap.isLayerVisible(BACKGROUND_LAYER), `is`(false))
+ assertThat(mapboxMap.isLayerVisible(SHADOW_LAYER), `is`(false))
+ assertThat(mapboxMap.isLayerVisible(ACCURACY_LAYER), `is`(false))
+ assertThat(mapboxMap.isLayerVisible(BEARING_LAYER), `is`(false))
+ }
+ }
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun whenLocationComponentDisabled_doesSetAllLayersToVisibilityNone() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.renderMode = RenderMode.NORMAL
+ component.forceLocationUpdate(location)
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
+ component.deactivateLocationComponent()
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER, shouldDisappear = true)
+ uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY)
+
+ // Check that all layers visibilities are set to none
+ assertThat(mapboxMap.isLayerVisible(FOREGROUND_LAYER), `is`(false))
+ assertThat(mapboxMap.isLayerVisible(BACKGROUND_LAYER), `is`(false))
+ assertThat(mapboxMap.isLayerVisible(SHADOW_LAYER), `is`(false))
+ assertThat(mapboxMap.isLayerVisible(ACCURACY_LAYER), `is`(false))
+ assertThat(mapboxMap.isLayerVisible(BEARING_LAYER), `is`(false))
+ }
+ }
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun onMapChange_locationComponentLayersDoGetRedrawn() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.renderMode = RenderMode.NORMAL
+ component.forceLocationUpdate(location)
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
+ mapboxMap.setStyleUrl(Style.LIGHT)
+ uiController.loopMainThreadForAtLeast(MAP_CONNECTION_DELAY)
+ component.forceLocationUpdate(location)
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
+
+ assertThat(component.renderMode, `is`(equalTo(RenderMode.NORMAL)))
+
+ // Check that the Source has been re-added to the new map style
+ val source: GeoJsonSource? = mapboxMap.getSourceAs(LOCATION_SOURCE)
+ assertThat(source, notNullValue())
+
+ // Check that all layers visibilities are set to visible
+ assertThat(mapboxMap.isLayerVisible(FOREGROUND_LAYER), `is`(true))
+ assertThat(mapboxMap.isLayerVisible(BACKGROUND_LAYER), `is`(true))
+ assertThat(mapboxMap.isLayerVisible(SHADOW_LAYER), `is`(true))
+ assertThat(mapboxMap.isLayerVisible(ACCURACY_LAYER), `is`(true))
+ assertThat(mapboxMap.isLayerVisible(BEARING_LAYER), `is`(false))
+ }
+ }
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun whenStyleChanged_continuesUsingStaleIcons() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.applyStyle(LocationComponentOptions.builder(context).staleStateTimeout(100).build())
+ component.forceLocationUpdate(location)
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
+ uiController.loopMainThreadForAtLeast(150)
+ uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY)
+
+ assertThat(mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].getBooleanProperty(PROPERTY_LOCATION_STALE), `is`(true))
+
+ mapboxMap.setStyleUrl(Style.LIGHT)
+ uiController.loopMainThreadForAtLeast(MAP_CONNECTION_DELAY)
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
+
+ assertThat(mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].getBooleanProperty(PROPERTY_LOCATION_STALE), `is`(true))
+ }
+ }
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun whenStyleChanged_staleStateChanges() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.applyStyle(LocationComponentOptions.builder(context).staleStateTimeout(1).build())
+ styleChangeIdlingResource.waitForStyle((rule.activity as SingleActivity).mapView, mapboxMap, MAPBOX_HEAVY_STYLE)
+ pushSourceUpdates(styleChangeIdlingResource) {
+ component.forceLocationUpdate(location)
+ }
+ }
+ }
+ executeComponentTest(componentAction)
+
+ // Waiting for style to finish loading while pushing updates
+ onView(withId(R.id.content)).check(matches(isDisplayed()))
+ }
+
+ @Test
+ fun whenStyleChanged_layerVisibilityUpdates() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ styleChangeIdlingResource.waitForStyle((rule.activity as SingleActivity).mapView, mapboxMap, MAPBOX_HEAVY_STYLE)
+ var show = true
+ pushSourceUpdates(styleChangeIdlingResource) {
+ if (show) {
+ component.activateLocationComponent(context, false)
+ } else {
+ component.deactivateLocationComponent()
+ }
+ show = !show
+ }
+
+ uiController.loopMainThreadForAtLeast(MAP_CONNECTION_DELAY)
+ }
+ }
+ executeComponentTest(componentAction)
+
+ // Waiting for style to finish loading while pushing updates
+ onView(withId(R.id.content)).check(matches(isDisplayed()))
+ }
+
+ @Test
+ fun accuracy_visibleWithNewLocation() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(location), 16.0))
+ component.forceLocationUpdate(location)
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
+ uiController.loopMainThreadForAtLeast(ACCURACY_RADIUS_ANIMATION_DURATION)
+
+ assertEquals(Utils.calculateZoomLevelRadius(mapboxMap, location) /*meters projected to radius on zoom 16*/,
+ mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0]
+ .getNumberProperty(PROPERTY_ACCURACY_RADIUS).toFloat(), 0.1f)
+ }
+ }
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun accuracy_visibleWhenCameraEased() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ component.forceLocationUpdate(location)
+ mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
+ mapboxMap.easeCamera(CameraUpdateFactory.newLatLngZoom(LatLng(location), 16.0), 300)
+ uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY + 300)
+
+ assertEquals(Utils.calculateZoomLevelRadius(mapboxMap, location) /*meters projected to radius on zoom 16*/,
+ mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0]
+ .getNumberProperty(PROPERTY_ACCURACY_RADIUS).toFloat(), 0.1f)
+ }
+ }
+ executeComponentTest(componentAction)
+ }
+
+ @Test
+ fun accuracy_visibleWhenCameraMoved() {
+ val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
+ override fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap,
+ uiController: UiController, context: Context) {
+ component.activateLocationComponent(context, false)
+ uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY)
+ component.forceLocationUpdate(location)
+ uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY)
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(location), 16.0))
+ uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY + 300)
+
+ assertEquals(Utils.calculateZoomLevelRadius(mapboxMap, location) /*meters projected to radius on zoom 16*/,
+ mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0]
+ .getNumberProperty(PROPERTY_ACCURACY_RADIUS).toFloat(), 0.1f)
+ }
+ }
+ executeComponentTest(componentAction)
+ }
+
+ @After
+ override fun afterTest() {
+ super.afterTest()
+ IdlingRegistry.getInstance().unregister(styleChangeIdlingResource)
+ }
+
+ private fun executeComponentTest(listener: LocationComponentAction.OnPerformLocationComponentAction) {
+ onView(withId(R.id.content)).perform(LocationComponentAction(mapboxMap, listener))
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/utils/LocationComponentAction.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/utils/LocationComponentAction.kt
new file mode 100644
index 0000000000..ad80a3333e
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/utils/LocationComponentAction.kt
@@ -0,0 +1,40 @@
+package com.mapbox.mapboxsdk.location.utils
+
+import android.content.Context
+import android.support.test.espresso.UiController
+import android.support.test.espresso.ViewAction
+import android.support.test.espresso.matcher.ViewMatchers.isDisplayed
+import android.view.View
+import com.mapbox.mapboxsdk.maps.MapboxMap
+import com.mapbox.mapboxsdk.location.LocationComponent
+import org.hamcrest.Matcher
+
+class LocationComponentAction(private val mapboxMap: MapboxMap,
+ private val onPerformLocationComponentAction: OnPerformLocationComponentAction) : ViewAction {
+
+ override fun getConstraints(): Matcher {
+ return isDisplayed()
+ }
+
+ override fun getDescription(): String {
+ return javaClass.simpleName
+ }
+
+ override fun perform(uiController: UiController, view: View) {
+ val component = mapboxMap.locationComponent
+
+ while (mapboxMap.getSource("mapbox-location-source") == null) {
+ uiController.loopMainThreadForAtLeast(MapboxTestingUtils.MAP_RENDER_DELAY)
+ }
+
+ onPerformLocationComponentAction.onLocationComponentAction(
+ component,
+ mapboxMap,
+ uiController,
+ view.context)
+ }
+
+ interface OnPerformLocationComponentAction {
+ fun onLocationComponentAction(component: LocationComponent, mapboxMap: MapboxMap, uiController: UiController, context: Context)
+ }
+}
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/utils/MapboxTestingUtils.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/utils/MapboxTestingUtils.kt
new file mode 100644
index 0000000000..591901385f
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/utils/MapboxTestingUtils.kt
@@ -0,0 +1,105 @@
+package com.mapbox.mapboxsdk.location.utils
+
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
+import android.location.Location
+import android.os.Handler
+import android.os.Looper
+import android.support.test.espresso.UiController
+import com.mapbox.geojson.Feature
+import com.mapbox.mapboxsdk.geometry.LatLng
+import com.mapbox.mapboxsdk.maps.MapboxMap
+import com.mapbox.mapboxsdk.style.layers.Property
+import com.mapbox.mapboxsdk.style.sources.GeoJsonSource
+
+fun MapboxMap.querySourceFeatures(sourceId: String): List {
+ return this.getSourceAs(sourceId)?.querySourceFeatures(null) ?: emptyList()
+}
+
+fun MapboxMap.queryRenderedFeatures(location: Location, layerId: String): List {
+ val latLng = LatLng(location.latitude, location.longitude)
+ val point = this.projection.toScreenLocation(latLng)
+ return this.queryRenderedFeatures(point, layerId)
+}
+
+fun MapboxMap.isLayerVisible(layerId: String): Boolean {
+ return this.getLayer(layerId)?.visibility?.value?.equals(Property.VISIBLE)!!
+}
+
+fun MapboxMap.waitForSource(uiController: UiController, sourceId: String) {
+ var counter = 0
+ val delay = MapboxTestingUtils.MAP_RENDER_DELAY
+ while (this.querySourceFeatures(sourceId).isEmpty() && delay * counter < MapboxTestingUtils.RENDER_TIMEOUT) {
+ uiController.loopMainThreadForAtLeast(delay)
+ counter++
+ }
+}
+
+fun MapboxMap.waitForLayer(uiController: UiController, location: Location, layerId: String, shouldDisappear: Boolean = false) {
+ var counter = 0
+ val delay = MapboxTestingUtils.MAP_RENDER_DELAY
+ while (
+ if (shouldDisappear) this.queryRenderedFeatures(location, layerId).isNotEmpty() else this.queryRenderedFeatures(location, layerId).isEmpty()
+ && delay * counter < MapboxTestingUtils.RENDER_TIMEOUT) {
+ uiController.loopMainThreadForAtLeast(delay)
+ counter++
+ }
+}
+
+
+class MapboxTestingUtils {
+ companion object {
+
+ const val MAP_RENDER_DELAY = 250L
+ const val MAP_CONNECTION_DELAY = 1000L
+ const val RENDER_TIMEOUT = 3_000L
+
+ /**
+ * Used to increase style load time for stress testing.
+ */
+ const val MAPBOX_HEAVY_STYLE = "asset://heavy_style.json"
+
+ private const val DATA_PUSH_INTERVAL = 1L
+
+ /**
+ * Pushes data updates every [DATA_PUSH_INTERVAL] milliseconds until the style has been loaded,
+ * checked with [StyleChangeIdlingResource].
+ */
+ fun pushSourceUpdates(styleChangeIdlingResource: StyleChangeIdlingResource, update: () -> Unit) {
+ val mainHandler = Handler(Looper.getMainLooper())
+ val runnable = object : Runnable {
+ override fun run() {
+ update.invoke()
+ if (!styleChangeIdlingResource.isIdleNow) {
+ mainHandler.postDelayed(this, DATA_PUSH_INTERVAL)
+ }
+ }
+ }
+
+ if (!styleChangeIdlingResource.isIdleNow) {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ runnable.run()
+ } else {
+ mainHandler.post(runnable)
+ }
+ }
+ }
+ }
+}
+
+fun MapboxMap.addImageFromDrawable(string: String, drawable: Drawable) {
+ val bitmapFromDrawable = getBitmapFromDrawable(drawable)
+ this.addImage(string, bitmapFromDrawable)
+}
+
+private fun getBitmapFromDrawable(drawable: Drawable): Bitmap {
+ if (drawable is BitmapDrawable) return drawable.bitmap
+ val bitmap = Bitmap.createBitmap(drawable.intrinsicWidth,
+ drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
+ val canvas = Canvas(bitmap)
+ drawable.setBounds(0, 0, canvas.width, canvas.height)
+ drawable.draw(canvas)
+ return bitmap
+}
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/utils/OnMapFragmentReadyIdlingResource.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/utils/OnMapFragmentReadyIdlingResource.kt
new file mode 100644
index 0000000000..4d02a4d2bf
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/utils/OnMapFragmentReadyIdlingResource.kt
@@ -0,0 +1,39 @@
+package com.mapbox.mapboxsdk.location.utils
+
+import android.os.Handler
+import android.os.Looper
+import android.support.test.espresso.IdlingResource
+
+import com.mapbox.mapboxsdk.maps.MapboxMap
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback
+import com.mapbox.mapboxsdk.maps.SupportMapFragment
+
+class OnMapFragmentReadyIdlingResource(fragment: SupportMapFragment?) : IdlingResource, OnMapReadyCallback {
+
+ lateinit var mapboxMap: MapboxMap
+
+ private var resourceCallback: IdlingResource.ResourceCallback? = null
+
+ init {
+ Handler(Looper.getMainLooper()).post {
+ fragment?.getMapAsync(this)
+ }
+ }
+
+ override fun getName(): String {
+ return javaClass.simpleName
+ }
+
+ override fun isIdleNow(): Boolean {
+ return this::mapboxMap.isInitialized
+ }
+
+ override fun registerIdleTransitionCallback(resourceCallback: IdlingResource.ResourceCallback) {
+ this.resourceCallback = resourceCallback
+ }
+
+ override fun onMapReady(mapboxMap: MapboxMap) {
+ this.mapboxMap = mapboxMap
+ resourceCallback?.onTransitionToIdle()
+ }
+}
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/utils/OnMapReadyIdlingResource.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/utils/OnMapReadyIdlingResource.java
new file mode 100644
index 0000000000..9adb30ee32
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/utils/OnMapReadyIdlingResource.java
@@ -0,0 +1,63 @@
+package com.mapbox.mapboxsdk.location.utils;
+
+import android.app.Activity;
+import android.os.Handler;
+import android.os.Looper;
+import android.support.test.espresso.IdlingResource;
+
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+
+import java.lang.reflect.Field;
+
+public class OnMapReadyIdlingResource implements IdlingResource, OnMapReadyCallback {
+
+ private MapboxMap mapboxMap;
+ private MapView mapView;
+ private IdlingResource.ResourceCallback resourceCallback;
+
+ public OnMapReadyIdlingResource(Activity activity) {
+ new Handler(Looper.getMainLooper()).post(() -> {
+ try {
+ Field field = activity.getClass().getDeclaredField("mapView");
+ field.setAccessible(true);
+ mapView = ((MapView) field.get(activity));
+ mapView.getMapAsync(this);
+ } catch (Exception err) {
+ throw new RuntimeException(err);
+ }
+ });
+ }
+
+ @Override
+ public String getName() {
+ return getClass().getSimpleName();
+ }
+
+ @Override
+ public boolean isIdleNow() {
+ return mapboxMap != null;
+ }
+
+ @Override
+ public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
+ this.resourceCallback = resourceCallback;
+ }
+
+ public MapView getMapView() {
+ return mapView;
+ }
+
+ public MapboxMap getMapboxMap() {
+ return mapboxMap;
+ }
+
+ @Override
+ public void onMapReady(MapboxMap mapboxMap) {
+ this.mapboxMap = mapboxMap;
+ if (resourceCallback != null) {
+ resourceCallback.onTransitionToIdle();
+ }
+ }
+}
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/utils/StyleChangeIdlingResource.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/utils/StyleChangeIdlingResource.kt
new file mode 100644
index 0000000000..0f37498a29
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/utils/StyleChangeIdlingResource.kt
@@ -0,0 +1,46 @@
+package com.mapbox.mapboxsdk.location.utils
+
+import android.support.test.espresso.IdlingResource
+import com.mapbox.mapboxsdk.maps.MapView
+import com.mapbox.mapboxsdk.maps.MapboxMap
+
+/**
+ * Resource, that's idling until the provided style is loaded.
+ * Remember to add any espresso action (like view assertion) after the [waitForStyle] call
+ * for the test to keep running.
+ */
+class StyleChangeIdlingResource : IdlingResource {
+
+ private var callback: IdlingResource.ResourceCallback? = null
+ private var isIdle = true
+
+ override fun getName(): String {
+ return javaClass.simpleName
+ }
+
+ override fun isIdleNow(): Boolean {
+ return isIdle
+ }
+
+ override fun registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback?) {
+ this.callback = callback
+ }
+
+ private fun setIdle() {
+ isIdle = true
+ callback?.onTransitionToIdle()
+ }
+
+ fun waitForStyle(mapView: MapView, mapboxMap: MapboxMap, styleUrl: String) {
+ isIdle = false
+ mapView.addOnMapChangedListener(object : MapView.OnMapChangedListener {
+ override fun onMapChanged(change: Int) {
+ if (change == MapView.DID_FINISH_LOADING_STYLE) {
+ mapView.removeOnMapChangedListener(this)
+ setIdle()
+ }
+ }
+ })
+ mapboxMap.setStyleUrl(styleUrl)
+ }
+}
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerPluginTest.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerPluginTest.kt
deleted file mode 100644
index e9552f974b..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerPluginTest.kt
+++ /dev/null
@@ -1,1104 +0,0 @@
-package com.mapbox.mapboxsdk.plugins.locationlayer
-
-import android.Manifest
-import android.R
-import android.content.Context
-import android.graphics.Color
-import android.graphics.RectF
-import android.location.Location
-import android.support.test.espresso.Espresso.onView
-import android.support.test.espresso.IdlingRegistry
-import android.support.test.espresso.UiController
-import android.support.test.espresso.assertion.ViewAssertions.matches
-import android.support.test.espresso.matcher.ViewMatchers.*
-import android.support.test.rule.GrantPermissionRule
-import android.support.test.runner.AndroidJUnit4
-import android.support.v4.content.ContextCompat
-import com.mapbox.geojson.Point
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory
-import com.mapbox.mapboxsdk.constants.Style
-import com.mapbox.mapboxsdk.geometry.LatLng
-import com.mapbox.mapboxsdk.maps.MapboxMap
-import com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.*
-import com.mapbox.mapboxsdk.plugins.locationlayer.modes.CameraMode
-import com.mapbox.mapboxsdk.plugins.locationlayer.modes.RenderMode
-import com.mapbox.mapboxsdk.plugins.utils.*
-import com.mapbox.mapboxsdk.plugins.utils.MapboxTestingUtils.Companion.MAPBOX_HEAVY_STYLE
-import com.mapbox.mapboxsdk.plugins.utils.MapboxTestingUtils.Companion.MAP_CONNECTION_DELAY
-import com.mapbox.mapboxsdk.plugins.utils.MapboxTestingUtils.Companion.MAP_RENDER_DELAY
-import com.mapbox.mapboxsdk.plugins.utils.MapboxTestingUtils.Companion.pushSourceUpdates
-import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest
-import com.mapbox.mapboxsdk.testapp.activity.SingleActivity
-import com.mapbox.mapboxsdk.utils.ColorUtils
-import org.hamcrest.CoreMatchers.*
-import org.junit.*
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertTrue
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-class LocationLayerPluginTest : BaseActivityTest() {
-
- @Rule
- @JvmField
- val permissionRule: GrantPermissionRule = GrantPermissionRule.grant(Manifest.permission.ACCESS_FINE_LOCATION)
-
- override fun getActivityClass(): Class<*> {
- return SingleActivity::class.java
- }
-
- private lateinit var styleChangeIdlingResource: StyleChangeIdlingResource
- private val location: Location by lazy {
- val initLocation = Location("test")
- initLocation.latitude = 15.0
- initLocation.longitude = 17.0
- initLocation.bearing = 10f
- initLocation.accuracy = 150f
- initLocation
- }
-
- @Before
- override fun beforeTest() {
- super.beforeTest()
- styleChangeIdlingResource = StyleChangeIdlingResource()
- IdlingRegistry.getInstance().register(styleChangeIdlingResource)
- }
-
- @Test
- fun locationLayerPlugin_initializesLocationEngineCorrectlyWhenOnesNotProvided() {
- validateTestSetup()
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context)
-
- val locationEngine = plugin.locationEngine
- assertThat(locationEngine, notNullValue())
-
- uiController.loopMainThreadForAtLeast(MAP_CONNECTION_DELAY)
- assertThat(locationEngine?.isConnected, `is`(true))
- }
- }
-
- executePluginTest(pluginAction)
- }
-
- @Test
- fun locationLayerPlugin_initializesLocationEngineCorrectlyWhenOnesNotProvidedButHasOptions() {
- validateTestSetup()
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(
- context,
- LocationLayerOptions.builder(context)
- .staleStateTimeout(200)
- .enableStaleState(false)
- .accuracyAlpha(.5f)
- .accuracyColor(Color.BLUE)
- .build())
-
- val locationEngine = plugin.locationEngine
- val pluginOptions = plugin.locationLayerOptions
-
- assertThat(locationEngine, notNullValue())
- assertThat(pluginOptions, notNullValue())
-
- uiController.loopMainThreadForAtLeast(MAP_CONNECTION_DELAY)
- assertThat(locationEngine?.isConnected, `is`(true))
- assertThat(pluginOptions?.accuracyAlpha(), `is`(.5f))
- assertThat(pluginOptions?.accuracyColor(), `is`(Color.BLUE))
- }
- }
-
- executePluginTest(pluginAction)
- }
-
- @Test
- fun locationLayerPlugin_doesntInitializeEngineWhenNullProvided() {
- validateTestSetup()
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(
- null,
- LocationLayerOptions.builder(context)
- .staleStateTimeout(200)
- .enableStaleState(false)
- .accuracyAlpha(.5f)
- .accuracyColor(Color.BLUE)
- .build())
-
- val locationEngine = plugin.locationEngine
- val pluginOptions = plugin.locationLayerOptions
-
- assertThat(locationEngine, nullValue())
- assertThat(pluginOptions, notNullValue())
-
- uiController.loopMainThreadForAtLeast(MAP_CONNECTION_DELAY)
- assertThat(pluginOptions?.accuracyAlpha(), `is`(.5f))
- assertThat(pluginOptions?.accuracyColor(), `is`(Color.BLUE))
- }
- }
-
- executePluginTest(pluginAction)
- }
-
- @Test
- fun settingMapStyleImmediatelyBeforeLoadingPlugin_doesStillLoadLayersProperly() {
- validateTestSetup()
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- mapboxMap.setStyle(Style.LIGHT)
- plugin.activateLocationLayerPlugin(context, false)
- plugin.forceLocationUpdate(location)
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
-
- assertThat(plugin.renderMode, `is`(equalTo(RenderMode.NORMAL)))
- assertThat(mapboxMap.isLayerVisible(FOREGROUND_LAYER), `is`(true))
- assertThat(mapboxMap.isLayerVisible(BACKGROUND_LAYER), `is`(true))
- assertThat(mapboxMap.isLayerVisible(SHADOW_LAYER), `is`(true))
- assertThat(mapboxMap.isLayerVisible(ACCURACY_LAYER), `is`(true))
- assertThat(mapboxMap.isLayerVisible(BEARING_LAYER), `is`(false))
- }
- }
-
- executePluginTest(pluginAction)
- }
-
- @Test
- fun locationLayer_doesntShowUntilFirstLocationFix() {
- validateTestSetup()
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
-
- // Source should be present but empty
- val mapView = (rule.activity as SingleActivity).mapView
- assertThat(mapboxMap.queryRenderedFeatures(
- RectF(0f, 0f, mapView.width.toFloat(), mapView.height.toFloat()), FOREGROUND_LAYER)
- .isEmpty(), `is`(true))
-
- // Force the first location update
- plugin.forceLocationUpdate(location)
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
-
- // Check if the puck is visible
- assertThat(mapboxMap.queryRenderedFeatures(location, FOREGROUND_LAYER).isEmpty(), `is`(false))
- }
- }
- executePluginTest(pluginAction)
- }
-
- //
- // Location Layer Options
- //
-
- @Test
- fun locationLayerOptions_disablingStaleStateDoesWorkCorrectly() {
- validateTestSetup()
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context,
- LocationLayerOptions.builder(context)
- .staleStateTimeout(200)
- .enableStaleState(false)
- .build())
-
- plugin.forceLocationUpdate(location)
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
- uiController.loopMainThreadForAtLeast(200)
- uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY)
-
- mapboxMap.querySourceFeatures(LOCATION_SOURCE).also {
- it.forEach {
- assertThat(it.getBooleanProperty(PROPERTY_LOCATION_STALE), `is`(false))
- }
- }
- }
- }
-
- executePluginTest(pluginAction)
- }
-
- @Test
- fun locationLayerOptions_loadsForegroundBitmapFromNameOption() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context,
- LocationLayerOptions.builder(context)
- .foregroundName("custom-foreground-bitmap")
- .backgroundName("custom-background-bitmap")
- .foregroundStaleName("custom-foreground-stale-bitmap")
- .backgroundStaleName("custom-background-stale-bitmap")
- .bearingName("custom-bearing-bitmap")
- .build())
-
- val foregroundDrawable = ContextCompat.getDrawable(context, R.drawable.ic_media_play)
- foregroundDrawable?.let {
- mapboxMap.addImageFromDrawable("custom-foreground-bitmap", it)
- mapboxMap.addImageFromDrawable("custom-background-bitmap", it)
- mapboxMap.addImageFromDrawable("custom-foreground-stale-bitmap", it)
- mapboxMap.addImageFromDrawable("custom-background-stale-bitmap", it)
- mapboxMap.addImageFromDrawable("custom-bearing-bitmap", it)
- }
-
- plugin.forceLocationUpdate(location)
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
- assertThat(mapboxMap.queryRenderedFeatures(location, FOREGROUND_LAYER).isEmpty(), `is`(false))
-
- val feature = mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0]
- assertThat(feature.getStringProperty(PROPERTY_FOREGROUND_ICON), `is`(equalTo("custom-foreground-bitmap")))
- assertThat(feature.getStringProperty(PROPERTY_BACKGROUND_ICON), `is`(equalTo("custom-background-bitmap")))
- assertThat(feature.getStringProperty(PROPERTY_FOREGROUND_STALE_ICON), `is`(equalTo("custom-foreground-stale-bitmap")))
- assertThat(feature.getStringProperty(PROPERTY_BACKGROUND_STALE_ICON), `is`(equalTo("custom-background-stale-bitmap")))
- assertThat(feature.getStringProperty(PROPERTY_BEARING_ICON), `is`(equalTo("custom-bearing-bitmap")))
- }
- }
-
- executePluginTest(pluginAction)
- }
-
- @Test
- fun locationLayerOptions_loadsGpsNameWithGpsRenderMode() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context,
- LocationLayerOptions.builder(context)
- .foregroundName("custom-foreground-bitmap")
- .gpsName("custom-gps-bitmap")
- .build())
-
- plugin.renderMode = RenderMode.GPS
- plugin.forceLocationUpdate(location)
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
- val foregroundDrawable = ContextCompat.getDrawable(context, R.drawable.ic_media_play)
- foregroundDrawable?.let {
- mapboxMap.addImageFromDrawable("custom-foreground-bitmap", it)
- mapboxMap.addImageFromDrawable("custom-gps-bitmap", it)
- }
-
- val foregroundId = mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].getStringProperty(PROPERTY_FOREGROUND_ICON)
- assertThat(foregroundId, `is`(equalTo("custom-gps-bitmap")))
- }
- }
-
- executePluginTest(pluginAction)
- }
-
- @Test
- fun locationLayerOptions_customIconNameRevertsToDefault() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context,
- LocationLayerOptions.builder(context)
- .foregroundName("custom-foreground-bitmap")
- .gpsName("custom-gps-bitmap")
- .build())
-
- plugin.renderMode = RenderMode.GPS
- plugin.forceLocationUpdate(location)
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
-
- val foregroundId = mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].getStringProperty(PROPERTY_FOREGROUND_ICON)
- assertThat(foregroundId, `is`(equalTo("custom-gps-bitmap")))
-
- plugin.applyStyle(LocationLayerOptions.builder(context).build())
- uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY)
-
- val revertedForegroundId = mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].getStringProperty(PROPERTY_FOREGROUND_ICON)
- assertThat(revertedForegroundId, `is`(equalTo(FOREGROUND_ICON)))
- }
- }
-
- executePluginTest(pluginAction)
- }
-
- @Test
- fun locationLayerOptions_customGpsIconNameChangeBackWithMode() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
-
- plugin.activateLocationLayerPlugin(context,
- LocationLayerOptions.builder(context)
- .gpsName("custom-gps-bitmap")
- .build())
-
- plugin.renderMode = RenderMode.GPS
- plugin.forceLocationUpdate(location)
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
-
- val foregroundId = mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].getStringProperty(PROPERTY_FOREGROUND_ICON)
- assertThat(foregroundId, `is`(equalTo("custom-gps-bitmap")))
-
- plugin.renderMode = RenderMode.NORMAL
- uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY)
-
- val revertedForegroundId = mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].getStringProperty(PROPERTY_FOREGROUND_ICON)
- assertThat(revertedForegroundId, `is`(equalTo(FOREGROUND_ICON)))
- }
- }
-
- executePluginTest(pluginAction)
- }
-
- @Test
- fun stillStaleAfterResuming() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context,
- LocationLayerOptions.builder(context)
- .staleStateTimeout(200)
- .build())
-
- plugin.forceLocationUpdate(location)
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
- uiController.loopMainThreadForAtLeast(250) // engaging stale state
- uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY)
- assertThat(mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].getBooleanProperty(PROPERTY_LOCATION_STALE), `is`(true))
-
- plugin.onStop()
- plugin.onStart()
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
-
- assertThat(mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].getBooleanProperty(PROPERTY_LOCATION_STALE), `is`(true))
- assertThat(mapboxMap.isLayerVisible(ACCURACY_LAYER), `is`(false))
- }
- }
-
- executePluginTest(pluginAction)
- }
-
- @Test
- fun stillNotStaleAfterResuming() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.forceLocationUpdate(location)
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
- assertThat(mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].getBooleanProperty(PROPERTY_LOCATION_STALE), `is`(false))
-
- plugin.onStop()
- plugin.onStart()
- uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY)
-
- assertThat(mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].getBooleanProperty(PROPERTY_LOCATION_STALE), `is`(false))
- assertThat(mapboxMap.isLayerVisible(ACCURACY_LAYER), `is`(true))
- }
- }
- executePluginTest(pluginAction)
- }
-
- @Test
- fun locationLayerOptions_accuracyRingWithColor() {
- val color = Color.parseColor("#4A90E2")
- val rgbaColor = ColorUtils.colorToRgbaString(color)
-
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context,
- LocationLayerOptions.builder(context)
- .accuracyColor(color)
- .build())
-
- plugin.forceLocationUpdate(location)
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
-
- // Check that the source property changes correctly
- mapboxMap.querySourceFeatures(LOCATION_SOURCE).also {
- it.forEach {
- assertThat(it.getStringProperty(PROPERTY_ACCURACY_COLOR), `is`(equalTo(rgbaColor)))
- }
- }
- }
- }
-
- executePluginTest(pluginAction)
- }
-
- @Test
- fun forceLocationUpdate_doesMoveLocationLayerIconToCorrectPosition() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.forceLocationUpdate(location)
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
-
- val point: Point = mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].geometry() as Point
-
- assertThat(plugin.locationEngine, nullValue())
- assertEquals(point.latitude(), location.latitude, 0.1)
- assertEquals(point.longitude(), location.longitude, 0.1)
- }
- }
- executePluginTest(pluginAction)
- }
-
- @Test
- fun disablingPluginHidesPuck() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.forceLocationUpdate(location)
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
-
- val point: Point = mapboxMap.queryRenderedFeatures(location, FOREGROUND_LAYER)[0].geometry() as Point
- assertEquals(point.latitude(), location.latitude, 0.1)
- assertEquals(point.longitude(), location.longitude, 0.1)
-
- plugin.deactivateLocationLayerPlugin()
- uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY)
- assertThat(mapboxMap.queryRenderedFeatures(location, FOREGROUND_LAYER).isEmpty(), `is`(true))
- }
- }
- executePluginTest(pluginAction)
- }
-
- @Test
- fun disablingPluginAndChangingStyleAllowsToEnableAgain() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.forceLocationUpdate(location)
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
-
- plugin.deactivateLocationLayerPlugin()
- mapboxMap.setStyle(Style.LIGHT)
-
- plugin.activateLocationLayerPlugin(context, false)
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
- assertThat(mapboxMap.isLayerVisible(FOREGROUND_LAYER), `is`(true))
- }
- }
- executePluginTest(pluginAction)
- }
-
- @Test
- fun lifecycle_isDisabledOnStart() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- assertThat(plugin.isLocationLayerEnabled, `is`(false))
- plugin.onStop()
- plugin.onStart()
- assertThat(plugin.isLocationLayerEnabled, `is`(false))
- plugin.activateLocationLayerPlugin(context, false)
- assertThat(plugin.isLocationLayerEnabled, `is`(true))
- }
- }
- executePluginTest(pluginAction)
- }
-
- @Test
- fun lifecycle_keepsEnabledWhenStoppedAndStarted() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- assertThat(plugin.isLocationLayerEnabled, `is`(true))
- plugin.onStop()
- plugin.onStart()
- assertThat(plugin.isLocationLayerEnabled, `is`(true))
- }
- }
- executePluginTest(pluginAction)
- }
-
- @Test
- fun lifecycle_keepsDisabledWhenStoppedAndStarted() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.deactivateLocationLayerPlugin()
- assertThat(plugin.isLocationLayerEnabled, `is`(false))
- plugin.onStop()
- plugin.onStart()
- assertThat(plugin.isLocationLayerEnabled, `is`(false))
- }
- }
- executePluginTest(pluginAction)
- }
-
- @Test
- fun lifecycle_ableToChangeStyleAfterResuming() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
-
- plugin.onStop()
- plugin.onStart()
-
- mapboxMap.setStyle(Style.DARK)
- uiController.loopMainThreadForAtLeast(MAP_CONNECTION_DELAY)
- }
- }
- executePluginTest(pluginAction)
- }
-
- @Test
- fun lifecycle_interruptedDuringStyleChange() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- mapboxMap.setStyle(Style.DARK)
- plugin.onStop()
- plugin.onStart()
- uiController.loopMainThreadForAtLeast(MAP_CONNECTION_DELAY)
- }
- }
- executePluginTest(pluginAction)
- }
-
- @Test
- fun lifecycle_forceLocationUpdateAfterStopped() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.onStop()
- plugin.forceLocationUpdate(location)
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
-
- assertThat(mapboxMap.querySourceFeatures(LOCATION_SOURCE).isEmpty(), `is`(true))
- }
- }
- executePluginTest(pluginAction)
- }
-
- @Test
- fun lifecycle_acceptAndReuseLocationUpdatesBeforeLayerStarted() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.onStop()
- plugin.forceLocationUpdate(location)
- plugin.onStart()
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
-
- val point: Point = mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].geometry() as Point
- assertEquals(point.latitude(), location.latitude, 0.1)
- assertEquals(point.longitude(), location.longitude, 0.1)
- }
- }
- executePluginTest(pluginAction)
- }
-
- @Test
- fun lifecycle_lifecycleChangeRightAfterStyleReload() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.forceLocationUpdate(location)
- mapboxMap.setStyle(Style.LIGHT)
- plugin.onStop()
- uiController.loopMainThreadForAtLeast(MAP_CONNECTION_DELAY)
- plugin.onStart()
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
-
- val point: Point = mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].geometry() as Point
- assertEquals(point.latitude(), location.latitude, 0.1)
- assertEquals(point.longitude(), location.longitude, 0.1)
-
- assertThat(mapboxMap.isLayerVisible(FOREGROUND_LAYER), `is`(true))
- assertThat(mapboxMap.isLayerVisible(BACKGROUND_LAYER), `is`(true))
- assertThat(mapboxMap.isLayerVisible(SHADOW_LAYER), `is`(true))
- assertThat(mapboxMap.isLayerVisible(ACCURACY_LAYER), `is`(true))
- assertThat(mapboxMap.isLayerVisible(BEARING_LAYER), `is`(false))
- }
- }
- executePluginTest(pluginAction)
- }
-
- @Test
- fun mapChange_settingPluginStyle() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- styleChangeIdlingResource.waitForStyle((rule.activity as SingleActivity).mapView, mapboxMap, MAPBOX_HEAVY_STYLE)
- val options = LocationLayerOptions.builder(context)
- .accuracyColor(Color.RED)
- .build()
-
- pushSourceUpdates(styleChangeIdlingResource) {
- plugin.applyStyle(options)
- }
-
- uiController.loopMainThreadForAtLeast(MAP_CONNECTION_DELAY)
- }
- }
- executePluginTest(pluginAction)
-
- // Waiting for style to finish loading while pushing updates
- onView(withId(R.id.content)).check(matches(isDisplayed()))
- }
-
- @Test
- fun mapChange_forcingLocation() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- styleChangeIdlingResource.waitForStyle((rule.activity as SingleActivity).mapView, mapboxMap, MAPBOX_HEAVY_STYLE)
-
- pushSourceUpdates(styleChangeIdlingResource) {
- plugin.forceLocationUpdate(location)
- }
-
- uiController.loopMainThreadForAtLeast(MAP_CONNECTION_DELAY)
- }
- }
- executePluginTest(pluginAction)
-
- // Waiting for style to finish loading while pushing updates
- onView(withId(R.id.content)).check(matches(isDisplayed()))
- }
-
- @Test
- fun mapChange_settingMapStyleBeforePluginCreation() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- styleChangeIdlingResource.waitForStyle((rule.activity as SingleActivity).mapView, mapboxMap, MAPBOX_HEAVY_STYLE)
- plugin.activateLocationLayerPlugin(context, false)
-
- val options = LocationLayerOptions.builder(context)
- .accuracyColor(Color.RED)
- .build()
-
- pushSourceUpdates(styleChangeIdlingResource) {
- plugin.forceLocationUpdate(location)
- plugin.applyStyle(options)
- }
- }
- }
- executePluginTest(pluginAction)
-
- // Waiting for style to finish loading while pushing updates
- onView(withId(R.id.content)).check(matches(isDisplayed()))
- }
-
- @Test
- fun animators_layerBearingCorrect() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.renderMode = RenderMode.GPS
- location.bearing = 77f
- plugin.forceLocationUpdate(location)
- uiController.loopMainThreadForAtLeast(MAX_ANIMATION_DURATION_MS + MAP_RENDER_DELAY)
- assertEquals(77.0, mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].getNumberProperty(PROPERTY_GPS_BEARING) as Double, 0.1)
-
- location.bearing = 92f
- plugin.forceLocationUpdate(location)
- uiController.loopMainThreadForAtLeast(MAX_ANIMATION_DURATION_MS + MAP_RENDER_DELAY) // Waiting for the animation to finish
- assertEquals(92.0, mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].getNumberProperty(PROPERTY_GPS_BEARING) as Double, 0.1)
- }
- }
-
- executePluginTest(pluginAction)
- }
-
- @Test
- fun animators_cameraLatLngBearingCorrect() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.cameraMode = CameraMode.TRACKING_GPS
- location.bearing = 77f
- plugin.forceLocationUpdate(location)
- uiController.loopMainThreadForAtLeast(MAX_ANIMATION_DURATION_MS + MAP_RENDER_DELAY)
- assertEquals(77.0, mapboxMap.cameraPosition.bearing, 0.1)
- assertEquals(location.latitude, mapboxMap.cameraPosition.target.latitude, 0.1)
- assertEquals(location.longitude, mapboxMap.cameraPosition.target.longitude, 0.1)
-
- location.bearing = 92f
- location.latitude = 30.0
- location.longitude = 35.0
- plugin.forceLocationUpdate(location)
- uiController.loopMainThreadForAtLeast(MAX_ANIMATION_DURATION_MS + MAP_RENDER_DELAY) // Waiting for the animation to finish
- assertEquals(92.0, mapboxMap.cameraPosition.bearing, 0.1)
- assertEquals(location.latitude, mapboxMap.cameraPosition.target.latitude, 0.1)
- assertEquals(location.longitude, mapboxMap.cameraPosition.target.longitude, 0.1)
- }
- }
-
- executePluginTest(pluginAction)
- }
-
- @Test
- fun animators_cameraBearingCorrect() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.cameraMode = CameraMode.NONE_GPS
- val latitude = mapboxMap.cameraPosition.target.latitude
- val longitude = mapboxMap.cameraPosition.target.longitude
-
- location.bearing = 77f
- plugin.forceLocationUpdate(location)
- uiController.loopMainThreadForAtLeast(MAX_ANIMATION_DURATION_MS + MAP_RENDER_DELAY)
- assertEquals(77.0, mapboxMap.cameraPosition.bearing, 0.1)
- assertEquals(latitude, mapboxMap.cameraPosition.target.latitude, 0.1)
- assertEquals(longitude, mapboxMap.cameraPosition.target.longitude, 0.1)
-
- location.bearing = 92f
- location.latitude = 30.0
- location.longitude = 35.0
- plugin.forceLocationUpdate(location)
- uiController.loopMainThreadForAtLeast(MAX_ANIMATION_DURATION_MS + MAP_RENDER_DELAY)
- assertEquals(92.0, mapboxMap.cameraPosition.bearing, 0.1)
- assertEquals(latitude, mapboxMap.cameraPosition.target.latitude, 0.1)
- assertEquals(longitude, mapboxMap.cameraPosition.target.longitude, 0.1)
- }
- }
-
- executePluginTest(pluginAction)
- }
-
- @Test
- fun animators_cameraNoneCorrect() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.cameraMode = CameraMode.NONE
- val latitude = mapboxMap.cameraPosition.target.latitude
- val longitude = mapboxMap.cameraPosition.target.longitude
- val bearing = mapboxMap.cameraPosition.bearing
-
- location.bearing = 77f
- plugin.forceLocationUpdate(location)
- uiController.loopMainThreadForAtLeast(MAX_ANIMATION_DURATION_MS + MAP_RENDER_DELAY)
- assertEquals(bearing, mapboxMap.cameraPosition.bearing, 0.1)
- assertEquals(latitude, mapboxMap.cameraPosition.target.latitude, 0.1)
- assertEquals(longitude, mapboxMap.cameraPosition.target.longitude, 0.1)
-
- location.bearing = 92f
- location.latitude = 30.0
- location.longitude = 35.0
- plugin.forceLocationUpdate(location)
- uiController.loopMainThreadForAtLeast(MAX_ANIMATION_DURATION_MS + MAP_RENDER_DELAY) // Waiting for the animation to finish
- assertEquals(bearing, mapboxMap.cameraPosition.bearing, 0.1)
- assertEquals(latitude, mapboxMap.cameraPosition.target.latitude, 0.1)
- assertEquals(longitude, mapboxMap.cameraPosition.target.longitude, 0.1)
- }
- }
-
- executePluginTest(pluginAction)
- }
-
- @Test
- fun animators_focalPointAdjustment() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.cameraMode = CameraMode.TRACKING
- plugin.cameraMode = CameraMode.NONE
- plugin.forceLocationUpdate(location)
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
-
- assertThat(mapboxMap.uiSettings.focalPoint, nullValue())
- }
- }
-
- executePluginTest(pluginAction)
- }
-
- @Test
- fun animators_dontZoomWhileNotTracking() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.cameraMode = CameraMode.NONE
- val zoom = mapboxMap.cameraPosition.zoom
- plugin.zoomWhileTracking(10.0)
- uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_ZOOM_ANIM_DURATION)
-
- assertEquals(zoom, mapboxMap.cameraPosition.zoom, 0.1)
- }
- }
-
- executePluginTest(pluginAction)
- }
-
- @Test
- fun animators_zoomWhileTracking() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.cameraMode = CameraMode.TRACKING
- plugin.zoomWhileTracking(10.0)
- uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_ZOOM_ANIM_DURATION)
-
- assertEquals(10.0, mapboxMap.cameraPosition.zoom, 0.1)
- }
- }
-
- executePluginTest(pluginAction)
- }
-
- @Test
- @Ignore
- fun animators_zoomWhileTrackingCanceledOnModeChange() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.cameraMode = CameraMode.TRACKING
- plugin.zoomWhileTracking(15.0)
- uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_ZOOM_ANIM_DURATION / 2)
- plugin.cameraMode = CameraMode.NONE
- uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_ZOOM_ANIM_DURATION / 2)
-
- assertEquals(15.0 / 2.0, mapboxMap.cameraPosition.zoom, 3.0)
- }
- }
-
- executePluginTest(pluginAction)
- }
-
- @Test
- fun animators_dontZoomWhileStopped() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
-
- plugin.cameraMode = CameraMode.TRACKING
- uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY)
- val zoom = mapboxMap.cameraPosition.zoom
-
- plugin.onStop()
- plugin.zoomWhileTracking(10.0)
- uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_ZOOM_ANIM_DURATION)
-
- assertEquals(zoom, mapboxMap.cameraPosition.zoom, 0.1)
- }
- }
-
- executePluginTest(pluginAction)
- }
-
- @Test
- @Ignore
- fun animators_cancelZoomWhileTracking() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.cameraMode = CameraMode.TRACKING
- plugin.zoomWhileTracking(15.0)
- uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_ZOOM_ANIM_DURATION / 2)
- plugin.cancelZoomWhileTrackingAnimation()
- uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_ZOOM_ANIM_DURATION / 2)
-
- assertEquals(15.0 / 2.0, mapboxMap.cameraPosition.zoom, 3.0)
- }
- }
-
- executePluginTest(pluginAction)
- }
-
- @Test
- fun animators_dontTiltWhileNotTracking() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.cameraMode = CameraMode.NONE
- val tilt = mapboxMap.cameraPosition.tilt
- plugin.tiltWhileTracking(30.0)
- uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_TILT_ANIM_DURATION)
-
- assertEquals(tilt, mapboxMap.cameraPosition.tilt, 0.1)
- }
- }
-
- executePluginTest(pluginAction)
- }
-
- @Test
- fun animators_tiltWhileTracking() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.cameraMode = CameraMode.TRACKING
- plugin.tiltWhileTracking(30.0)
- uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_TILT_ANIM_DURATION)
-
- assertEquals(30.0, mapboxMap.cameraPosition.tilt, 0.1)
- }
- }
-
- executePluginTest(pluginAction)
- }
-
- @Test
- @Ignore
- fun animators_tiltWhileTrackingCanceledOnModeChange() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.cameraMode = CameraMode.TRACKING
- plugin.tiltWhileTracking(30.0)
- uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_TILT_ANIM_DURATION / 2)
- plugin.cameraMode = CameraMode.NONE
- uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_TILT_ANIM_DURATION / 2)
-
- assertEquals(30.0 / 2.0, mapboxMap.cameraPosition.tilt, 3.0)
- }
- }
-
- executePluginTest(pluginAction)
- }
-
- @Test
- fun animators_dontTiltWhileStopped() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.cameraMode = CameraMode.TRACKING
- val tilt = mapboxMap.cameraPosition.tilt
-
- plugin.onStop()
- plugin.tiltWhileTracking(30.0)
- uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_TILT_ANIM_DURATION)
-
- assertEquals(tilt, mapboxMap.cameraPosition.tilt, 0.1)
- }
- }
-
- executePluginTest(pluginAction)
- }
-
- @Test
- @Ignore
- fun animators_cancelTiltWhileTracking() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.cameraMode = CameraMode.TRACKING
- plugin.tiltWhileTracking(30.0)
- uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_TILT_ANIM_DURATION / 2)
- plugin.cancelTiltWhileTrackingAnimation()
- uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_TILT_ANIM_DURATION / 2)
-
- assertEquals(30.0 / 2.0, mapboxMap.cameraPosition.tilt, 3.0)
- }
- }
-
- executePluginTest(pluginAction)
- }
-
- @Test
- fun cameraPositionAdjustedToTrackingModeWhenPluginEnabled() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.cameraMode = CameraMode.TRACKING_GPS
- plugin.forceLocationUpdate(location)
- plugin.deactivateLocationLayerPlugin()
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLng(LatLng(51.0, 17.0)))
- mapboxMap.moveCamera(CameraUpdateFactory.bearingTo(90.0))
- plugin.activateLocationLayerPlugin(context, false)
- uiController.loopMainThreadForAtLeast(MAX_ANIMATION_DURATION_MS + MAP_RENDER_DELAY)
-
- assertEquals(location.bearing.toDouble(), mapboxMap.cameraPosition.bearing, 0.1)
- assertEquals(location.latitude, mapboxMap.cameraPosition.target.latitude, 0.1)
- assertEquals(location.longitude, mapboxMap.cameraPosition.target.longitude, 0.1)
- }
- }
-
- executePluginTest(pluginAction)
- }
-
- @Test
- fun compassEngine_onPluginInitializedDefaultIsProvided() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- assertTrue(plugin.compassEngine is LocationLayerCompassEngine)
- }
- }
-
- executePluginTest(pluginAction)
- }
-
- @Test
- fun compassEngine_changesWhenNewProvided() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- val engine: CompassEngine = object : CompassEngine {
- override fun addCompassListener(compassListener: CompassListener) {
- }
-
- override fun removeCompassListener(compassListener: CompassListener) {
- }
-
- override fun getLastHeading(): Float {
- return 0f
- }
-
- override fun getLastAccuracySensorStatus(): Int {
- return 0
- }
-
- override fun onStart() {
- }
-
- override fun onStop() {
- }
- }
-
- plugin.compassEngine = engine
- assertThat(plugin.compassEngine, notNullValue())
- assertThat(plugin.compassEngine, `is`(equalTo(engine)))
- }
- }
-
- executePluginTest(pluginAction)
- }
-
- @After
- override fun afterTest() {
- super.afterTest()
- IdlingRegistry.getInstance().unregister(styleChangeIdlingResource)
- }
-
- private fun executePluginTest(listener: LocationLayerPluginAction.OnPerformLocationLayerPluginAction) {
- onView(withId(R.id.content)).perform(LocationLayerPluginAction(mapboxMap, listener))
- }
-}
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerTest.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerTest.kt
deleted file mode 100644
index a0f23ce485..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerTest.kt
+++ /dev/null
@@ -1,360 +0,0 @@
-package com.mapbox.mapboxsdk.plugins.locationlayer
-
-import android.Manifest
-import android.R
-import android.content.Context
-import android.location.Location
-import android.support.test.espresso.Espresso.onView
-import android.support.test.espresso.IdlingRegistry
-import android.support.test.espresso.UiController
-import android.support.test.espresso.assertion.ViewAssertions.matches
-import android.support.test.espresso.matcher.ViewMatchers.isDisplayed
-import android.support.test.espresso.matcher.ViewMatchers.withId
-import android.support.test.rule.GrantPermissionRule
-import android.support.test.rule.GrantPermissionRule.grant
-import android.support.test.runner.AndroidJUnit4
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory
-import com.mapbox.mapboxsdk.constants.Style
-import com.mapbox.mapboxsdk.geometry.LatLng
-import com.mapbox.mapboxsdk.maps.MapboxMap
-import com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.*
-import com.mapbox.mapboxsdk.plugins.locationlayer.modes.RenderMode
-import com.mapbox.mapboxsdk.plugins.utils.*
-import com.mapbox.mapboxsdk.plugins.utils.MapboxTestingUtils.Companion.MAPBOX_HEAVY_STYLE
-import com.mapbox.mapboxsdk.plugins.utils.MapboxTestingUtils.Companion.MAP_CONNECTION_DELAY
-import com.mapbox.mapboxsdk.plugins.utils.MapboxTestingUtils.Companion.MAP_RENDER_DELAY
-import com.mapbox.mapboxsdk.plugins.utils.MapboxTestingUtils.Companion.pushSourceUpdates
-import com.mapbox.mapboxsdk.style.sources.GeoJsonSource
-import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest
-import com.mapbox.mapboxsdk.testapp.activity.SingleActivity
-import org.hamcrest.CoreMatchers.`is`
-import org.hamcrest.CoreMatchers.notNullValue
-import org.hamcrest.Matchers.equalTo
-import org.junit.After
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertThat
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-class LocationLayerTest : BaseActivityTest() {
-
- @Rule
- @JvmField
- val permissionRule: GrantPermissionRule = grant(Manifest.permission.ACCESS_FINE_LOCATION)
-
- override fun getActivityClass(): Class<*> {
- return SingleActivity::class.java
- }
-
- private lateinit var styleChangeIdlingResource: StyleChangeIdlingResource
- private val location: Location by lazy {
- val initLocation = Location("test")
- initLocation.latitude = 15.0
- initLocation.longitude = 17.0
- initLocation.bearing = 10f
- initLocation.accuracy = 150f
- initLocation
- }
-
- @Before
- override fun beforeTest() {
- super.beforeTest()
- styleChangeIdlingResource = StyleChangeIdlingResource()
- IdlingRegistry.getInstance().register(styleChangeIdlingResource)
- }
-
- //
- // Location Source
- //
-
- @Test
- fun renderModeNormal_sourceDoesGetAdded() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.renderMode = RenderMode.NORMAL
- uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY)
- assertThat(mapboxMap.getSource(LOCATION_SOURCE), notNullValue())
- }
- }
- executePluginTest(pluginAction)
- }
-
- //
- // Location Layers
- //
-
- @Test
- fun renderModeNormal_trackingNormalLayersDoGetAdded() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.renderMode = RenderMode.NORMAL
- plugin.forceLocationUpdate(location)
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
- assertThat(mapboxMap.isLayerVisible(FOREGROUND_LAYER), `is`(true))
- assertThat(mapboxMap.isLayerVisible(BACKGROUND_LAYER), `is`(true))
- assertThat(mapboxMap.isLayerVisible(SHADOW_LAYER), `is`(true))
- assertThat(mapboxMap.isLayerVisible(ACCURACY_LAYER), `is`(true))
- assertThat(mapboxMap.isLayerVisible(BEARING_LAYER), `is`(false))
- }
- }
- executePluginTest(pluginAction)
- }
-
- @Test
- fun renderModeCompass_bearingLayersDoGetAdded() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.renderMode = RenderMode.COMPASS
- plugin.forceLocationUpdate(location)
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
- assertThat(mapboxMap.isLayerVisible(FOREGROUND_LAYER), `is`(true))
- assertThat(mapboxMap.isLayerVisible(BACKGROUND_LAYER), `is`(true))
- assertThat(mapboxMap.isLayerVisible(SHADOW_LAYER), `is`(true))
- assertThat(mapboxMap.isLayerVisible(ACCURACY_LAYER), `is`(true))
- assertThat(mapboxMap.isLayerVisible(BEARING_LAYER), `is`(true))
- }
- }
- executePluginTest(pluginAction)
- }
-
- @Test
- fun renderModeGps_navigationLayersDoGetAdded() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.renderMode = RenderMode.GPS
- plugin.forceLocationUpdate(location)
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
- assertThat(mapboxMap.isLayerVisible(FOREGROUND_LAYER), `is`(true))
- assertThat(mapboxMap.isLayerVisible(BACKGROUND_LAYER), `is`(true))
- assertThat(mapboxMap.isLayerVisible(SHADOW_LAYER), `is`(false))
- assertThat(mapboxMap.isLayerVisible(ACCURACY_LAYER), `is`(false))
- assertThat(mapboxMap.isLayerVisible(BEARING_LAYER), `is`(false))
- }
- }
- executePluginTest(pluginAction)
- }
-
- @Test
- fun dontShowPuckWhenRenderModeSetAndPluginDisabled() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.forceLocationUpdate(location)
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
- plugin.deactivateLocationLayerPlugin()
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER, shouldDisappear = true)
- plugin.renderMode = RenderMode.GPS
- uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY)
- assertThat(mapboxMap.isLayerVisible(FOREGROUND_LAYER), `is`(false))
- assertThat(mapboxMap.isLayerVisible(BACKGROUND_LAYER), `is`(false))
- assertThat(mapboxMap.isLayerVisible(SHADOW_LAYER), `is`(false))
- assertThat(mapboxMap.isLayerVisible(ACCURACY_LAYER), `is`(false))
- assertThat(mapboxMap.isLayerVisible(BEARING_LAYER), `is`(false))
- }
- }
- executePluginTest(pluginAction)
- }
-
- @Test
- fun whenLocationLayerPluginDisabled_doesSetAllLayersToVisibilityNone() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.renderMode = RenderMode.NORMAL
- plugin.forceLocationUpdate(location)
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
- plugin.deactivateLocationLayerPlugin()
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER, shouldDisappear = true)
- uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY)
-
- // Check that all layers visibilities are set to none
- assertThat(mapboxMap.isLayerVisible(FOREGROUND_LAYER), `is`(false))
- assertThat(mapboxMap.isLayerVisible(BACKGROUND_LAYER), `is`(false))
- assertThat(mapboxMap.isLayerVisible(SHADOW_LAYER), `is`(false))
- assertThat(mapboxMap.isLayerVisible(ACCURACY_LAYER), `is`(false))
- assertThat(mapboxMap.isLayerVisible(BEARING_LAYER), `is`(false))
- }
- }
- executePluginTest(pluginAction)
- }
-
- @Test
- fun onMapChange_locationLayerLayersDoGetRedrawn() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.renderMode = RenderMode.NORMAL
- plugin.forceLocationUpdate(location)
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
- mapboxMap.setStyleUrl(Style.LIGHT)
- uiController.loopMainThreadForAtLeast(MAP_CONNECTION_DELAY)
- plugin.forceLocationUpdate(location)
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
-
- assertThat(plugin.renderMode, `is`(equalTo(RenderMode.NORMAL)))
-
- // Check that the Source has been re-added to the new map style
- val source: GeoJsonSource? = mapboxMap.getSourceAs(LOCATION_SOURCE)
- assertThat(source, notNullValue())
-
- // Check that all layers visibilities are set to visible
- assertThat(mapboxMap.isLayerVisible(FOREGROUND_LAYER), `is`(true))
- assertThat(mapboxMap.isLayerVisible(BACKGROUND_LAYER), `is`(true))
- assertThat(mapboxMap.isLayerVisible(SHADOW_LAYER), `is`(true))
- assertThat(mapboxMap.isLayerVisible(ACCURACY_LAYER), `is`(true))
- assertThat(mapboxMap.isLayerVisible(BEARING_LAYER), `is`(false))
- }
- }
- executePluginTest(pluginAction)
- }
-
- @Test
- fun whenStyleChanged_continuesUsingStaleIcons() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.applyStyle(LocationLayerOptions.builder(context).staleStateTimeout(100).build())
- plugin.forceLocationUpdate(location)
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
- uiController.loopMainThreadForAtLeast(150)
- uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY)
-
- assertThat(mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].getBooleanProperty(PROPERTY_LOCATION_STALE), `is`(true))
-
- mapboxMap.setStyleUrl(Style.LIGHT)
- uiController.loopMainThreadForAtLeast(MAP_CONNECTION_DELAY)
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
-
- assertThat(mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].getBooleanProperty(PROPERTY_LOCATION_STALE), `is`(true))
- }
- }
- executePluginTest(pluginAction)
- }
-
- @Test
- fun whenStyleChanged_staleStateChanges() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.applyStyle(LocationLayerOptions.builder(context).staleStateTimeout(1).build())
- styleChangeIdlingResource.waitForStyle((rule.activity as SingleActivity).mapView, mapboxMap, MAPBOX_HEAVY_STYLE)
- pushSourceUpdates(styleChangeIdlingResource) {
- plugin.forceLocationUpdate(location)
- }
- }
- }
- executePluginTest(pluginAction)
-
- // Waiting for style to finish loading while pushing updates
- onView(withId(R.id.content)).check(matches(isDisplayed()))
- }
-
- @Test
- fun whenStyleChanged_layerVisibilityUpdates() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- styleChangeIdlingResource.waitForStyle((rule.activity as SingleActivity).mapView, mapboxMap, MAPBOX_HEAVY_STYLE)
- var show = true
- pushSourceUpdates(styleChangeIdlingResource) {
- if (show) {
- plugin.activateLocationLayerPlugin(context, false)
- } else {
- plugin.deactivateLocationLayerPlugin()
- }
- show = !show
- }
-
- uiController.loopMainThreadForAtLeast(MAP_CONNECTION_DELAY)
- }
- }
- executePluginTest(pluginAction)
-
- // Waiting for style to finish loading while pushing updates
- onView(withId(R.id.content)).check(matches(isDisplayed()))
- }
-
- @Test
- fun accuracy_visibleWithNewLocation() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(location), 16.0))
- plugin.forceLocationUpdate(location)
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
- uiController.loopMainThreadForAtLeast(ACCURACY_RADIUS_ANIMATION_DURATION)
-
- assertEquals(Utils.calculateZoomLevelRadius(mapboxMap, location) /*meters projected to radius on zoom 16*/,
- mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0]
- .getNumberProperty(PROPERTY_ACCURACY_RADIUS).toFloat(), 0.1f)
- }
- }
- executePluginTest(pluginAction)
- }
-
- @Test
- fun accuracy_visibleWhenCameraEased() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- plugin.forceLocationUpdate(location)
- mapboxMap.waitForLayer(uiController, location, FOREGROUND_LAYER)
- mapboxMap.easeCamera(CameraUpdateFactory.newLatLngZoom(LatLng(location), 16.0), 300)
- uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY + 300)
-
- assertEquals(Utils.calculateZoomLevelRadius(mapboxMap, location) /*meters projected to radius on zoom 16*/,
- mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0]
- .getNumberProperty(PROPERTY_ACCURACY_RADIUS).toFloat(), 0.1f)
- }
- }
- executePluginTest(pluginAction)
- }
-
- @Test
- fun accuracy_visibleWhenCameraMoved() {
- val pluginAction = object : LocationLayerPluginAction.OnPerformLocationLayerPluginAction {
- override fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap,
- uiController: UiController, context: Context) {
- plugin.activateLocationLayerPlugin(context, false)
- uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY)
- plugin.forceLocationUpdate(location)
- uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY)
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(location), 16.0))
- uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY + 300)
-
- assertEquals(Utils.calculateZoomLevelRadius(mapboxMap, location) /*meters projected to radius on zoom 16*/,
- mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0]
- .getNumberProperty(PROPERTY_ACCURACY_RADIUS).toFloat(), 0.1f)
- }
- }
- executePluginTest(pluginAction)
- }
-
- @After
- override fun afterTest() {
- super.afterTest()
- IdlingRegistry.getInstance().unregister(styleChangeIdlingResource)
- }
-
- private fun executePluginTest(listener: LocationLayerPluginAction.OnPerformLocationLayerPluginAction) {
- onView(withId(R.id.content)).perform(LocationLayerPluginAction(mapboxMap, listener))
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/plugins/utils/LocationLayerPluginAction.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/plugins/utils/LocationLayerPluginAction.kt
deleted file mode 100644
index af2c9adf35..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/plugins/utils/LocationLayerPluginAction.kt
+++ /dev/null
@@ -1,40 +0,0 @@
-package com.mapbox.mapboxsdk.plugins.utils
-
-import android.content.Context
-import android.support.test.espresso.UiController
-import android.support.test.espresso.ViewAction
-import android.support.test.espresso.matcher.ViewMatchers.isDisplayed
-import android.view.View
-import com.mapbox.mapboxsdk.maps.MapboxMap
-import com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerPlugin
-import org.hamcrest.Matcher
-
-class LocationLayerPluginAction(private val mapboxMap: MapboxMap,
- private val onPerformLocationLayerPluginAction: OnPerformLocationLayerPluginAction) : ViewAction {
-
- override fun getConstraints(): Matcher {
- return isDisplayed()
- }
-
- override fun getDescription(): String {
- return javaClass.simpleName
- }
-
- override fun perform(uiController: UiController, view: View) {
- val plugin = mapboxMap.locationLayerPlugin
-
- while (mapboxMap.getSource("mapbox-location-source") == null) {
- uiController.loopMainThreadForAtLeast(MapboxTestingUtils.MAP_RENDER_DELAY)
- }
-
- onPerformLocationLayerPluginAction.onLocationLayerPluginAction(
- plugin,
- mapboxMap,
- uiController,
- view.context)
- }
-
- interface OnPerformLocationLayerPluginAction {
- fun onLocationLayerPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap, uiController: UiController, context: Context)
- }
-}
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/plugins/utils/MapboxTestingUtils.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/plugins/utils/MapboxTestingUtils.kt
deleted file mode 100644
index b77ab127d9..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/plugins/utils/MapboxTestingUtils.kt
+++ /dev/null
@@ -1,105 +0,0 @@
-package com.mapbox.mapboxsdk.plugins.utils
-
-import android.graphics.Bitmap
-import android.graphics.Canvas
-import android.graphics.drawable.BitmapDrawable
-import android.graphics.drawable.Drawable
-import android.location.Location
-import android.os.Handler
-import android.os.Looper
-import android.support.test.espresso.UiController
-import com.mapbox.geojson.Feature
-import com.mapbox.mapboxsdk.geometry.LatLng
-import com.mapbox.mapboxsdk.maps.MapboxMap
-import com.mapbox.mapboxsdk.style.layers.Property
-import com.mapbox.mapboxsdk.style.sources.GeoJsonSource
-
-fun MapboxMap.querySourceFeatures(sourceId: String): List {
- return this.getSourceAs(sourceId)?.querySourceFeatures(null) ?: emptyList()
-}
-
-fun MapboxMap.queryRenderedFeatures(location: Location, layerId: String): List {
- val latLng = LatLng(location.latitude, location.longitude)
- val point = this.projection.toScreenLocation(latLng)
- return this.queryRenderedFeatures(point, layerId)
-}
-
-fun MapboxMap.isLayerVisible(layerId: String): Boolean {
- return this.getLayer(layerId)?.visibility?.value?.equals(Property.VISIBLE)!!
-}
-
-fun MapboxMap.waitForSource(uiController: UiController, sourceId: String) {
- var counter = 0
- val delay = MapboxTestingUtils.MAP_RENDER_DELAY
- while (this.querySourceFeatures(sourceId).isEmpty() && delay * counter < MapboxTestingUtils.RENDER_TIMEOUT) {
- uiController.loopMainThreadForAtLeast(delay)
- counter++
- }
-}
-
-fun MapboxMap.waitForLayer(uiController: UiController, location: Location, layerId: String, shouldDisappear: Boolean = false) {
- var counter = 0
- val delay = MapboxTestingUtils.MAP_RENDER_DELAY
- while (
- if (shouldDisappear) this.queryRenderedFeatures(location, layerId).isNotEmpty() else this.queryRenderedFeatures(location, layerId).isEmpty()
- && delay * counter < MapboxTestingUtils.RENDER_TIMEOUT) {
- uiController.loopMainThreadForAtLeast(delay)
- counter++
- }
-}
-
-
-class MapboxTestingUtils {
- companion object {
-
- const val MAP_RENDER_DELAY = 250L
- const val MAP_CONNECTION_DELAY = 1000L
- const val RENDER_TIMEOUT = 3_000L
-
- /**
- * Used to increase style load time for stress testing.
- */
- const val MAPBOX_HEAVY_STYLE = "asset://heavy_style.json"
-
- private const val DATA_PUSH_INTERVAL = 1L
-
- /**
- * Pushes data updates every [DATA_PUSH_INTERVAL] milliseconds until the style has been loaded,
- * checked with [StyleChangeIdlingResource].
- */
- fun pushSourceUpdates(styleChangeIdlingResource: StyleChangeIdlingResource, update: () -> Unit) {
- val mainHandler = Handler(Looper.getMainLooper())
- val runnable = object : Runnable {
- override fun run() {
- update.invoke()
- if (!styleChangeIdlingResource.isIdleNow) {
- mainHandler.postDelayed(this, DATA_PUSH_INTERVAL)
- }
- }
- }
-
- if (!styleChangeIdlingResource.isIdleNow) {
- if (Looper.myLooper() == Looper.getMainLooper()) {
- runnable.run()
- } else {
- mainHandler.post(runnable)
- }
- }
- }
- }
-}
-
-fun MapboxMap.addImageFromDrawable(string: String, drawable: Drawable) {
- val bitmapFromDrawable = getBitmapFromDrawable(drawable)
- this.addImage(string, bitmapFromDrawable)
-}
-
-private fun getBitmapFromDrawable(drawable: Drawable): Bitmap {
- if (drawable is BitmapDrawable) return drawable.bitmap
- val bitmap = Bitmap.createBitmap(drawable.intrinsicWidth,
- drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
- val canvas = Canvas(bitmap)
- drawable.setBounds(0, 0, canvas.width, canvas.height)
- drawable.draw(canvas)
- return bitmap
-}
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/plugins/utils/OnMapFragmentReadyIdlingResource.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/plugins/utils/OnMapFragmentReadyIdlingResource.kt
deleted file mode 100644
index fa6b732770..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/plugins/utils/OnMapFragmentReadyIdlingResource.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.mapbox.mapboxsdk.plugins.utils
-
-import android.os.Handler
-import android.os.Looper
-import android.support.test.espresso.IdlingResource
-
-import com.mapbox.mapboxsdk.maps.MapboxMap
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback
-import com.mapbox.mapboxsdk.maps.SupportMapFragment
-
-class OnMapFragmentReadyIdlingResource(fragment: SupportMapFragment?) : IdlingResource, OnMapReadyCallback {
-
- lateinit var mapboxMap: MapboxMap
-
- private var resourceCallback: IdlingResource.ResourceCallback? = null
-
- init {
- Handler(Looper.getMainLooper()).post {
- fragment?.getMapAsync(this)
- }
- }
-
- override fun getName(): String {
- return javaClass.simpleName
- }
-
- override fun isIdleNow(): Boolean {
- return this::mapboxMap.isInitialized
- }
-
- override fun registerIdleTransitionCallback(resourceCallback: IdlingResource.ResourceCallback) {
- this.resourceCallback = resourceCallback
- }
-
- override fun onMapReady(mapboxMap: MapboxMap) {
- this.mapboxMap = mapboxMap
- resourceCallback?.onTransitionToIdle()
- }
-}
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/plugins/utils/OnMapReadyIdlingResource.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/plugins/utils/OnMapReadyIdlingResource.java
deleted file mode 100644
index f084343594..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/plugins/utils/OnMapReadyIdlingResource.java
+++ /dev/null
@@ -1,63 +0,0 @@
-package com.mapbox.mapboxsdk.plugins.utils;
-
-import android.app.Activity;
-import android.os.Handler;
-import android.os.Looper;
-import android.support.test.espresso.IdlingResource;
-
-import com.mapbox.mapboxsdk.maps.MapView;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-
-import java.lang.reflect.Field;
-
-public class OnMapReadyIdlingResource implements IdlingResource, OnMapReadyCallback {
-
- private MapboxMap mapboxMap;
- private MapView mapView;
- private IdlingResource.ResourceCallback resourceCallback;
-
- public OnMapReadyIdlingResource(Activity activity) {
- new Handler(Looper.getMainLooper()).post(() -> {
- try {
- Field field = activity.getClass().getDeclaredField("mapView");
- field.setAccessible(true);
- mapView = ((MapView) field.get(activity));
- mapView.getMapAsync(this);
- } catch (Exception err) {
- throw new RuntimeException(err);
- }
- });
- }
-
- @Override
- public String getName() {
- return getClass().getSimpleName();
- }
-
- @Override
- public boolean isIdleNow() {
- return mapboxMap != null;
- }
-
- @Override
- public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
- this.resourceCallback = resourceCallback;
- }
-
- public MapView getMapView() {
- return mapView;
- }
-
- public MapboxMap getMapboxMap() {
- return mapboxMap;
- }
-
- @Override
- public void onMapReady(MapboxMap mapboxMap) {
- this.mapboxMap = mapboxMap;
- if (resourceCallback != null) {
- resourceCallback.onTransitionToIdle();
- }
- }
-}
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/plugins/utils/StyleChangeIdlingResource.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/plugins/utils/StyleChangeIdlingResource.kt
deleted file mode 100644
index ab2c855c65..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/plugins/utils/StyleChangeIdlingResource.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-package com.mapbox.mapboxsdk.plugins.utils
-
-import android.support.test.espresso.IdlingResource
-import com.mapbox.mapboxsdk.maps.MapView
-import com.mapbox.mapboxsdk.maps.MapboxMap
-
-/**
- * Resource, that's idling until the provided style is loaded.
- * Remember to add any espresso action (like view assertion) after the [waitForStyle] call
- * for the test to keep running.
- */
-class StyleChangeIdlingResource : IdlingResource {
-
- private var callback: IdlingResource.ResourceCallback? = null
- private var isIdle = true
-
- override fun getName(): String {
- return javaClass.simpleName
- }
-
- override fun isIdleNow(): Boolean {
- return isIdle
- }
-
- override fun registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback?) {
- this.callback = callback
- }
-
- private fun setIdle() {
- isIdle = true
- callback?.onTransitionToIdle()
- }
-
- fun waitForStyle(mapView: MapView, mapboxMap: MapboxMap, styleUrl: String) {
- isIdle = false
- mapView.addOnMapChangedListener(object : MapView.OnMapChangedListener {
- override fun onMapChanged(change: Int) {
- if (change == MapView.DID_FINISH_LOADING_STYLE) {
- mapView.removeOnMapChangedListener(this)
- setIdle()
- }
- }
- })
- mapboxMap.setStyleUrl(styleUrl)
- }
-}
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
index 8597a9ac14..a0594d8b83 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
@@ -816,7 +816,7 @@
android:value="com.mapbox.mapboxsdk.testapp.activity.FeatureOverviewActivity" />
?) {
+ Toast.makeText(this@LocationFragmentActivity, "You need to accept location permissions.",
+ Toast.LENGTH_SHORT).show()
+ }
+
+ override fun onPermissionResult(granted: Boolean) {
+ if (granted) {
+ if (savedInstanceState == null) {
+ fragmentManager
+ .beginTransaction()
+ .replace(R.id.container, LocationFragment.newInstance(), LocationFragment.TAG)
+ .commit()
+ }
+ } else {
+ finish()
+ }
+ }
+ })
+ permissionsManager.requestLocationPermissions(this)
+ }
+ }
+
+ override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults)
+ permissionsManager.onRequestPermissionsResult(requestCode, permissions, grantResults)
+ }
+
+ class LocationFragment : Fragment(), LocationEngineListener {
+ companion object {
+ const val TAG = "LFragment"
+ fun newInstance(): LocationFragment {
+ return LocationFragment()
+ }
+ }
+
+ private lateinit var mapView: MapView
+ private lateinit var mapboxMap: MapboxMap
+ private var component: LocationComponent? = null
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ mapView = MapView(inflater.context)
+ return mapView
+ }
+
+ @SuppressLint("MissingPermission")
+ override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
+ mapView.onCreate(savedInstanceState)
+ mapView.getMapAsync {
+ mapboxMap = it
+ component = mapboxMap.locationComponent
+ component?.activateLocationComponent(activity)
+ component?.locationEngine?.addLocationEngineListener(this)
+ }
+ }
+
+ override fun onLocationChanged(location: Location?) {
+ if (location != null) {
+ mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(LatLng(location), 12.0))
+ component?.locationEngine?.removeLocationEngineListener(this)
+ }
+ }
+
+ override fun onConnected() {
+ // no impl
+ }
+
+ override fun onStart() {
+ super.onStart()
+ mapView.onStart()
+ }
+
+ override fun onResume() {
+ super.onResume()
+ mapView.onResume()
+ }
+
+ override fun onPause() {
+ super.onPause()
+ mapView.onPause()
+ }
+
+ override fun onSaveInstanceState(outState: Bundle) {
+ super.onSaveInstanceState(outState)
+ mapView.onSaveInstanceState(outState)
+ }
+
+ override fun onStop() {
+ super.onStop()
+ mapView.onStop()
+ }
+
+ override fun onLowMemory() {
+ super.onLowMemory()
+ mapView.onLowMemory()
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ mapView.onDestroy()
+ component?.locationEngine?.removeLocationEngineListener(this)
+ }
+ }
+
+ class EmptyFragment : Fragment() {
+ companion object {
+ const val TAG = "EmptyFragment"
+ fun newInstance(): EmptyFragment {
+ return EmptyFragment()
+ }
+ }
+
+ override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View {
+ val textView = TextView(inflater?.context)
+ textView.text = "This is an empty Fragment"
+ return textView
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationLayerFragmentActivity.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationLayerFragmentActivity.kt
deleted file mode 100644
index fa2c84ab92..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationLayerFragmentActivity.kt
+++ /dev/null
@@ -1,171 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.activity.location
-
-import android.annotation.SuppressLint
-import android.app.Fragment
-import android.location.Location
-import android.os.Bundle
-import android.support.v7.app.AppCompatActivity
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.TextView
-import android.widget.Toast
-import com.mapbox.android.core.location.LocationEngineListener
-import com.mapbox.android.core.permissions.PermissionsListener
-import com.mapbox.android.core.permissions.PermissionsManager
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory
-import com.mapbox.mapboxsdk.geometry.LatLng
-import com.mapbox.mapboxsdk.maps.MapView
-import com.mapbox.mapboxsdk.maps.MapboxMap
-import com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerPlugin
-import com.mapbox.mapboxsdk.testapp.R
-import kotlinx.android.synthetic.main.activity_location_layer_fragment.*
-
-class LocationLayerFragmentActivity : AppCompatActivity() {
- private lateinit var permissionsManager: PermissionsManager
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_location_layer_fragment)
-
- fab.setOnClickListener {
- val fragment = fragmentManager.findFragmentByTag(EmptyFragment.TAG)
- if (fragment == null) {
- fragmentManager
- .beginTransaction()
- .replace(R.id.container, EmptyFragment.newInstance(), EmptyFragment.TAG)
- .addToBackStack("transaction2")
- .commit()
- } else {
- this.onBackPressed()
- }
- }
- supportActionBar?.setDisplayHomeAsUpEnabled(true)
-
- if (PermissionsManager.areLocationPermissionsGranted(this)) {
- if (savedInstanceState == null) {
- fragmentManager
- .beginTransaction()
- .replace(R.id.container, LocationLayerFragment.newInstance(), LocationLayerFragment.TAG)
- .commit()
- }
- } else {
- permissionsManager = PermissionsManager(object : PermissionsListener {
- override fun onExplanationNeeded(permissionsToExplain: MutableList?) {
- Toast.makeText(this@LocationLayerFragmentActivity, "You need to accept location permissions.",
- Toast.LENGTH_SHORT).show()
- }
-
- override fun onPermissionResult(granted: Boolean) {
- if (granted) {
- if (savedInstanceState == null) {
- fragmentManager
- .beginTransaction()
- .replace(R.id.container, LocationLayerFragment.newInstance(), LocationLayerFragment.TAG)
- .commit()
- }
- } else {
- finish()
- }
- }
- })
- permissionsManager.requestLocationPermissions(this)
- }
- }
-
- override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults)
- permissionsManager.onRequestPermissionsResult(requestCode, permissions, grantResults)
- }
-
- class LocationLayerFragment : Fragment(), LocationEngineListener {
- companion object {
- const val TAG = "LLFragment"
- fun newInstance(): LocationLayerFragment {
- return LocationLayerFragment()
- }
- }
-
- private lateinit var mapView: MapView
- private lateinit var mapboxMap: MapboxMap
- private var plugin: LocationLayerPlugin? = null
-
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- mapView = MapView(inflater.context)
- return mapView
- }
-
- @SuppressLint("MissingPermission")
- override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
- mapView.onCreate(savedInstanceState)
- mapView.getMapAsync {
- mapboxMap = it
- plugin = mapboxMap.locationLayerPlugin
- plugin?.activateLocationLayerPlugin(activity)
- plugin?.locationEngine?.addLocationEngineListener(this)
- }
- }
-
- override fun onLocationChanged(location: Location?) {
- if (location != null) {
- mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(LatLng(location), 12.0))
- plugin?.locationEngine?.removeLocationEngineListener(this)
- }
- }
-
- override fun onConnected() {
- // no impl
- }
-
- override fun onStart() {
- super.onStart()
- mapView.onStart()
- }
-
- override fun onResume() {
- super.onResume()
- mapView.onResume()
- }
-
- override fun onPause() {
- super.onPause()
- mapView.onPause()
- }
-
- override fun onSaveInstanceState(outState: Bundle) {
- super.onSaveInstanceState(outState)
- mapView.onSaveInstanceState(outState)
- }
-
- override fun onStop() {
- super.onStop()
- mapView.onStop()
- }
-
- override fun onLowMemory() {
- super.onLowMemory()
- mapView.onLowMemory()
- }
-
- override fun onDestroyView() {
- super.onDestroyView()
- mapView.onDestroy()
- plugin?.locationEngine?.removeLocationEngineListener(this)
- }
- }
-
- class EmptyFragment : Fragment() {
- companion object {
- const val TAG = "EmptyFragment"
- fun newInstance(): EmptyFragment {
- return EmptyFragment()
- }
- }
-
- override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View {
- val textView = TextView(inflater?.context)
- textView.text = "This is an empty Fragment"
- return textView
- }
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationLayerMapChangeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationLayerMapChangeActivity.java
deleted file mode 100644
index e59627d35e..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationLayerMapChangeActivity.java
+++ /dev/null
@@ -1,131 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.activity.location;
-
-import android.annotation.SuppressLint;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.design.widget.FloatingActionButton;
-import android.support.v7.app.AppCompatActivity;
-import android.view.View;
-import android.widget.Toast;
-
-import com.mapbox.android.core.permissions.PermissionsListener;
-import com.mapbox.android.core.permissions.PermissionsManager;
-import com.mapbox.mapboxsdk.maps.MapView;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerPlugin;
-import com.mapbox.mapboxsdk.plugins.locationlayer.modes.RenderMode;
-import com.mapbox.mapboxsdk.testapp.R;
-
-import java.util.List;
-
-public class LocationLayerMapChangeActivity extends AppCompatActivity implements OnMapReadyCallback {
-
- private MapView mapView;
- private MapboxMap mapboxMap;
- private PermissionsManager permissionsManager;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_location_layer_map_change);
-
- mapView = findViewById(R.id.mapView);
- FloatingActionButton stylesFab = findViewById(R.id.fabStyles);
-
- stylesFab.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mapboxMap != null) {
- mapboxMap.setStyleUrl(Utils.getNextStyle());
- }
- }
- });
-
- mapView.setStyleUrl(Utils.getNextStyle());
- mapView.onCreate(savedInstanceState);
-
- if (PermissionsManager.areLocationPermissionsGranted(this)) {
- mapView.getMapAsync(this);
- } else {
- permissionsManager = new PermissionsManager(new PermissionsListener() {
- @Override
- public void onExplanationNeeded(List permissionsToExplain) {
- Toast.makeText(LocationLayerMapChangeActivity.this, "You need to accept location permissions.",
- Toast.LENGTH_SHORT).show();
- }
-
- @Override
- public void onPermissionResult(boolean granted) {
- if (granted) {
- mapView.getMapAsync(LocationLayerMapChangeActivity.this);
- } else {
- finish();
- }
- }
- });
- permissionsManager.requestLocationPermissions(this);
- }
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults);
- permissionsManager.onRequestPermissionsResult(requestCode, permissions, grantResults);
- }
-
- @Override
- public void onMapReady(MapboxMap mapboxMap) {
- this.mapboxMap = mapboxMap;
- activateLocationLayer();
- }
-
- @SuppressLint("MissingPermission")
- private void activateLocationLayer() {
- LocationLayerPlugin locationPlugin = mapboxMap.getLocationLayerPlugin();
- locationPlugin.activateLocationLayerPlugin(this);
- locationPlugin.setRenderMode(RenderMode.COMPASS);
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- mapView.onStart();
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- mapView.onResume();
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- mapView.onPause();
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- mapView.onStop();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mapView.onSaveInstanceState(outState);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mapView.onDestroy();
- }
-
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- mapView.onLowMemory();
- }
-}
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationLayerModesActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationLayerModesActivity.java
deleted file mode 100644
index 4c8c8eda4b..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationLayerModesActivity.java
+++ /dev/null
@@ -1,383 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.activity.location;
-
-import android.annotation.SuppressLint;
-import android.content.res.Configuration;
-import android.location.Location;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.ListPopupWindow;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.Toast;
-
-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.android.core.permissions.PermissionsListener;
-import com.mapbox.android.core.permissions.PermissionsManager;
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.constants.Style;
-import com.mapbox.mapboxsdk.maps.MapView;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerOptions;
-import com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerPlugin;
-import com.mapbox.mapboxsdk.plugins.locationlayer.OnCameraTrackingChangedListener;
-import com.mapbox.mapboxsdk.plugins.locationlayer.OnLocationLayerClickListener;
-import com.mapbox.mapboxsdk.plugins.locationlayer.modes.CameraMode;
-import com.mapbox.mapboxsdk.plugins.locationlayer.modes.RenderMode;
-import com.mapbox.mapboxsdk.testapp.R;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class LocationLayerModesActivity extends AppCompatActivity implements OnMapReadyCallback,
- LocationEngineListener, OnLocationLayerClickListener, OnCameraTrackingChangedListener {
-
- private MapView mapView;
- private Button locationModeBtn;
- private Button locationTrackingBtn;
-
- private PermissionsManager permissionsManager;
-
- private LocationLayerPlugin locationLayerPlugin;
- private LocationEngine locationEngine;
- private MapboxMap mapboxMap;
- private boolean customStyle;
-
- private Bundle savedInstanceState;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_location_layer_mode);
-
- mapView = findViewById(R.id.mapView);
-
- locationModeBtn = findViewById(R.id.button_location_mode);
- locationModeBtn.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (locationLayerPlugin == null) {
- return;
- }
- showModeListDialog();
- }
- });
-
- locationTrackingBtn = findViewById(R.id.button_location_tracking);
- locationTrackingBtn.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (locationLayerPlugin == null) {
- return;
- }
- showTrackingListDialog();
- }
- });
-
- mapView.onCreate(savedInstanceState);
-
- if (PermissionsManager.areLocationPermissionsGranted(this)) {
- mapView.getMapAsync(this);
- } else {
- permissionsManager = new PermissionsManager(new PermissionsListener() {
- @Override
- public void onExplanationNeeded(List permissionsToExplain) {
- Toast.makeText(LocationLayerModesActivity.this, "You need to accept location permissions.",
- Toast.LENGTH_SHORT).show();
- }
-
- @Override
- public void onPermissionResult(boolean granted) {
- if (granted) {
- mapView.getMapAsync(LocationLayerModesActivity.this);
- } else {
- finish();
- }
- }
- });
- permissionsManager.requestLocationPermissions(this);
- }
-
- this.savedInstanceState = savedInstanceState;
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults);
- permissionsManager.onRequestPermissionsResult(requestCode, permissions, grantResults);
- }
-
- @SuppressLint("MissingPermission")
- @Override
- public void onMapReady(MapboxMap mapboxMap) {
- this.mapboxMap = mapboxMap;
-
- locationEngine = new LocationEngineProvider(this).obtainBestLocationEngineAvailable();
- locationEngine.setPriority(LocationEnginePriority.HIGH_ACCURACY);
- locationEngine.setFastestInterval(1000);
- locationEngine.addLocationEngineListener(this);
- locationEngine.activate();
-
- locationLayerPlugin = mapboxMap.getLocationLayerPlugin();
- locationLayerPlugin.addOnLocationClickListener(this);
- locationLayerPlugin.addOnCameraTrackingChangedListener(this);
-
- activateLocationLayer();
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.menu_location_mode, menu);
- return true;
- }
-
- @SuppressLint("MissingPermission")
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (locationLayerPlugin == null) {
- return super.onOptionsItemSelected(item);
- }
-
- int id = item.getItemId();
- if (id == R.id.action_style_change) {
- toggleStyle();
- return true;
- } else if (id == R.id.action_map_style_change) {
- toggleMapStyle();
- return true;
- } else if (id == R.id.action_plugin_disable) {
- locationLayerPlugin.deactivateLocationLayerPlugin();
- return true;
- } else if (id == R.id.action_plugin_enabled) {
- activateLocationLayer();
- return true;
- }
-
- return super.onOptionsItemSelected(item);
- }
-
- private void activateLocationLayer() {
- if (locationLayerPlugin != null) {
- int[] padding;
- if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
- padding = new int[] {0, 750, 0, 0};
- } else {
- padding = new int[] {0, 250, 0, 0};
- }
-
- if (savedInstanceState == null) {
- LocationLayerOptions options = LocationLayerOptions.builder(this)
- .padding(padding)
- .layerBelow("waterway-label")
- .build();
-
- locationLayerPlugin.activateLocationLayerPlugin(locationEngine, options);
- } else {
- LocationLayerOptions options = locationLayerPlugin
- .getLocationLayerOptions()
- .toBuilder()
- .padding(padding)
- .build();
-
- locationLayerPlugin.setLocationEngine(locationEngine);
- locationLayerPlugin.applyStyle(options);
- }
- }
- }
-
- public void toggleStyle() {
- customStyle = !customStyle;
- locationLayerPlugin.applyStyle(
- this,
- customStyle ? R.style.CustomLocationLayer : R.style.mapbox_LocationLayer);
- }
-
- public void toggleMapStyle() {
- String styleUrl = mapboxMap.getStyleUrl().contentEquals(Style.DARK) ? Style.LIGHT : Style.DARK;
- mapboxMap.setStyle(styleUrl);
- }
-
- @Override
- @SuppressWarnings( {"MissingPermission"})
- protected void onStart() {
- super.onStart();
- mapView.onStart();
- if (locationEngine != null) {
- locationEngine.addLocationEngineListener(this);
- if (locationEngine.isConnected()) {
- locationEngine.requestLocationUpdates();
- } else {
- locationEngine.activate();
- }
- }
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- mapView.onResume();
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- mapView.onPause();
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- mapView.onStop();
- if (locationEngine != null) {
- locationEngine.removeLocationEngineListener(this);
- locationEngine.removeLocationUpdates();
- }
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mapView.onSaveInstanceState(outState);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mapView.onDestroy();
- if (locationEngine != null) {
- locationEngine.deactivate();
- }
- }
-
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- mapView.onLowMemory();
- }
-
- @Override
- @SuppressWarnings( {"MissingPermission"})
- public void onConnected() {
- locationEngine.requestLocationUpdates();
- }
-
- @Override
- public void onLocationChanged(Location location) {
- // no impl
- }
-
- @Override
- public void onLocationLayerClick() {
- Toast.makeText(this, "OnLocationLayerClick", Toast.LENGTH_LONG).show();
- }
-
- private void showModeListDialog() {
- List modes = new ArrayList<>();
- modes.add("Normal");
- modes.add("Compass");
- modes.add("GPS");
- ArrayAdapter profileAdapter = new ArrayAdapter<>(this,
- android.R.layout.simple_list_item_1, modes);
- ListPopupWindow listPopup = new ListPopupWindow(this);
- listPopup.setAdapter(profileAdapter);
- listPopup.setAnchorView(locationModeBtn);
- listPopup.setOnItemClickListener((parent, itemView, position, id) -> {
- String selectedMode = modes.get(position);
- locationModeBtn.setText(selectedMode);
- if (selectedMode.contentEquals("Normal")) {
- setRendererMode(RenderMode.NORMAL);
- } else if (selectedMode.contentEquals("Compass")) {
- setRendererMode(RenderMode.COMPASS);
- } else if (selectedMode.contentEquals("GPS")) {
- setRendererMode(RenderMode.GPS);
- }
- listPopup.dismiss();
- });
- listPopup.show();
- }
-
- private void setRendererMode(@RenderMode.Mode int mode) {
- locationLayerPlugin.setRenderMode(mode);
- if (mode == RenderMode.NORMAL) {
- locationModeBtn.setText("Normal");
- } else if (mode == RenderMode.COMPASS) {
- locationModeBtn.setText("Compass");
- } else if (mode == RenderMode.GPS) {
- locationModeBtn.setText("Gps");
- }
- }
-
- private void showTrackingListDialog() {
- List trackingTypes = new ArrayList<>();
- trackingTypes.add("None");
- trackingTypes.add("Tracking");
- trackingTypes.add("Tracking Compass");
- trackingTypes.add("Tracking GPS");
- trackingTypes.add("Tracking GPS North");
- ArrayAdapter profileAdapter = new ArrayAdapter<>(this,
- android.R.layout.simple_list_item_1, trackingTypes);
- ListPopupWindow listPopup = new ListPopupWindow(this);
- listPopup.setAdapter(profileAdapter);
- listPopup.setAnchorView(locationTrackingBtn);
- listPopup.setOnItemClickListener((parent, itemView, position, id) -> {
- String selectedTrackingType = trackingTypes.get(position);
- locationTrackingBtn.setText(selectedTrackingType);
- if (selectedTrackingType.contentEquals("None")) {
- locationLayerPlugin.setCameraMode(CameraMode.NONE);
- } else if (selectedTrackingType.contentEquals("Tracking")) {
- locationLayerPlugin.setCameraMode(CameraMode.TRACKING);
- } else if (selectedTrackingType.contentEquals("Tracking Compass")) {
- locationLayerPlugin.setCameraMode(CameraMode.TRACKING_COMPASS);
- } else if (selectedTrackingType.contentEquals("Tracking GPS")) {
- locationLayerPlugin.setCameraMode(CameraMode.TRACKING_GPS);
- } else if (selectedTrackingType.contentEquals("Tracking GPS North")) {
- locationLayerPlugin.setCameraMode(CameraMode.TRACKING_GPS_NORTH);
- }
- listPopup.dismiss();
-
- if (locationLayerPlugin.getCameraMode() != CameraMode.NONE) {
- locationLayerPlugin.zoomWhileTracking(15, 750, new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- // No impl
- }
-
- @Override
- public void onFinish() {
- locationLayerPlugin.tiltWhileTracking(45);
- }
- });
- } else {
- mapboxMap.easeCamera(CameraUpdateFactory.tiltTo(0));
- }
- });
- listPopup.show();
- }
-
- @Override
- public void onCameraTrackingDismissed() {
- locationTrackingBtn.setText("None");
- }
-
- @Override
- public void onCameraTrackingChanged(int currentMode) {
- if (currentMode == CameraMode.NONE) {
- locationTrackingBtn.setText("None");
- } else if (currentMode == CameraMode.TRACKING) {
- locationTrackingBtn.setText("Tracking");
- } else if (currentMode == CameraMode.TRACKING_COMPASS) {
- locationTrackingBtn.setText("Tracking Compass");
- } else if (currentMode == CameraMode.TRACKING_GPS) {
- locationTrackingBtn.setText("Tracking GPS");
- } else if (currentMode == CameraMode.TRACKING_GPS_NORTH) {
- locationTrackingBtn.setText("Tracking GPS North");
- }
- }
-}
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationMapChangeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationMapChangeActivity.java
new file mode 100644
index 0000000000..6fa514e28b
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationMapChangeActivity.java
@@ -0,0 +1,131 @@
+package com.mapbox.mapboxsdk.testapp.activity.location;
+
+import android.annotation.SuppressLint;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.design.widget.FloatingActionButton;
+import android.support.v7.app.AppCompatActivity;
+import android.view.View;
+import android.widget.Toast;
+
+import com.mapbox.android.core.permissions.PermissionsListener;
+import com.mapbox.android.core.permissions.PermissionsManager;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.location.LocationComponent;
+import com.mapbox.mapboxsdk.location.modes.RenderMode;
+import com.mapbox.mapboxsdk.testapp.R;
+
+import java.util.List;
+
+public class LocationMapChangeActivity extends AppCompatActivity implements OnMapReadyCallback {
+
+ private MapView mapView;
+ private MapboxMap mapboxMap;
+ private PermissionsManager permissionsManager;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_location_layer_map_change);
+
+ mapView = findViewById(R.id.mapView);
+ FloatingActionButton stylesFab = findViewById(R.id.fabStyles);
+
+ stylesFab.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mapboxMap != null) {
+ mapboxMap.setStyleUrl(Utils.getNextStyle());
+ }
+ }
+ });
+
+ mapView.setStyleUrl(Utils.getNextStyle());
+ mapView.onCreate(savedInstanceState);
+
+ if (PermissionsManager.areLocationPermissionsGranted(this)) {
+ mapView.getMapAsync(this);
+ } else {
+ permissionsManager = new PermissionsManager(new PermissionsListener() {
+ @Override
+ public void onExplanationNeeded(List permissionsToExplain) {
+ Toast.makeText(LocationMapChangeActivity.this, "You need to accept location permissions.",
+ Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onPermissionResult(boolean granted) {
+ if (granted) {
+ mapView.getMapAsync(LocationMapChangeActivity.this);
+ } else {
+ finish();
+ }
+ }
+ });
+ permissionsManager.requestLocationPermissions(this);
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ permissionsManager.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ }
+
+ @Override
+ public void onMapReady(MapboxMap mapboxMap) {
+ this.mapboxMap = mapboxMap;
+ activateLocationComponent();
+ }
+
+ @SuppressLint("MissingPermission")
+ private void activateLocationComponent() {
+ LocationComponent locationComponent = mapboxMap.getLocationComponent();
+ locationComponent.activateLocationComponent(this);
+ locationComponent.setRenderMode(RenderMode.COMPASS);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ mapView.onStart();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ mapView.onStop();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mapView.onLowMemory();
+ }
+}
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationModesActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationModesActivity.java
new file mode 100644
index 0000000000..f2f57c854d
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationModesActivity.java
@@ -0,0 +1,383 @@
+package com.mapbox.mapboxsdk.testapp.activity.location;
+
+import android.annotation.SuppressLint;
+import android.content.res.Configuration;
+import android.location.Location;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.ListPopupWindow;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.Toast;
+
+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.android.core.permissions.PermissionsListener;
+import com.mapbox.android.core.permissions.PermissionsManager;
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
+import com.mapbox.mapboxsdk.constants.Style;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.location.LocationComponentOptions;
+import com.mapbox.mapboxsdk.location.LocationComponent;
+import com.mapbox.mapboxsdk.location.OnCameraTrackingChangedListener;
+import com.mapbox.mapboxsdk.location.OnLocationComponentClickListener;
+import com.mapbox.mapboxsdk.location.modes.CameraMode;
+import com.mapbox.mapboxsdk.location.modes.RenderMode;
+import com.mapbox.mapboxsdk.testapp.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class LocationModesActivity extends AppCompatActivity implements OnMapReadyCallback,
+ LocationEngineListener, OnLocationComponentClickListener, OnCameraTrackingChangedListener {
+
+ private MapView mapView;
+ private Button locationModeBtn;
+ private Button locationTrackingBtn;
+
+ private PermissionsManager permissionsManager;
+
+ private LocationComponent locationComponent;
+ private LocationEngine locationEngine;
+ private MapboxMap mapboxMap;
+ private boolean customStyle;
+
+ private Bundle savedInstanceState;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_location_layer_mode);
+
+ mapView = findViewById(R.id.mapView);
+
+ locationModeBtn = findViewById(R.id.button_location_mode);
+ locationModeBtn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (locationComponent == null) {
+ return;
+ }
+ showModeListDialog();
+ }
+ });
+
+ locationTrackingBtn = findViewById(R.id.button_location_tracking);
+ locationTrackingBtn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (locationComponent == null) {
+ return;
+ }
+ showTrackingListDialog();
+ }
+ });
+
+ mapView.onCreate(savedInstanceState);
+
+ if (PermissionsManager.areLocationPermissionsGranted(this)) {
+ mapView.getMapAsync(this);
+ } else {
+ permissionsManager = new PermissionsManager(new PermissionsListener() {
+ @Override
+ public void onExplanationNeeded(List permissionsToExplain) {
+ Toast.makeText(LocationModesActivity.this, "You need to accept location permissions.",
+ Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onPermissionResult(boolean granted) {
+ if (granted) {
+ mapView.getMapAsync(LocationModesActivity.this);
+ } else {
+ finish();
+ }
+ }
+ });
+ permissionsManager.requestLocationPermissions(this);
+ }
+
+ this.savedInstanceState = savedInstanceState;
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ permissionsManager.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ }
+
+ @SuppressLint("MissingPermission")
+ @Override
+ public void onMapReady(MapboxMap mapboxMap) {
+ this.mapboxMap = mapboxMap;
+
+ locationEngine = new LocationEngineProvider(this).obtainBestLocationEngineAvailable();
+ locationEngine.setPriority(LocationEnginePriority.HIGH_ACCURACY);
+ locationEngine.setFastestInterval(1000);
+ locationEngine.addLocationEngineListener(this);
+ locationEngine.activate();
+
+ locationComponent = mapboxMap.getLocationComponent();
+ locationComponent.addOnLocationClickListener(this);
+ locationComponent.addOnCameraTrackingChangedListener(this);
+
+ activateLocationComponent();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_location_mode, menu);
+ return true;
+ }
+
+ @SuppressLint("MissingPermission")
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (locationComponent == null) {
+ return super.onOptionsItemSelected(item);
+ }
+
+ int id = item.getItemId();
+ if (id == R.id.action_style_change) {
+ toggleStyle();
+ return true;
+ } else if (id == R.id.action_map_style_change) {
+ toggleMapStyle();
+ return true;
+ } else if (id == R.id.action_component_disable) {
+ locationComponent.deactivateLocationComponent();
+ return true;
+ } else if (id == R.id.action_component_enabled) {
+ activateLocationComponent();
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void activateLocationComponent() {
+ if (locationComponent != null) {
+ int[] padding;
+ if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
+ padding = new int[] {0, 750, 0, 0};
+ } else {
+ padding = new int[] {0, 250, 0, 0};
+ }
+
+ if (savedInstanceState == null) {
+ LocationComponentOptions options = LocationComponentOptions.builder(this)
+ .padding(padding)
+ .layerBelow("waterway-label")
+ .build();
+
+ locationComponent.activateLocationComponent(locationEngine, options);
+ } else {
+ LocationComponentOptions options = locationComponent
+ .getLocationComponentOptions()
+ .toBuilder()
+ .padding(padding)
+ .build();
+
+ locationComponent.setLocationEngine(locationEngine);
+ locationComponent.applyStyle(options);
+ }
+ }
+ }
+
+ public void toggleStyle() {
+ customStyle = !customStyle;
+ locationComponent.applyStyle(
+ this,
+ customStyle ? R.style.CustomLocationComponent : R.style.mapbox_LocationComponent);
+ }
+
+ public void toggleMapStyle() {
+ String styleUrl = mapboxMap.getStyleUrl().contentEquals(Style.DARK) ? Style.LIGHT : Style.DARK;
+ mapboxMap.setStyle(styleUrl);
+ }
+
+ @Override
+ @SuppressWarnings( {"MissingPermission"})
+ protected void onStart() {
+ super.onStart();
+ mapView.onStart();
+ if (locationEngine != null) {
+ locationEngine.addLocationEngineListener(this);
+ if (locationEngine.isConnected()) {
+ locationEngine.requestLocationUpdates();
+ } else {
+ locationEngine.activate();
+ }
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ mapView.onStop();
+ if (locationEngine != null) {
+ locationEngine.removeLocationEngineListener(this);
+ locationEngine.removeLocationUpdates();
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mapView.onDestroy();
+ if (locationEngine != null) {
+ locationEngine.deactivate();
+ }
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mapView.onLowMemory();
+ }
+
+ @Override
+ @SuppressWarnings( {"MissingPermission"})
+ public void onConnected() {
+ locationEngine.requestLocationUpdates();
+ }
+
+ @Override
+ public void onLocationChanged(Location location) {
+ // no impl
+ }
+
+ @Override
+ public void onLocationComponentClick() {
+ Toast.makeText(this, "OnlocationComponentClick", Toast.LENGTH_LONG).show();
+ }
+
+ private void showModeListDialog() {
+ List modes = new ArrayList<>();
+ modes.add("Normal");
+ modes.add("Compass");
+ modes.add("GPS");
+ ArrayAdapter profileAdapter = new ArrayAdapter<>(this,
+ android.R.layout.simple_list_item_1, modes);
+ ListPopupWindow listPopup = new ListPopupWindow(this);
+ listPopup.setAdapter(profileAdapter);
+ listPopup.setAnchorView(locationModeBtn);
+ listPopup.setOnItemClickListener((parent, itemView, position, id) -> {
+ String selectedMode = modes.get(position);
+ locationModeBtn.setText(selectedMode);
+ if (selectedMode.contentEquals("Normal")) {
+ setRendererMode(RenderMode.NORMAL);
+ } else if (selectedMode.contentEquals("Compass")) {
+ setRendererMode(RenderMode.COMPASS);
+ } else if (selectedMode.contentEquals("GPS")) {
+ setRendererMode(RenderMode.GPS);
+ }
+ listPopup.dismiss();
+ });
+ listPopup.show();
+ }
+
+ private void setRendererMode(@RenderMode.Mode int mode) {
+ locationComponent.setRenderMode(mode);
+ if (mode == RenderMode.NORMAL) {
+ locationModeBtn.setText("Normal");
+ } else if (mode == RenderMode.COMPASS) {
+ locationModeBtn.setText("Compass");
+ } else if (mode == RenderMode.GPS) {
+ locationModeBtn.setText("Gps");
+ }
+ }
+
+ private void showTrackingListDialog() {
+ List trackingTypes = new ArrayList<>();
+ trackingTypes.add("None");
+ trackingTypes.add("Tracking");
+ trackingTypes.add("Tracking Compass");
+ trackingTypes.add("Tracking GPS");
+ trackingTypes.add("Tracking GPS North");
+ ArrayAdapter profileAdapter = new ArrayAdapter<>(this,
+ android.R.layout.simple_list_item_1, trackingTypes);
+ ListPopupWindow listPopup = new ListPopupWindow(this);
+ listPopup.setAdapter(profileAdapter);
+ listPopup.setAnchorView(locationTrackingBtn);
+ listPopup.setOnItemClickListener((parent, itemView, position, id) -> {
+ String selectedTrackingType = trackingTypes.get(position);
+ locationTrackingBtn.setText(selectedTrackingType);
+ if (selectedTrackingType.contentEquals("None")) {
+ locationComponent.setCameraMode(CameraMode.NONE);
+ } else if (selectedTrackingType.contentEquals("Tracking")) {
+ locationComponent.setCameraMode(CameraMode.TRACKING);
+ } else if (selectedTrackingType.contentEquals("Tracking Compass")) {
+ locationComponent.setCameraMode(CameraMode.TRACKING_COMPASS);
+ } else if (selectedTrackingType.contentEquals("Tracking GPS")) {
+ locationComponent.setCameraMode(CameraMode.TRACKING_GPS);
+ } else if (selectedTrackingType.contentEquals("Tracking GPS North")) {
+ locationComponent.setCameraMode(CameraMode.TRACKING_GPS_NORTH);
+ }
+ listPopup.dismiss();
+
+ if (locationComponent.getCameraMode() != CameraMode.NONE) {
+ locationComponent.zoomWhileTracking(15, 750, new MapboxMap.CancelableCallback() {
+ @Override
+ public void onCancel() {
+ // No impl
+ }
+
+ @Override
+ public void onFinish() {
+ locationComponent.tiltWhileTracking(45);
+ }
+ });
+ } else {
+ mapboxMap.easeCamera(CameraUpdateFactory.tiltTo(0));
+ }
+ });
+ listPopup.show();
+ }
+
+ @Override
+ public void onCameraTrackingDismissed() {
+ locationTrackingBtn.setText("None");
+ }
+
+ @Override
+ public void onCameraTrackingChanged(int currentMode) {
+ if (currentMode == CameraMode.NONE) {
+ locationTrackingBtn.setText("None");
+ } else if (currentMode == CameraMode.TRACKING) {
+ locationTrackingBtn.setText("Tracking");
+ } else if (currentMode == CameraMode.TRACKING_COMPASS) {
+ locationTrackingBtn.setText("Tracking Compass");
+ } else if (currentMode == CameraMode.TRACKING_GPS) {
+ locationTrackingBtn.setText("Tracking GPS");
+ } else if (currentMode == CameraMode.TRACKING_GPS_NORTH) {
+ locationTrackingBtn.setText("Tracking GPS North");
+ }
+ }
+}
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/ManualLocationUpdatesActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/ManualLocationUpdatesActivity.java
index 3e9f0853ea..5750e97d63 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/ManualLocationUpdatesActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/ManualLocationUpdatesActivity.java
@@ -18,8 +18,8 @@ import com.mapbox.mapboxsdk.geometry.LatLngBounds;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerPlugin;
-import com.mapbox.mapboxsdk.plugins.locationlayer.modes.RenderMode;
+import com.mapbox.mapboxsdk.location.LocationComponent;
+import com.mapbox.mapboxsdk.location.modes.RenderMode;
import com.mapbox.mapboxsdk.testapp.R;
import java.util.List;
@@ -30,7 +30,7 @@ public class ManualLocationUpdatesActivity extends AppCompatActivity implements
LocationEngineListener {
private MapView mapView;
- private LocationLayerPlugin locationLayerPlugin;
+ private LocationComponent locationComponent;
private LocationEngine locationEngine;
private PermissionsManager permissionsManager;
@@ -43,8 +43,8 @@ public class ManualLocationUpdatesActivity extends AppCompatActivity implements
fabManualUpdate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- if (locationLayerPlugin != null && locationLayerPlugin.getLocationEngine() == null) {
- locationLayerPlugin.forceLocationUpdate(
+ if (locationComponent != null && locationComponent.getLocationEngine() == null) {
+ locationComponent.forceLocationUpdate(
Utils.getRandomLocation(LatLngBounds.from(60, 25, 40, -5)));
}
}
@@ -55,11 +55,11 @@ public class ManualLocationUpdatesActivity extends AppCompatActivity implements
fabToggle.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- if (locationLayerPlugin != null) {
- locationLayerPlugin.setLocationEngine(locationLayerPlugin.getLocationEngine() == null ? locationEngine :
+ if (locationComponent != null) {
+ locationComponent.setLocationEngine(locationComponent.getLocationEngine() == null ? locationEngine :
null);
- if (locationLayerPlugin.getLocationEngine() == null) {
+ if (locationComponent.getLocationEngine() == null) {
fabToggle.setImageResource(R.drawable.ic_layers_clear);
fabManualUpdate.setEnabled(true);
fabManualUpdate.setAlpha(1f);
@@ -118,9 +118,9 @@ public class ManualLocationUpdatesActivity extends AppCompatActivity implements
locationEngine.addLocationEngineListener(this);
locationEngine.setPriority(LocationEnginePriority.HIGH_ACCURACY);
locationEngine.activate();
- locationLayerPlugin = mapboxMap.getLocationLayerPlugin();
- locationLayerPlugin.activateLocationLayerPlugin(this, locationEngine);
- locationLayerPlugin.setRenderMode(RenderMode.COMPASS);
+ locationComponent = mapboxMap.getLocationComponent();
+ locationComponent.activateLocationComponent(this, locationEngine);
+ locationComponent.setRenderMode(RenderMode.COMPASS);
}
@Override
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_location_mode.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_location_mode.xml
index 7ce217ecd9..7004999f88 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_location_mode.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_location_mode.xml
@@ -9,12 +9,12 @@
android:title="Toggle custom Map style"
app:showAsAction="never"/>
-
-
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml
index 05035760f9..cb9c2043dc 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml
@@ -73,6 +73,6 @@
Click to add a marker, long-click to drag
Change map\'s style while location is displayed
Showcases location render and tracking modes
- Uses LocationLayer in a Fragment
+ Uses LocationComponent in a Fragment
Force location updates and don\'t rely on the engine
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/styles.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/styles.xml
index facc169299..a0525171a5 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/styles.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/styles.xml
@@ -38,7 +38,7 @@
-