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 @@