diff --git a/app/src/androidTest/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerPluginTest.kt b/app/src/androidTest/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerPluginTest.kt index c11012bb4..4b54639c4 100644 --- a/app/src/androidTest/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerPluginTest.kt +++ b/app/src/androidTest/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerPluginTest.kt @@ -18,11 +18,13 @@ 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.maps.MapView import com.mapbox.mapboxsdk.maps.MapboxMap import com.mapbox.mapboxsdk.maps.SupportMapFragment 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.testapp.activity.SingleFragmentActivity import com.mapbox.mapboxsdk.plugins.utils.* @@ -33,12 +35,9 @@ import com.mapbox.mapboxsdk.plugins.utils.PluginGenerationUtil.Companion.MAP_REN import com.mapbox.mapboxsdk.style.layers.PropertyFactory import com.mapbox.mapboxsdk.style.sources.GeoJsonSource import org.hamcrest.CoreMatchers.* -import org.junit.After +import org.junit.* import org.junit.Assert.assertEquals import org.junit.Assert.assertNotEquals -import org.junit.Before -import org.junit.Rule -import org.junit.Test import org.junit.runner.RunWith import timber.log.Timber @@ -638,6 +637,326 @@ class LocationLayerPluginTest { onView(withId(R.id.content)).check(matches(isDisplayed())) } + @Test + fun animators_layerBearingCorrect() { + val pluginAction = object : GenericPluginAction.OnPerformGenericPluginAction { + override fun onGenericPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap, + uiController: UiController, context: Context) { + plugin.renderMode = RenderMode.GPS + location.bearing = 77f + plugin.forceLocationUpdate(location) + uiController.loopMainThreadForAtLeast(1000) + assertEquals(77.0, mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].getNumberProperty(PROPERTY_GPS_BEARING) as Double, 0.1) + + location.bearing = 92f + plugin.forceLocationUpdate(location) + uiController.loopMainThreadForAtLeast(2000) // Waiting for the animation to finish + assertEquals(92.0, mapboxMap.querySourceFeatures(LOCATION_SOURCE)[0].getNumberProperty(PROPERTY_GPS_BEARING) as Double, 0.1) + } + } + + executePluginTest(pluginAction, PluginGenerationUtil.getLocationLayerPluginProvider(rule.activity)) + } + + @Test + fun animators_cameraLatLngBearingCorrect() { + val pluginAction = object : GenericPluginAction.OnPerformGenericPluginAction { + override fun onGenericPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap, + uiController: UiController, context: Context) { + plugin.cameraMode = CameraMode.TRACKING_GPS + location.bearing = 77f + plugin.forceLocationUpdate(location) + uiController.loopMainThreadForAtLeast(1000) + 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(2000) // 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, PluginGenerationUtil.getLocationLayerPluginProvider(rule.activity)) + } + + @Test + fun animators_cameraBearingCorrect() { + val pluginAction = object : GenericPluginAction.OnPerformGenericPluginAction { + override fun onGenericPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap, + uiController: UiController, context: Context) { + 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(1000) + 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(2000) // Waiting for the animation to finish + 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, PluginGenerationUtil.getLocationLayerPluginProvider(rule.activity)) + } + + @Test + fun animators_cameraNoneCorrect() { + val pluginAction = object : GenericPluginAction.OnPerformGenericPluginAction { + override fun onGenericPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap, + uiController: UiController, context: Context) { + 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(1000) + 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(2000) // 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, PluginGenerationUtil.getLocationLayerPluginProvider(rule.activity)) + } + + @Test + fun animators_focalPointAdjustment() { + val pluginAction = object : GenericPluginAction.OnPerformGenericPluginAction { + override fun onGenericPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap, + uiController: UiController, context: Context) { + plugin.cameraMode = CameraMode.TRACKING + plugin.cameraMode = CameraMode.NONE + plugin.forceLocationUpdate(location) + uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY) + + assertThat(mapboxMap.uiSettings.focalPoint, nullValue()) + } + } + + executePluginTest(pluginAction, PluginGenerationUtil.getLocationLayerPluginProvider(rule.activity)) + } + + @Test + fun animators_dontZoomWhileNotTracking() { + val pluginAction = object : GenericPluginAction.OnPerformGenericPluginAction { + override fun onGenericPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap, + uiController: UiController, context: Context) { + plugin.cameraMode = CameraMode.NONE + val zoom = mapboxMap.cameraPosition.zoom + plugin.zoomWhileTracking(10.0) + uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_ZOOM_ANIMATION_DURATION) + + assertEquals(zoom, mapboxMap.cameraPosition.zoom, 0.1) + } + } + + executePluginTest(pluginAction, PluginGenerationUtil.getLocationLayerPluginProvider(rule.activity)) + } + + @Test + fun animators_zoomWhileTracking() { + val pluginAction = object : GenericPluginAction.OnPerformGenericPluginAction { + override fun onGenericPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap, + uiController: UiController, context: Context) { + plugin.cameraMode = CameraMode.TRACKING + plugin.zoomWhileTracking(10.0) + uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_ZOOM_ANIMATION_DURATION) + + assertEquals(10.0, mapboxMap.cameraPosition.zoom, 0.1) + } + } + + executePluginTest(pluginAction, PluginGenerationUtil.getLocationLayerPluginProvider(rule.activity)) + } + + @Test + @Ignore + fun animators_zoomWhileTrackingCanceledOnModeChange() { + val pluginAction = object : GenericPluginAction.OnPerformGenericPluginAction { + override fun onGenericPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap, + uiController: UiController, context: Context) { + plugin.cameraMode = CameraMode.TRACKING + plugin.zoomWhileTracking(15.0) + uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_ZOOM_ANIMATION_DURATION / 2) + plugin.cameraMode = CameraMode.NONE + uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_ZOOM_ANIMATION_DURATION / 2) + + assertEquals(15.0 / 2.0, mapboxMap.cameraPosition.zoom, 3.0) + } + } + + executePluginTest(pluginAction, PluginGenerationUtil.getLocationLayerPluginProvider(rule.activity)) + } + + @Test + fun animators_dontZoomWhileStopped() { + val pluginAction = object : GenericPluginAction.OnPerformGenericPluginAction { + override fun onGenericPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap, + uiController: UiController, context: Context) { + + val testLifecycleOwner = TestLifecycleOwner() + testLifecycleOwner.markState(Lifecycle.State.RESUMED) + testLifecycleOwner.lifecycle.addObserver(plugin) + + plugin.cameraMode = CameraMode.TRACKING + mapboxMap.moveCamera(CameraUpdateFactory.zoomTo(5.0)) + uiController.loopMainThreadForAtLeast(MAP_RENDER_DELAY) + val zoom = mapboxMap.cameraPosition.zoom + + testLifecycleOwner.markState(Lifecycle.State.CREATED) + plugin.zoomWhileTracking(10.0) + uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_ZOOM_ANIMATION_DURATION) + + assertEquals(zoom, mapboxMap.cameraPosition.zoom, 0.1) + } + } + + executePluginTest(pluginAction, + PluginGenerationUtil.getLocationLayerPluginProvider(rule.activity, false, null, null, false)) + } + + @Test + @Ignore + fun animators_cancelZoomWhileTracking() { + val pluginAction = object : GenericPluginAction.OnPerformGenericPluginAction { + override fun onGenericPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap, + uiController: UiController, context: Context) { + plugin.cameraMode = CameraMode.TRACKING + plugin.zoomWhileTracking(15.0) + uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_ZOOM_ANIMATION_DURATION / 2) + plugin.cancelZoomWhileTrackingAnimation() + uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_ZOOM_ANIMATION_DURATION / 2) + + assertEquals(15.0 / 2.0, mapboxMap.cameraPosition.zoom, 3.0) + } + } + + executePluginTest(pluginAction, PluginGenerationUtil.getLocationLayerPluginProvider(rule.activity)) + } + + @Test + fun animators_dontTiltWhileNotTracking() { + val pluginAction = object : GenericPluginAction.OnPerformGenericPluginAction { + override fun onGenericPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap, + uiController: UiController, context: Context) { + plugin.cameraMode = CameraMode.NONE + val tilt = mapboxMap.cameraPosition.tilt + plugin.tiltWhileTracking(30.0) + uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_TILT_ANIMATION_DURATION) + + assertEquals(tilt, mapboxMap.cameraPosition.tilt, 0.1) + } + } + + executePluginTest(pluginAction, PluginGenerationUtil.getLocationLayerPluginProvider(rule.activity)) + } + + @Test + fun animators_tiltWhileTracking() { + val pluginAction = object : GenericPluginAction.OnPerformGenericPluginAction { + override fun onGenericPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap, + uiController: UiController, context: Context) { + plugin.cameraMode = CameraMode.TRACKING + plugin.tiltWhileTracking(30.0) + uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_TILT_ANIMATION_DURATION) + + assertEquals(30.0, mapboxMap.cameraPosition.tilt, 0.1) + } + } + + executePluginTest(pluginAction, PluginGenerationUtil.getLocationLayerPluginProvider(rule.activity)) + } + + @Test + @Ignore + fun animators_tiltWhileTrackingCanceledOnModeChange() { + val pluginAction = object : GenericPluginAction.OnPerformGenericPluginAction { + override fun onGenericPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap, + uiController: UiController, context: Context) { + plugin.cameraMode = CameraMode.TRACKING + plugin.tiltWhileTracking(30.0) + uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_TILT_ANIMATION_DURATION / 2) + plugin.cameraMode = CameraMode.NONE + uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_TILT_ANIMATION_DURATION / 2) + + assertEquals(30.0 / 2.0, mapboxMap.cameraPosition.tilt, 3.0) + } + } + + executePluginTest(pluginAction, PluginGenerationUtil.getLocationLayerPluginProvider(rule.activity)) + } + + @Test + fun animators_dontTiltWhileStopped() { + val pluginAction = object : GenericPluginAction.OnPerformGenericPluginAction { + override fun onGenericPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap, + uiController: UiController, context: Context) { + + val testLifecycleOwner = TestLifecycleOwner() + testLifecycleOwner.markState(Lifecycle.State.RESUMED) + testLifecycleOwner.lifecycle.addObserver(plugin) + + plugin.cameraMode = CameraMode.TRACKING + val tilt = mapboxMap.cameraPosition.tilt + + testLifecycleOwner.markState(Lifecycle.State.CREATED) + plugin.tiltWhileTracking(30.0) + uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_TILT_ANIMATION_DURATION) + + assertEquals(tilt, mapboxMap.cameraPosition.tilt, 0.1) + } + } + + executePluginTest(pluginAction, + PluginGenerationUtil.getLocationLayerPluginProvider(rule.activity, false, null, null, false)) + } + + @Test + @Ignore + fun animators_cancelTiltWhileTracking() { + val pluginAction = object : GenericPluginAction.OnPerformGenericPluginAction { + override fun onGenericPluginAction(plugin: LocationLayerPlugin, mapboxMap: MapboxMap, + uiController: UiController, context: Context) { + plugin.cameraMode = CameraMode.TRACKING + plugin.tiltWhileTracking(30.0) + uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_TILT_ANIMATION_DURATION / 2) + plugin.cancelTiltWhileTrackingAnimation() + uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_TILT_ANIMATION_DURATION / 2) + + assertEquals(30.0 / 2.0, mapboxMap.cameraPosition.tilt, 3.0) + } + } + + executePluginTest(pluginAction, PluginGenerationUtil.getLocationLayerPluginProvider(rule.activity)) + } + @After fun afterTest() { Timber.e("@After: unregister idle resource") diff --git a/app/src/main/java/com/mapbox/mapboxsdk/plugins/testapp/activity/location/LocationLayerModesActivity.java b/app/src/main/java/com/mapbox/mapboxsdk/plugins/testapp/activity/location/LocationLayerModesActivity.java index 361253a8d..5edbcf03e 100644 --- a/app/src/main/java/com/mapbox/mapboxsdk/plugins/testapp/activity/location/LocationLayerModesActivity.java +++ b/app/src/main/java/com/mapbox/mapboxsdk/plugins/testapp/activity/location/LocationLayerModesActivity.java @@ -316,6 +316,22 @@ private void showTrackingListDialog() { 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(); } diff --git a/plugin-locationlayer/CHANGELOG.md b/plugin-locationlayer/CHANGELOG.md index 62f46ef3f..e9bba0801 100644 --- a/plugin-locationlayer/CHANGELOG.md +++ b/plugin-locationlayer/CHANGELOG.md @@ -2,6 +2,9 @@ Mapbox welcomes participation and contributions from everyone. +### master +- Additional methods to tilt and zoom in/out the camera while camera tracking is engaged [#583](https://github.com/mapbox/mapbox-plugins-android/pull/583) + ### 0.6.0 - July 9, 2018 - Animate accuracy ring when new value is provided [#577](https://github.com/mapbox/mapbox-plugins-android/pull/577) - Allow pre-loaded MapboxMap images / maki icons in LocationLayerOptions [#574](https://github.com/mapbox/mapbox-plugins-android/pull/574) diff --git a/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/CameraCompassBearingAnimator.java b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/CameraCompassBearingAnimator.java new file mode 100644 index 000000000..6d5fd9da8 --- /dev/null +++ b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/CameraCompassBearingAnimator.java @@ -0,0 +1,24 @@ +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/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/CameraGpsBearingAnimator.java b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/CameraGpsBearingAnimator.java new file mode 100644 index 000000000..ac88a0258 --- /dev/null +++ b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/CameraGpsBearingAnimator.java @@ -0,0 +1,23 @@ +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/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/CameraLatLngAnimator.java b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/CameraLatLngAnimator.java new file mode 100644 index 000000000..6f383a480 --- /dev/null +++ b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/CameraLatLngAnimator.java @@ -0,0 +1,25 @@ +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/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LatLngEvaluator.java b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LatLngEvaluator.java new file mode 100644 index 000000000..b137ea013 --- /dev/null +++ b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LatLngEvaluator.java @@ -0,0 +1,19 @@ +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/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerAccuracyAnimator.java b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerAccuracyAnimator.java new file mode 100644 index 000000000..1d200f454 --- /dev/null +++ b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerAccuracyAnimator.java @@ -0,0 +1,24 @@ +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/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerCompassBearingAnimator.java b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerCompassBearingAnimator.java new file mode 100644 index 000000000..3b3898d1e --- /dev/null +++ b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerCompassBearingAnimator.java @@ -0,0 +1,24 @@ +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/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerGpsBearingAnimator.java b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerGpsBearingAnimator.java new file mode 100644 index 000000000..c14ffbb93 --- /dev/null +++ b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerGpsBearingAnimator.java @@ -0,0 +1,23 @@ +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/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerLatLngAnimator.java b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerLatLngAnimator.java new file mode 100644 index 000000000..0ff0a46bd --- /dev/null +++ b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LayerLatLngAnimator.java @@ -0,0 +1,25 @@ +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/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayer.java b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayer.java index d24fc9ec7..35c139798 100644 --- a/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayer.java +++ b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayer.java @@ -81,7 +81,7 @@ import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconSize; import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility; -final class LocationLayer implements LocationLayerAnimator.OnLayerAnimationsValuesChangeListener { +final class LocationLayer implements PluginAnimator.OnLayerAnimationsValuesChangeListener { @RenderMode.Mode private int renderMode; diff --git a/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerAnimator.java b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerAnimator.java deleted file mode 100644 index eb345031d..000000000 --- a/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerAnimator.java +++ /dev/null @@ -1,455 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer; - -import android.animation.Animator; -import android.animation.AnimatorSet; -import android.animation.ValueAnimator; -import android.location.Location; -import android.os.SystemClock; -import android.support.annotation.NonNull; -import android.view.animation.LinearInterpolator; - -import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.plugins.locationlayer.camera.AccuracyAnimator; -import com.mapbox.mapboxsdk.plugins.locationlayer.camera.BearingAnimator; -import com.mapbox.mapboxsdk.plugins.locationlayer.camera.LatLngAnimator; - -import java.util.ArrayList; -import java.util.List; - -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; - -final class LocationLayerAnimator { - - private final List layerListeners = new ArrayList<>(); - private final List cameraListeners = new ArrayList<>(); - - private LatLngAnimator layerLatLngAnimator; - private BearingAnimator layerGpsBearingAnimator; - private BearingAnimator layerCompassBearingAnimator; - private AccuracyAnimator accuracyRadiusAnimator; - - private LatLngAnimator cameraLatLngAnimator; - private BearingAnimator cameraGpsBearingAnimator; - private BearingAnimator cameraCompassBearingAnimator; - - private Location previousLocation; - private float previousAccuracyRadius = -1; - private float previousCompassBearing = -1; - private long locationUpdateTimestamp = -1; - - void addLayerListener(OnLayerAnimationsValuesChangeListener listener) { - layerListeners.add(listener); - } - - void removeLayerListener(OnLayerAnimationsValuesChangeListener listener) { - layerListeners.remove(listener); - } - - void addCameraListener(OnCameraAnimationsValuesChangeListener listener) { - cameraListeners.add(listener); - } - - void removeCameraListener(OnCameraAnimationsValuesChangeListener listener) { - cameraListeners.remove(listener); - } - - void feedNewLocation(@NonNull Location newLocation, @NonNull CameraPosition currentCameraPosition, - boolean isGpsNorth) { - if (previousLocation == null) { - previousLocation = newLocation; - locationUpdateTimestamp = SystemClock.elapsedRealtime(); - } - - 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); - - playAllLocationAnimators(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); - accuracyRadiusAnimator.setDuration(noAnimation ? 0 : ACCURACY_RADIUS_ANIMATION_DURATION); - accuracyRadiusAnimator.start(); - - this.previousAccuracyRadius = targetAccuracyRadius; - } - - private final ValueAnimator.AnimatorUpdateListener layerLatLngUpdateListener = - new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator valueAnimator) { - for (OnLayerAnimationsValuesChangeListener listener : layerListeners) { - listener.onNewLatLngValue((LatLng) valueAnimator.getAnimatedValue()); - } - } - }; - - private final ValueAnimator.AnimatorUpdateListener layerCompassBearingUpdateListener = - new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator valueAnimator) { - for (OnLayerAnimationsValuesChangeListener listener : layerListeners) { - listener.onNewCompassBearingValue((Float) valueAnimator.getAnimatedValue()); - } - } - }; - - private final ValueAnimator.AnimatorUpdateListener layerGpsBearingUpdateListener = - new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator valueAnimator) { - for (OnLayerAnimationsValuesChangeListener listener : layerListeners) { - listener.onNewGpsBearingValue((Float) valueAnimator.getAnimatedValue()); - } - } - }; - - private final ValueAnimator.AnimatorUpdateListener accuracyRadiusUpdateListener = - new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator valueAnimator) { - for (OnLayerAnimationsValuesChangeListener listener : layerListeners) { - listener.onNewAccuracyRadiusValue((Float) valueAnimator.getAnimatedValue()); - } - } - }; - - private final ValueAnimator.AnimatorUpdateListener cameraLatLngUpdateListener = - new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator valueAnimator) { - for (OnCameraAnimationsValuesChangeListener listener : cameraListeners) { - listener.onNewLatLngValue((LatLng) valueAnimator.getAnimatedValue()); - } - } - }; - - private final ValueAnimator.AnimatorUpdateListener cameraCompassBearingUpdateListener = - new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator valueAnimator) { - for (OnCameraAnimationsValuesChangeListener listener : cameraListeners) { - listener.onNewCompassBearingValue((Float) valueAnimator.getAnimatedValue()); - } - } - }; - - private final ValueAnimator.AnimatorUpdateListener cameraGpsBearingUpdateListener = - new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator valueAnimator) { - for (OnCameraAnimationsValuesChangeListener listener : cameraListeners) { - listener.onNewGpsBearingValue((Float) valueAnimator.getAnimatedValue()); - } - } - }; - - 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 resetAllCameraAnimations(CameraPosition currentCameraPosition, boolean isGpsNorth) { - resetCameraCompassAnimation(currentCameraPosition); - resetCameraLocationAnimations(currentCameraPosition, isGpsNorth); - playCameraAnimators(TRANSITION_ANIMATION_DURATION_MS); - } - - void cancelAllAnimations() { - cancelLayerLocationAnimations(); - cancelLayerCompassAnimations(); - cancelAccuracyRadiusAnimations(); - cancelCameraLocationAnimations(); - cancelCameraCompassAnimations(); - } - - private LatLng getPreviousLayerLatLng() { - LatLng previousLatLng; - if (layerLatLngAnimator != null) { - previousLatLng = (LatLng) layerLatLngAnimator.getAnimatedValue(); - } else { - previousLatLng = new LatLng(previousLocation); - } - return previousLatLng; - } - - private float getPreviousLayerGpsBearing() { - float previousBearing; - if (layerGpsBearingAnimator != null) { - previousBearing = (float) layerGpsBearingAnimator.getAnimatedValue(); - } else { - previousBearing = previousLocation.getBearing(); - } - return previousBearing; - } - - private float getPreviousAccuracyRadius() { - float previousRadius; - if (accuracyRadiusAnimator != null) { - previousRadius = (float) accuracyRadiusAnimator.getAnimatedValue(); - } else { - previousRadius = previousAccuracyRadius; - } - return previousRadius; - } - - private float getPreviousLayerCompassBearing() { - float previousBearing; - if (layerCompassBearingAnimator != null) { - previousBearing = (float) layerCompassBearingAnimator.getAnimatedValue(); - } else { - previousBearing = previousCompassBearing; - } - return previousBearing; - } - - private void updateLayerAnimators(LatLng previousLatLng, LatLng targetLatLng, - float previousBearing, float targetBearing) { - cancelLayerLocationAnimations(); - layerLatLngAnimator = new LatLngAnimator(previousLatLng, targetLatLng); - float normalizedLayerBearing = Utils.shortestRotation(targetBearing, previousBearing); - layerGpsBearingAnimator = new BearingAnimator(previousBearing, normalizedLayerBearing); - - layerLatLngAnimator.addUpdateListener(layerLatLngUpdateListener); - layerGpsBearingAnimator.addUpdateListener(layerGpsBearingUpdateListener); - } - - private void updateCameraAnimators(LatLng previousCameraLatLng, float previousCameraBearing, - LatLng targetLatLng, float targetBearing) { - cancelCameraLocationAnimations(); - cameraLatLngAnimator = new LatLngAnimator(previousCameraLatLng, targetLatLng); - cameraLatLngAnimator.addUpdateListener(cameraLatLngUpdateListener); - - float normalizedCameraBearing = Utils.shortestRotation(targetBearing, previousCameraBearing); - cameraGpsBearingAnimator = new BearingAnimator(previousCameraBearing, normalizedCameraBearing); - cameraGpsBearingAnimator.addUpdateListener(cameraGpsBearingUpdateListener); - } - - 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 playAllLocationAnimators(long duration) { - List locationAnimators = new ArrayList<>(); - locationAnimators.add(layerLatLngAnimator); - locationAnimators.add(layerGpsBearingAnimator); - locationAnimators.add(cameraLatLngAnimator); - locationAnimators.add(cameraGpsBearingAnimator); - AnimatorSet locationAnimatorSet = new AnimatorSet(); - locationAnimatorSet.playTogether(locationAnimators); - locationAnimatorSet.setInterpolator(new LinearInterpolator()); - locationAnimatorSet.setDuration(duration); - locationAnimatorSet.start(); - } - - private void playCameraAnimators(long duration) { - List locationAnimators = new ArrayList<>(); - locationAnimators.add(cameraLatLngAnimator); - locationAnimators.add(cameraGpsBearingAnimator); - AnimatorSet locationAnimatorSet = new AnimatorSet(); - locationAnimatorSet.playTogether(locationAnimators); - locationAnimatorSet.setInterpolator(new LinearInterpolator()); - locationAnimatorSet.setDuration(duration); - locationAnimatorSet.start(); - } - - private void updateCompassAnimators(float targetCompassBearing, float previousLayerBearing, - float previousCameraBearing) { - cancelLayerCompassAnimations(); - float normalizedLayerBearing = Utils.shortestRotation(targetCompassBearing, previousLayerBearing); - layerCompassBearingAnimator = new BearingAnimator(previousLayerBearing, normalizedLayerBearing); - layerCompassBearingAnimator.addUpdateListener(layerCompassBearingUpdateListener); - - cancelCameraCompassAnimations(); - float normalizedCameraBearing = Utils.shortestRotation(targetCompassBearing, previousCameraBearing); - cameraCompassBearingAnimator = new BearingAnimator(previousCameraBearing, normalizedCameraBearing); - cameraCompassBearingAnimator.addUpdateListener(cameraCompassBearingUpdateListener); - } - - private void playCompassAnimators(long duration) { - List compassAnimators = new ArrayList<>(); - compassAnimators.add(layerCompassBearingAnimator); - compassAnimators.add(cameraCompassBearingAnimator); - AnimatorSet compassAnimatorSet = new AnimatorSet(); - compassAnimatorSet.playTogether(compassAnimators); - compassAnimatorSet.setDuration(duration); - compassAnimatorSet.start(); - } - - private void updateAccuracyAnimators(float targetAccuracyRadius, float previousAccuracyRadius) { - cancelAccuracyRadiusAnimations(); - accuracyRadiusAnimator = new AccuracyAnimator(previousAccuracyRadius, targetAccuracyRadius); - accuracyRadiusAnimator.addUpdateListener(accuracyRadiusUpdateListener); - } - - private void playAccuracyRadiusAnimation(long duration) { - } - - private void cancelLayerLocationAnimations() { - cancelLayerLatLngAnimations(); - cancelLayerGpsBearingAnimations(); - } - - private void cancelLayerLatLngAnimations() { - if (layerLatLngAnimator != null) { - layerLatLngAnimator.cancel(); - layerLatLngAnimator.removeAllUpdateListeners(); - } - } - - private void cancelLayerGpsBearingAnimations() { - if (layerGpsBearingAnimator != null) { - layerGpsBearingAnimator.cancel(); - layerGpsBearingAnimator.removeAllUpdateListeners(); - } - } - - private void cancelAccuracyRadiusAnimations() { - if (accuracyRadiusAnimator != null) { - accuracyRadiusAnimator.cancel(); - accuracyRadiusAnimator.removeAllUpdateListeners(); - } - } - - private void cancelLayerCompassAnimations() { - if (layerCompassBearingAnimator != null) { - layerCompassBearingAnimator.cancel(); - layerCompassBearingAnimator.removeAllUpdateListeners(); - } - } - - private void cancelCameraLocationAnimations() { - cancelCameraLatLngAnimations(); - cancelCameraGpsBearingAnimations(); - } - - private void resetCameraLocationAnimations(CameraPosition currentCameraPosition, boolean isGpsNorth) { - resetCameraLatLngAnimation(currentCameraPosition); - resetCameraGpsBearingAnimation(currentCameraPosition, isGpsNorth); - } - - private void cancelCameraLatLngAnimations() { - if (cameraLatLngAnimator != null) { - cameraLatLngAnimator.cancel(); - cameraLatLngAnimator.removeAllUpdateListeners(); - } - } - - private void resetCameraLatLngAnimation(CameraPosition currentCameraPosition) { - if (cameraLatLngAnimator == null) { - return; - } - cancelCameraLatLngAnimations(); - LatLng currentTarget = cameraLatLngAnimator.getTarget(); - LatLng previousCameraTarget = currentCameraPosition.target; - cameraLatLngAnimator = new LatLngAnimator(previousCameraTarget, currentTarget); - cameraLatLngAnimator.addUpdateListener(cameraLatLngUpdateListener); - } - - private void cancelCameraGpsBearingAnimations() { - if (cameraGpsBearingAnimator != null) { - cameraGpsBearingAnimator.cancel(); - cameraGpsBearingAnimator.removeAllUpdateListeners(); - } - } - - private void resetCameraGpsBearingAnimation(CameraPosition currentCameraPosition, boolean isGpsNorth) { - if (cameraGpsBearingAnimator == null) { - return; - } - cancelCameraGpsBearingAnimations(); - float currentTargetBearing = cameraGpsBearingAnimator.getTargetBearing(); - currentTargetBearing = checkGpsNorth(isGpsNorth, currentTargetBearing); - float previousCameraBearing = (float) currentCameraPosition.bearing; - float normalizedCameraBearing = Utils.shortestRotation(currentTargetBearing, previousCameraBearing); - cameraGpsBearingAnimator = new BearingAnimator(previousCameraBearing, normalizedCameraBearing); - cameraGpsBearingAnimator.addUpdateListener(cameraGpsBearingUpdateListener); - } - - private void cancelCameraCompassAnimations() { - if (cameraCompassBearingAnimator != null) { - cameraCompassBearingAnimator.cancel(); - cameraCompassBearingAnimator.removeAllUpdateListeners(); - } - } - - private void resetCameraCompassAnimation(CameraPosition currentCameraPosition) { - if (cameraCompassBearingAnimator == null || cameraLatLngAnimator == null) { - return; - } - cancelCameraCompassAnimations(); - float currentTargetBearing = cameraCompassBearingAnimator.getTargetBearing(); - float previousCameraBearing = (float) currentCameraPosition.bearing; - float normalizedCameraBearing = Utils.shortestRotation(currentTargetBearing, previousCameraBearing); - cameraCompassBearingAnimator = new BearingAnimator(previousCameraBearing, normalizedCameraBearing); - cameraCompassBearingAnimator.addUpdateListener(cameraCompassBearingUpdateListener); - } -} diff --git a/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerCamera.java b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerCamera.java index 194fc3353..2aad8dfc8 100644 --- a/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerCamera.java +++ b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerCamera.java @@ -15,7 +15,7 @@ import java.util.List; import java.util.Set; -final class LocationLayerCamera implements LocationLayerAnimator.OnCameraAnimationsValuesChangeListener { +final class LocationLayerCamera implements PluginAnimator.OnCameraAnimationsValuesChangeListener { @CameraMode.Mode private int cameraMode; @@ -72,6 +72,16 @@ private void setLatLng(LatLng 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 @@ -79,12 +89,12 @@ public void onNewLatLngValue(LatLng latLng) { || 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; + if (adjustFocalPoint) { + PointF focalPoint = mapboxMap.getProjection().toScreenLocation(latLng); + mapboxMap.getUiSettings().setFocalPoint(focalPoint); + adjustFocalPoint = false; + } } } @@ -108,6 +118,16 @@ public void onNewCompassBearingValue(float compassBearing) { } } + @Override + public void onNewZoomValue(float zoom) { + setZoom(zoom); + } + + @Override + public void onNewTiltValue(float tilt) { + setTilt(tilt); + } + private void adjustGesturesThresholds() { if (isLocationTracking()) { adjustFocalPoint = true; @@ -160,9 +180,7 @@ public void onMove(MoveGestureDetector detector) { return; } - if (isLocationTracking()) { - setCameraMode(CameraMode.NONE); - } + setCameraMode(CameraMode.NONE); } @Override diff --git a/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerConstants.java b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerConstants.java index 8c27ce027..7cf1b43a9 100644 --- a/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerConstants.java +++ b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerConstants.java @@ -19,6 +19,12 @@ final class LocationLayerConstants { // 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_ANIMATION_DURATION = 750; + + // Default animation duration for tilting while tracking. + static final long DEFAULT_TRACKING_TILT_ANIMATION_DURATION = 1250; + // Sources static final String LOCATION_SOURCE = "mapbox-location-source"; static final String PROPERTY_GPS_BEARING = "mapbox-property-gps-bearing"; diff --git a/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerPlugin.java b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerPlugin.java index 2c39a4a8a..2cf811d9c 100644 --- a/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerPlugin.java +++ b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerPlugin.java @@ -16,6 +16,7 @@ import com.mapbox.android.core.location.LocationEnginePriority; import com.mapbox.android.core.location.LocationEngineProvider; import com.mapbox.mapboxsdk.camera.CameraPosition; +import com.mapbox.mapboxsdk.camera.CameraUpdate; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapView.OnMapChangedListener; @@ -28,8 +29,12 @@ import java.util.concurrent.CopyOnWriteArrayList; +import timber.log.Timber; + 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_ANIMATION_DURATION; +import static com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerConstants.DEFAULT_TRACKING_ZOOM_ANIMATION_DURATION; /** * The Location layer plugin provides location awareness to your mobile application. Enabling this @@ -71,7 +76,7 @@ public final class LocationLayerPlugin implements LifecycleObserver { private LocationLayer locationLayer; private LocationLayerCamera locationLayerCamera; - private LocationLayerAnimator locationLayerAnimator; + private PluginAnimatorCoordinator pluginAnimatorCoordinator; /** * Holds last location which is being returned in the {@link #getLastKnownLocation()} @@ -222,7 +227,7 @@ public boolean isLocationLayerEnabled() { */ public void setCameraMode(@CameraMode.Mode int cameraMode) { boolean isGpsNorth = cameraMode == CameraMode.TRACKING_GPS_NORTH; - locationLayerAnimator.resetAllCameraAnimations(mapboxMap.getCameraPosition(), isGpsNorth); + pluginAnimatorCoordinator.resetAllCameraAnimations(mapboxMap.getCameraPosition(), isGpsNorth); locationLayerCamera.setCameraMode(cameraMode); } @@ -303,6 +308,122 @@ public void applyStyle(LocationLayerOptions options) { 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) { + Timber.e("%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_ANIMATION_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) { + Timber.e("%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_ANIMATION_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. @@ -535,7 +656,7 @@ void onLocationLayerStop() { locationLayer.hide(); staleStateManager.onStop(); compassManager.onStop(); - locationLayerAnimator.cancelAllAnimations(); + pluginAnimatorCoordinator.cancelAllAnimations(); if (locationEngine != null) { locationEngine.removeLocationEngineListener(locationEngineListener); } @@ -556,9 +677,9 @@ private void initialize() { locationLayer = new LocationLayer(mapView, mapboxMap, options); locationLayerCamera = new LocationLayerCamera( mapView.getContext(), mapboxMap, cameraTrackingChangedListener, options, onCameraMoveInvalidateListener); - locationLayerAnimator = new LocationLayerAnimator(); - locationLayerAnimator.addLayerListener(locationLayer); - locationLayerAnimator.addCameraListener(locationLayerCamera); + pluginAnimatorCoordinator = new PluginAnimatorCoordinator(); + pluginAnimatorCoordinator.addLayerListener(locationLayer); + pluginAnimatorCoordinator.addCameraListener(locationLayerCamera); compassManager = new CompassManager(mapView.getContext()); compassManager.addCompassListener(compassListener); @@ -615,13 +736,13 @@ private void updateLocation(final Location location) { staleStateManager.updateLatestLocationTime(); CameraPosition currentCameraPosition = mapboxMap.getCameraPosition(); boolean isGpsNorth = getCameraMode() == CameraMode.TRACKING_GPS_NORTH; - locationLayerAnimator.feedNewLocation(location, currentCameraPosition, isGpsNorth); + pluginAnimatorCoordinator.feedNewLocation(location, currentCameraPosition, isGpsNorth); updateAccuracyRadius(location, false); lastLocation = location; } private void updateCompassHeading(float heading) { - locationLayerAnimator.feedNewCompassBearing(heading, mapboxMap.getCameraPosition()); + pluginAnimatorCoordinator.feedNewCompassBearing(heading, mapboxMap.getCameraPosition()); } /** @@ -663,7 +784,7 @@ private void updateLayerOffsets(boolean forceUpdate) { } private void updateAccuracyRadius(Location location, boolean noAnimation) { - locationLayerAnimator.feedNewAccuracyRadius(Utils.calculateZoomLevelRadius(mapboxMap, location), noAnimation); + pluginAnimatorCoordinator.feedNewAccuracyRadius(Utils.calculateZoomLevelRadius(mapboxMap, location), noAnimation); } private OnCameraMoveListener onCameraMoveListener = new OnCameraMoveListener() { @@ -771,6 +892,8 @@ public void onCameraTrackingDismissed() { @Override public void onCameraTrackingChanged(int currentMode) { + pluginAnimatorCoordinator.cancelZoomAnimation(); + pluginAnimatorCoordinator.cancelTiltAnimation(); for (OnCameraTrackingChangedListener listener : onCameraTrackingChangedListeners) { listener.onCameraTrackingChanged(currentMode); } diff --git a/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/MapboxCameraAnimatorAdapter.java b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/MapboxCameraAnimatorAdapter.java new file mode 100644 index 000000000..d2381cc1e --- /dev/null +++ b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/MapboxCameraAnimatorAdapter.java @@ -0,0 +1,38 @@ +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/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginAnimator.java b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginAnimator.java new file mode 100644 index 000000000..faa2a1ae4 --- /dev/null +++ b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginAnimator.java @@ -0,0 +1,92 @@ +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/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginAnimatorCoordinator.java b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginAnimatorCoordinator.java new file mode 100644 index 000000000..611a7b76b --- /dev/null +++ b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginAnimatorCoordinator.java @@ -0,0 +1,375 @@ +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") + private final Map animatorMap = new HashMap<>(); + + private final List layerListeners = new ArrayList<>(); + private 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(); + } + + 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/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginFloatAnimator.java b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginFloatAnimator.java new file mode 100644 index 000000000..f99f17cd9 --- /dev/null +++ b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginFloatAnimator.java @@ -0,0 +1,17 @@ +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/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginLatLngAnimator.java b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginLatLngAnimator.java new file mode 100644 index 000000000..29d73f8db --- /dev/null +++ b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/PluginLatLngAnimator.java @@ -0,0 +1,19 @@ +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/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/TiltAnimator.java b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/TiltAnimator.java new file mode 100644 index 000000000..2a12622df --- /dev/null +++ b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/TiltAnimator.java @@ -0,0 +1,27 @@ +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/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/ZoomAnimator.java b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/ZoomAnimator.java new file mode 100644 index 000000000..a1b1d4bf8 --- /dev/null +++ b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/ZoomAnimator.java @@ -0,0 +1,29 @@ +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/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/camera/AccuracyAnimator.java b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/camera/AccuracyAnimator.java deleted file mode 100644 index 37982978b..000000000 --- a/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/camera/AccuracyAnimator.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer.camera; - -import android.animation.FloatEvaluator; -import android.animation.ValueAnimator; - -public class AccuracyAnimator extends ValueAnimator { - - public AccuracyAnimator(float previous, float target) { - setEvaluator(new FloatEvaluator()); - setFloatValues(previous, target); - } -} \ No newline at end of file diff --git a/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/camera/BearingAnimator.java b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/camera/BearingAnimator.java deleted file mode 100644 index 53ec82454..000000000 --- a/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/camera/BearingAnimator.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer.camera; - -import android.animation.FloatEvaluator; -import android.animation.ValueAnimator; - -public class BearingAnimator extends ValueAnimator { - - private float targetBearing; - - public BearingAnimator(float previous, float target) { - setEvaluator(new FloatEvaluator()); - setFloatValues(previous, target); - this.targetBearing = target; - } - - public float getTargetBearing() { - return targetBearing; - } -} diff --git a/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/camera/LatLngAnimator.java b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/camera/LatLngAnimator.java deleted file mode 100644 index 243d9890d..000000000 --- a/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/camera/LatLngAnimator.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.mapbox.mapboxsdk.plugins.locationlayer.camera; - -import android.animation.TypeEvaluator; -import android.animation.ValueAnimator; -import android.support.annotation.NonNull; - -import com.mapbox.mapboxsdk.geometry.LatLng; - -public class LatLngAnimator extends ValueAnimator { - - private LatLng target; - - public LatLngAnimator(@NonNull LatLng previous, @NonNull LatLng target) { - setObjectValues(previous, target); - setEvaluator(new LatLngEvaluator()); - this.target = target; - } - - public LatLng getTarget() { - return target; - } - - private static 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/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/modes/CameraMode.java b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/modes/CameraMode.java index 68692a429..59cff613e 100644 --- a/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/modes/CameraMode.java +++ b/plugin-locationlayer/src/main/java/com/mapbox/mapboxsdk/plugins/locationlayer/modes/CameraMode.java @@ -35,7 +35,7 @@ private CameraMode() { * * @since 0.5.0 */ - public static final int NONE = 0x00000000; + public static final int NONE = 0x00000008; /** * Camera does not track location, but does track compass bearing. @@ -74,7 +74,6 @@ private CameraMode() { */ public static final int TRACKING_GPS = 0x00000022; - /** * Camera tracks the user location, with bearing * always set to north (0).