summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorŁukasz Paczos <lukas.paczos@gmail.com>2018-12-06 18:03:54 +0100
committerŁukasz Paczos <lukas.paczos@gmail.com>2018-12-10 18:09:22 +0100
commitb4d92664fbd56a3ad831c1555782920bf9c257ae (patch)
tree33856a0d81468873f242ef5e815cbdbf8f7a0fc0
parentebd87a5442772e9e840cd5c00be56cccfddb5c68 (diff)
downloadqtlocation-mapboxgl-upstream/lp-13243-location-transition-handler.tar.gz
[android] CameraMode transition animation and result listenersupstream/lp-13243-location-transition-handler
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinator.java11
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationCameraController.java99
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java66
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationCameraTransitionListener.java24
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/Utils.java12
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationCameraControllerTest.java258
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentTest.kt42
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationModesActivity.java51
8 files changed, 523 insertions, 40 deletions
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
index fd8537f23f..b49be5c885 100644
--- 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
@@ -19,7 +19,6 @@ import java.util.List;
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.INSTANT_LOCATION_TRANSITION_THRESHOLD;
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;
@@ -31,6 +30,7 @@ import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_LAYER_GPS_BE
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;
+import static com.mapbox.mapboxsdk.location.Utils.immediateAnimation;
final class LocationAnimatorCoordinator {
@@ -381,13 +381,4 @@ final class LocationAnimatorCoordinator {
void setTrackingAnimationDurationMultiplier(float trackingAnimationDurationMultiplier) {
this.durationMultiplier = trackingAnimationDurationMultiplier;
}
-
- private boolean immediateAnimation(LatLng current, @NonNull LatLng target, double zoom) {
- // TODO: calculate the value based on the projection
- double distance = current.distanceTo(target);
- if (zoom > 10) {
- distance *= zoom;
- }
- return distance > INSTANT_LOCATION_TRANSITION_THRESHOLD;
- }
}
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
index a50b510073..0a876fb5cb 100644
--- 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
@@ -2,6 +2,7 @@ package com.mapbox.mapboxsdk.location;
import android.content.Context;
import android.graphics.PointF;
+import android.location.Location;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
@@ -9,6 +10,8 @@ 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.CameraPosition;
+import com.mapbox.mapboxsdk.camera.CameraUpdate;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.location.modes.CameraMode;
@@ -30,6 +33,8 @@ final class LocationCameraController implements MapboxAnimator.OnCameraAnimation
private final AndroidGesturesManager initialGesturesManager;
private final AndroidGesturesManager internalGesturesManager;
+ private boolean isTransitioning;
+
LocationCameraController(
Context context,
MapboxMap mapboxMap,
@@ -76,11 +81,68 @@ final class LocationCameraController implements MapboxAnimator.OnCameraAnimation
}
void setCameraMode(@CameraMode.Mode int cameraMode) {
+ setCameraMode(cameraMode, null, null);
+ }
+
+ void setCameraMode(@CameraMode.Mode final int cameraMode, @Nullable Location lastLocation,
+ @Nullable OnLocationCameraTransitionListener internalTransitionListener) {
final boolean wasTracking = isLocationTracking();
this.cameraMode = cameraMode;
mapboxMap.cancelTransitions();
adjustGesturesThresholds();
notifyCameraTrackingChangeListener(wasTracking);
+ transitionToCurrentLocation(wasTracking, lastLocation, internalTransitionListener);
+ }
+
+ /**
+ * Initiates a camera animation to the current location if location tracking was engaged.
+ * Notifies an internal listener when the transition's finished to invalidate animators and notify external listeners.
+ */
+ private void transitionToCurrentLocation(boolean wasTracking, Location lastLocation,
+ final OnLocationCameraTransitionListener internalTransitionListener) {
+ if (!wasTracking && isLocationTracking() && lastLocation != null) {
+ isTransitioning = true;
+ LatLng target = new LatLng(lastLocation);
+ CameraPosition.Builder builder = new CameraPosition.Builder().target(target);
+ if (isLocationBearingTracking()) {
+ builder.bearing(cameraMode == CameraMode.TRACKING_GPS_NORTH ? 0 : lastLocation.getBearing());
+ }
+
+ CameraUpdate update = CameraUpdateFactory.newCameraPosition(builder.build());
+ MapboxMap.CancelableCallback callback = new MapboxMap.CancelableCallback() {
+ @Override
+ public void onCancel() {
+ isTransitioning = false;
+ if (internalTransitionListener != null) {
+ internalTransitionListener.onLocationCameraTransitionCanceled(cameraMode);
+ }
+ }
+
+ @Override
+ public void onFinish() {
+ isTransitioning = false;
+ if (internalTransitionListener != null) {
+ internalTransitionListener.onLocationCameraTransitionFinished(cameraMode);
+ }
+ }
+ };
+
+ CameraPosition currentPosition = mapboxMap.getCameraPosition();
+ if (Utils.immediateAnimation(currentPosition.target, target, currentPosition.zoom)) {
+ mapboxMap.moveCamera(
+ update,
+ callback);
+ } else {
+ mapboxMap.animateCamera(
+ update,
+ (int) LocationComponentConstants.TRANSITION_ANIMATION_DURATION_MS,
+ callback);
+ }
+ } else {
+ if (internalTransitionListener != null) {
+ internalTransitionListener.onLocationCameraTransitionFinished(cameraMode);
+ }
+ }
}
int getCameraMode() {
@@ -88,21 +150,43 @@ final class LocationCameraController implements MapboxAnimator.OnCameraAnimation
}
private void setBearing(float bearing) {
+ if (isTransitioning) {
+ return;
+ }
+
mapboxMap.moveCamera(CameraUpdateFactory.bearingTo(bearing));
onCameraMoveInvalidateListener.onInvalidateCameraMove();
}
private void setLatLng(@NonNull LatLng latLng) {
+ if (isTransitioning) {
+ return;
+ }
+
mapboxMap.moveCamera(CameraUpdateFactory.newLatLng(latLng));
onCameraMoveInvalidateListener.onInvalidateCameraMove();
+
+ if (adjustFocalPoint) {
+ PointF focalPoint = mapboxMap.getProjection().toScreenLocation(latLng);
+ mapboxMap.getUiSettings().setFocalPoint(focalPoint);
+ adjustFocalPoint = false;
+ }
}
private void setZoom(float zoom) {
+ if (isTransitioning) {
+ return;
+ }
+
mapboxMap.moveCamera(CameraUpdateFactory.zoomTo(zoom));
onCameraMoveInvalidateListener.onInvalidateCameraMove();
}
private void setTilt(float tilt) {
+ if (isTransitioning) {
+ return;
+ }
+
mapboxMap.moveCamera(CameraUpdateFactory.tiltTo(tilt));
onCameraMoveInvalidateListener.onInvalidateCameraMove();
}
@@ -114,12 +198,6 @@ final class LocationCameraController implements MapboxAnimator.OnCameraAnimation
|| 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;
- }
}
}
@@ -153,6 +231,10 @@ final class LocationCameraController implements MapboxAnimator.OnCameraAnimation
setTilt(tilt);
}
+ boolean isTransitioning() {
+ return isTransitioning;
+ }
+
private void adjustGesturesThresholds() {
if (options.trackingGesturesManagement()) {
if (isLocationTracking()) {
@@ -179,6 +261,11 @@ final class LocationCameraController implements MapboxAnimator.OnCameraAnimation
|| cameraMode == CameraMode.TRACKING_GPS_NORTH;
}
+ private boolean isLocationBearingTracking() {
+ return cameraMode == CameraMode.TRACKING_GPS
+ || cameraMode == CameraMode.TRACKING_GPS_NORTH;
+ }
+
private void notifyCameraTrackingChangeListener(boolean wasTracking) {
internalCameraTrackingChangedListener.onCameraTrackingChanged(cameraMode);
if (wasTracking && !isLocationTracking()) {
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
index 340e84abef..7e312a8158 100644
--- 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
@@ -396,6 +396,10 @@ public final class LocationComponent {
/**
* Sets the camera mode, which determines how the map camera will track the rendered location.
* <p>
+ * When camera is transitioning to a new mode, it will reject inputs like {@link #zoomWhileTracking(double)} or
+ * {@link #tiltWhileTracking(double)}.
+ * Use {@link OnLocationCameraTransitionListener} to listen for the transition state.
+ * <p>
* <ul>
* <li>{@link CameraMode#NONE}: No camera tracking</li>
* <li>{@link CameraMode#NONE_COMPASS}: Camera does not track location, but does track compass bearing</li>
@@ -409,9 +413,65 @@ public final class LocationComponent {
* @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);
+ setCameraMode(cameraMode, null);
+ }
+
+ /**
+ * Sets the camera mode, which determines how the map camera will track the rendered location.
+ * <p>
+ * When camera is transitioning to a new mode, it will reject inputs like {@link #zoomWhileTracking(double)} or
+ * {@link #tiltWhileTracking(double)}.
+ * Use {@link OnLocationCameraTransitionListener} to listen for the transition state.
+ * <p>
+ * <ul>
+ * <li>{@link CameraMode#NONE}: No camera tracking</li>
+ * <li>{@link CameraMode#NONE_COMPASS}: Camera does not track location, but does track compass bearing</li>
+ * <li>{@link CameraMode#NONE_GPS}: Camera does not track location, but does track GPS bearing</li>
+ * <li>{@link CameraMode#TRACKING}: Camera tracks the user location</li>
+ * <li>{@link CameraMode#TRACKING_COMPASS}: Camera tracks the user location, with bearing provided by a compass</li>
+ * <li>{@link CameraMode#TRACKING_GPS}: Camera tracks the user location, with normalized bearing</li>
+ * <li>{@link CameraMode#TRACKING_GPS_NORTH}: Camera tracks the user location, with bearing always set to north</li>
+ * </ul>
+ *
+ * @param cameraMode one of the modes found in {@link CameraMode}
+ * @param transitionListener callback that's going to be invoked when the transition animation finishes
+ */
+ public void setCameraMode(@CameraMode.Mode int cameraMode,
+ @Nullable OnLocationCameraTransitionListener transitionListener) {
+ locationCameraController.setCameraMode(cameraMode, lastLocation, new CameraTransitionListener(transitionListener));
+ }
+
+ /**
+ * Used to reset camera animators and notify listeners when the transition finishes.
+ */
+ private class CameraTransitionListener implements OnLocationCameraTransitionListener {
+
+ private final OnLocationCameraTransitionListener externalListener;
+
+ private CameraTransitionListener(OnLocationCameraTransitionListener externalListener) {
+ this.externalListener = externalListener;
+ }
+
+ @Override
+ public void onLocationCameraTransitionFinished(int cameraMode) {
+ if (externalListener != null) {
+ externalListener.onLocationCameraTransitionFinished(cameraMode);
+ }
+ reset(cameraMode);
+ }
+
+ @Override
+ public void onLocationCameraTransitionCanceled(int cameraMode) {
+ if (externalListener != null) {
+ externalListener.onLocationCameraTransitionCanceled(cameraMode);
+ }
+ reset(cameraMode);
+ }
+
+ private void reset(@CameraMode.Mode int cameraMode) {
+ locationAnimatorCoordinator.resetAllCameraAnimations(mapboxMap.getCameraPosition(),
+ cameraMode == CameraMode.TRACKING_GPS_NORTH);
+ }
}
/**
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationCameraTransitionListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationCameraTransitionListener.java
new file mode 100644
index 0000000000..824432efc3
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationCameraTransitionListener.java
@@ -0,0 +1,24 @@
+package com.mapbox.mapboxsdk.location;
+
+import com.mapbox.mapboxsdk.location.modes.CameraMode;
+
+/**
+ * Callback for {@link CameraMode } transition state.
+ */
+public interface OnLocationCameraTransitionListener {
+ /**
+ * Invoked when the camera mode transition animation has been finished.
+ *
+ * @param cameraMode camera mode change that initiated the transition
+ */
+ void onLocationCameraTransitionFinished(@CameraMode.Mode int cameraMode);
+
+ /**
+ * Invoked when the camera mode transition animation has been canceled.
+ * <p>
+ * The camera mode is set regardless of the cancellation of the transition animation.
+ *
+ * @param cameraMode camera mode change that initiated the transition
+ */
+ void onLocationCameraTransitionCanceled(@CameraMode.Mode int cameraMode);
+}
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
index 8907b93b5f..7a2415ed5d 100644
--- 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
@@ -14,8 +14,11 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
+import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapboxMap;
+import static com.mapbox.mapboxsdk.location.LocationComponentConstants.INSTANT_LOCATION_TRANSITION_THRESHOLD;
+
public final class Utils {
private Utils() {
@@ -89,6 +92,15 @@ public final class Utils {
return (float) (location.getAccuracy() * (1 / metersPerPixel));
}
+ static boolean immediateAnimation(LatLng current, @NonNull LatLng target, double zoom) {
+ // TODO: calculate the value based on the projection
+ double distance = current.distanceTo(target);
+ if (zoom > 10) {
+ distance *= zoom;
+ }
+ return distance > INSTANT_LOCATION_TRANSITION_THRESHOLD;
+ }
+
/**
* Casts the value to an even integer.
*/
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
index 9e26bb1a60..4f950cebac 100644
--- 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
@@ -1,20 +1,29 @@
package com.mapbox.mapboxsdk.location;
import android.graphics.PointF;
+import android.location.Location;
import com.mapbox.android.gestures.AndroidGesturesManager;
import com.mapbox.android.gestures.MoveGestureDetector;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.camera.CameraUpdate;
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
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 junit.framework.Assert;
+
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -413,6 +422,255 @@ public class LocationCameraControllerTest {
verify(moveGestureDetector, times(1)).interrupt();
}
+ @Test
+ public void transition_locationIsNull() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ LocationCameraController camera = buildCamera(mapboxMap);
+ camera.initializeOptions(mock(LocationComponentOptions.class));
+ OnLocationCameraTransitionListener listener = mock(OnLocationCameraTransitionListener.class);
+
+ camera.setCameraMode(CameraMode.TRACKING, null, listener);
+ Assert.assertEquals(CameraMode.TRACKING, camera.getCameraMode());
+ verify(listener).onLocationCameraTransitionFinished(CameraMode.TRACKING);
+ verify(mapboxMap, times(0))
+ .animateCamera(any(CameraUpdate.class), any(Integer.class), any(MapboxMap.CancelableCallback.class));
+ }
+
+ @Test
+ public void transition_notTracking() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ LocationCameraController camera = buildCamera(mapboxMap);
+ camera.initializeOptions(mock(LocationComponentOptions.class));
+ OnLocationCameraTransitionListener listener = mock(OnLocationCameraTransitionListener.class);
+ Location location = mock(Location.class);
+
+ camera.setCameraMode(CameraMode.NONE, location, listener);
+ verify(listener, times(1)).onLocationCameraTransitionFinished(CameraMode.NONE);
+ verify(mapboxMap, times(0))
+ .animateCamera(any(CameraUpdate.class), any(Integer.class), any(MapboxMap.CancelableCallback.class));
+ }
+
+ @Test
+ public void transition_trackingChanged() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ when(mapboxMap.getCameraPosition()).thenReturn(CameraPosition.DEFAULT);
+ LocationCameraController camera = buildCamera(mapboxMap);
+ camera.initializeOptions(mock(LocationComponentOptions.class));
+ final OnLocationCameraTransitionListener listener = mock(OnLocationCameraTransitionListener.class);
+ Location location = mock(Location.class);
+
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ listener.onLocationCameraTransitionFinished(CameraMode.TRACKING);
+ return null;
+ }
+ }).when(mapboxMap).animateCamera(any(CameraUpdate.class), any(Integer.class), any(MapboxMap.CancelableCallback
+ .class));
+
+ camera.setCameraMode(CameraMode.TRACKING, location, listener);
+ verify(listener).onLocationCameraTransitionFinished(CameraMode.TRACKING);
+ verify(mapboxMap)
+ .animateCamera(any(CameraUpdate.class), any(Integer.class), any(MapboxMap.CancelableCallback.class));
+ }
+
+ @Test
+ public void transition_trackingNotChanged() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ when(mapboxMap.getCameraPosition()).thenReturn(CameraPosition.DEFAULT);
+ LocationCameraController camera = buildCamera(mapboxMap);
+ camera.initializeOptions(mock(LocationComponentOptions.class));
+ final OnLocationCameraTransitionListener listener = mock(OnLocationCameraTransitionListener.class);
+ Location location = mock(Location.class);
+
+ camera.setCameraMode(CameraMode.TRACKING, location, listener);
+
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ listener.onLocationCameraTransitionFinished(CameraMode.TRACKING_GPS_NORTH);
+ return null;
+ }
+ }).when(mapboxMap).animateCamera(any(CameraUpdate.class), any(Integer.class), any(MapboxMap.CancelableCallback
+ .class));
+
+ camera.setCameraMode(CameraMode.TRACKING_GPS_NORTH, location, listener);
+ verify(listener, times(1)).onLocationCameraTransitionFinished(CameraMode.TRACKING_GPS_NORTH);
+ verify(mapboxMap, times(1))
+ .animateCamera(any(CameraUpdate.class), any(Integer.class), any(MapboxMap.CancelableCallback.class));
+ }
+
+ @Test
+ public void transition_canceled() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ when(mapboxMap.getCameraPosition()).thenReturn(CameraPosition.DEFAULT);
+ LocationCameraController camera = buildCamera(mapboxMap);
+ camera.initializeOptions(mock(LocationComponentOptions.class));
+ final OnLocationCameraTransitionListener listener = mock(OnLocationCameraTransitionListener.class);
+ Location location = mock(Location.class);
+
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ listener.onLocationCameraTransitionCanceled(CameraMode.TRACKING);
+ return null;
+ }
+ }).when(mapboxMap).animateCamera(any(CameraUpdate.class), any(Integer.class), any(MapboxMap.CancelableCallback
+ .class));
+
+ camera.setCameraMode(CameraMode.TRACKING, location, listener);
+ verify(listener).onLocationCameraTransitionCanceled(CameraMode.TRACKING);
+ verify(mapboxMap)
+ .animateCamera(any(CameraUpdate.class), any(Integer.class), any(MapboxMap.CancelableCallback.class));
+ }
+
+ @Test
+ public void transition_mapboxCallbackFinished() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ when(mapboxMap.getCameraPosition()).thenReturn(CameraPosition.DEFAULT);
+ LocationCameraController camera = buildCamera(mapboxMap);
+ camera.initializeOptions(mock(LocationComponentOptions.class));
+ final OnLocationCameraTransitionListener listener = mock(OnLocationCameraTransitionListener.class);
+ Location location = mock(Location.class);
+ when(location.getLatitude()).thenReturn(1.0);
+ when(location.getLongitude()).thenReturn(1.0);
+ when(location.getBearing()).thenReturn(30f);
+ when(location.getAltitude()).thenReturn(0.0);
+
+ ArgumentCaptor<MapboxMap.CancelableCallback> callbackCaptor
+ = ArgumentCaptor.forClass(MapboxMap.CancelableCallback.class);
+
+ camera.setCameraMode(CameraMode.TRACKING, location, listener);
+
+ CameraPosition.Builder builder = new CameraPosition.Builder().target(new LatLng(location));
+ verify(mapboxMap).animateCamera(
+ eq(CameraUpdateFactory.newCameraPosition(builder.build())),
+ eq((int) LocationComponentConstants.TRANSITION_ANIMATION_DURATION_MS),
+ callbackCaptor.capture());
+
+ Assert.assertTrue(camera.isTransitioning());
+
+ callbackCaptor.getValue().onFinish();
+
+ Assert.assertFalse(camera.isTransitioning());
+
+ verify(listener).onLocationCameraTransitionFinished(CameraMode.TRACKING);
+ }
+
+ @Test
+ public void transition_mapboxCallbackCanceled() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ when(mapboxMap.getCameraPosition()).thenReturn(CameraPosition.DEFAULT);
+ LocationCameraController camera = buildCamera(mapboxMap);
+ camera.initializeOptions(mock(LocationComponentOptions.class));
+ final OnLocationCameraTransitionListener listener = mock(OnLocationCameraTransitionListener.class);
+ Location location = mock(Location.class);
+ when(location.getLatitude()).thenReturn(1.0);
+ when(location.getLongitude()).thenReturn(1.0);
+ when(location.getBearing()).thenReturn(30f);
+ when(location.getAltitude()).thenReturn(0.0);
+
+ ArgumentCaptor<MapboxMap.CancelableCallback> callbackCaptor
+ = ArgumentCaptor.forClass(MapboxMap.CancelableCallback.class);
+
+ camera.setCameraMode(CameraMode.TRACKING, location, listener);
+
+ CameraPosition.Builder builder = new CameraPosition.Builder().target(new LatLng(location));
+ verify(mapboxMap).animateCamera(
+ eq(CameraUpdateFactory.newCameraPosition(builder.build())),
+ eq((int) LocationComponentConstants.TRANSITION_ANIMATION_DURATION_MS),
+ callbackCaptor.capture());
+
+ Assert.assertTrue(camera.isTransitioning());
+
+ callbackCaptor.getValue().onCancel();
+
+ Assert.assertFalse(camera.isTransitioning());
+
+ verify(listener).onLocationCameraTransitionCanceled(CameraMode.TRACKING);
+ }
+
+ @Test
+ public void transition_mapboxAnimateBearing() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ when(mapboxMap.getCameraPosition()).thenReturn(CameraPosition.DEFAULT);
+ LocationCameraController camera = buildCamera(mapboxMap);
+ camera.initializeOptions(mock(LocationComponentOptions.class));
+ final OnLocationCameraTransitionListener listener = mock(OnLocationCameraTransitionListener.class);
+ Location location = mock(Location.class);
+ when(location.getLatitude()).thenReturn(1.0);
+ when(location.getLongitude()).thenReturn(1.0);
+ when(location.getBearing()).thenReturn(30f);
+ when(location.getAltitude()).thenReturn(0.0);
+
+ camera.setCameraMode(CameraMode.TRACKING_GPS, location, listener);
+
+ CameraPosition.Builder builder = new CameraPosition.Builder().target(new LatLng(location)).bearing(30);
+ verify(mapboxMap).animateCamera(
+ eq(CameraUpdateFactory.newCameraPosition(builder.build())),
+ eq((int) LocationComponentConstants.TRANSITION_ANIMATION_DURATION_MS),
+ any(MapboxMap.CancelableCallback.class));
+ }
+
+ @Test
+ public void transition_mapboxAnimateNorth() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ when(mapboxMap.getCameraPosition()).thenReturn(CameraPosition.DEFAULT);
+ LocationCameraController camera = buildCamera(mapboxMap);
+ camera.initializeOptions(mock(LocationComponentOptions.class));
+ final OnLocationCameraTransitionListener listener = mock(OnLocationCameraTransitionListener.class);
+ Location location = mock(Location.class);
+ when(location.getLatitude()).thenReturn(1.0);
+ when(location.getLongitude()).thenReturn(1.0);
+ when(location.getBearing()).thenReturn(30f);
+ when(location.getAltitude()).thenReturn(0.0);
+
+ camera.setCameraMode(CameraMode.TRACKING_GPS_NORTH, location, listener);
+
+ CameraPosition.Builder builder = new CameraPosition.Builder().target(new LatLng(location)).bearing(0);
+ verify(mapboxMap).animateCamera(
+ eq(CameraUpdateFactory.newCameraPosition(builder.build())),
+ eq((int) LocationComponentConstants.TRANSITION_ANIMATION_DURATION_MS),
+ any(MapboxMap.CancelableCallback.class));
+ }
+
+ @Test
+ public void transition_animatorValuesDuringTransition() {
+ MapboxMap mapboxMap = mock(MapboxMap.class);
+ when(mapboxMap.getCameraPosition()).thenReturn(CameraPosition.DEFAULT);
+ LocationCameraController camera = buildCamera(mapboxMap);
+ camera.initializeOptions(mock(LocationComponentOptions.class));
+ final OnLocationCameraTransitionListener listener = mock(OnLocationCameraTransitionListener.class);
+ Location location = mock(Location.class);
+
+ ArgumentCaptor<MapboxMap.CancelableCallback> callbackCaptor
+ = ArgumentCaptor.forClass(MapboxMap.CancelableCallback.class);
+
+ camera.setCameraMode(CameraMode.TRACKING_GPS, location, listener);
+
+ verify(mapboxMap).animateCamera(
+ any(CameraUpdate.class),
+ eq((int) LocationComponentConstants.TRANSITION_ANIMATION_DURATION_MS),
+ callbackCaptor.capture());
+
+ camera.onNewLatLngValue(new LatLng(10, 10));
+ camera.onNewGpsBearingValue(10f);
+ camera.onNewCompassBearingValue(10f);
+ camera.onNewTiltValue(10f);
+ camera.onNewZoomValue(10f);
+
+ verify(mapboxMap, times(0)).moveCamera(any(CameraUpdate.class));
+
+ callbackCaptor.getValue().onFinish();
+
+ camera.onNewLatLngValue(new LatLng(10, 10));
+ camera.onNewGpsBearingValue(10f);
+ camera.onNewTiltValue(10f);
+ camera.onNewZoomValue(10f);
+
+ verify(mapboxMap, times(4)).moveCamera(any(CameraUpdate.class));
+ }
+
private LocationCameraController buildCamera(OnCameraTrackingChangedListener onCameraTrackingChangedListener) {
MapboxMap mapboxMap = mock(MapboxMap.class);
when(mapboxMap.getUiSettings()).thenReturn(mock(UiSettings.class));
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentTest.kt
index b0bea5aa0e..2fa340fbb8 100644
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentTest.kt
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentTest.kt
@@ -7,6 +7,8 @@ import android.os.Looper
import com.mapbox.android.core.location.LocationEngine
import com.mapbox.android.core.location.LocationEngineRequest
import com.mapbox.mapboxsdk.R
+import com.mapbox.mapboxsdk.camera.CameraPosition
+import com.mapbox.mapboxsdk.location.modes.CameraMode
import com.mapbox.mapboxsdk.maps.MapboxMap
import com.mapbox.mapboxsdk.maps.Style
import io.mockk.mockk
@@ -14,7 +16,7 @@ import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.*
import org.mockito.MockitoAnnotations
@@ -90,7 +92,7 @@ class LocationComponentTest {
.getDimension(R.dimen.mapbox_locationComponentTrackingMultiFingerMoveThreshold)
doReturn(0f).`when`(resources)
.getDimension(R.dimen.mapbox_locationComponentTrackingMultiFingerMoveThreshold)
- locationComponent.activateLocationComponent(context, mockk(),true, locationEngineRequest)
+ locationComponent.activateLocationComponent(context, mockk(), true, locationEngineRequest)
Assert.assertEquals(locationEngineRequest, locationComponent.locationEngineRequest)
}
@@ -145,4 +147,40 @@ class LocationComponentTest {
verify(locationEngine).getLastLocation(lastListener)
}
+
+ @Test
+ fun transitionCallbackFinishedTest() {
+ locationComponent.activateLocationComponent(context, mockk(), locationEngine, locationEngineRequest, locationComponentOptions)
+ locationComponent.onStart()
+ locationComponent.isLocationComponentEnabled = true
+ `when`(mapboxMap.cameraPosition).thenReturn(CameraPosition.DEFAULT)
+
+ val listener = mock(OnLocationCameraTransitionListener::class.java)
+
+ val callback = ArgumentCaptor.forClass(OnLocationCameraTransitionListener::class.java)
+ locationComponent.setCameraMode(CameraMode.TRACKING, listener)
+ verify(locationCameraController).setCameraMode(eq(CameraMode.TRACKING), any(), callback.capture())
+ callback.value.onLocationCameraTransitionFinished(CameraMode.TRACKING)
+
+ verify(listener).onLocationCameraTransitionFinished(CameraMode.TRACKING)
+ verify(locationAnimatorCoordinator).resetAllCameraAnimations(CameraPosition.DEFAULT, false)
+ }
+
+ @Test
+ fun transitionCallbackCanceledTest() {
+ locationComponent.activateLocationComponent(context, mockk(), locationEngine, locationEngineRequest, locationComponentOptions)
+ locationComponent.onStart()
+ locationComponent.isLocationComponentEnabled = true
+ `when`(mapboxMap.cameraPosition).thenReturn(CameraPosition.DEFAULT)
+
+ val listener = mock(OnLocationCameraTransitionListener::class.java)
+
+ val callback = ArgumentCaptor.forClass(OnLocationCameraTransitionListener::class.java)
+ locationComponent.setCameraMode(CameraMode.TRACKING, listener)
+ verify(locationCameraController).setCameraMode(eq(CameraMode.TRACKING), any(), callback.capture())
+ callback.value.onLocationCameraTransitionCanceled(CameraMode.TRACKING)
+
+ verify(listener).onLocationCameraTransitionCanceled(CameraMode.TRACKING)
+ verify(locationAnimatorCoordinator).resetAllCameraAnimations(CameraPosition.DEFAULT, false)
+ }
} \ 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
index 068aa487d2..618032f5e0 100644
--- 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
@@ -19,6 +19,7 @@ import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.location.LocationComponent;
import com.mapbox.mapboxsdk.location.LocationComponentOptions;
import com.mapbox.mapboxsdk.location.OnCameraTrackingChangedListener;
+import com.mapbox.mapboxsdk.location.OnLocationCameraTransitionListener;
import com.mapbox.mapboxsdk.location.OnLocationClickListener;
import com.mapbox.mapboxsdk.location.modes.CameraMode;
import com.mapbox.mapboxsdk.location.modes.RenderMode;
@@ -341,35 +342,47 @@ public class LocationModesActivity extends AppCompatActivity implements OnMapRea
String selectedTrackingType = trackingTypes.get(position);
locationTrackingBtn.setText(selectedTrackingType);
if (selectedTrackingType.contentEquals("None")) {
- locationComponent.setCameraMode(CameraMode.NONE);
+ setCameraTrackingMode(CameraMode.NONE);
} else if (selectedTrackingType.contentEquals("Tracking")) {
- locationComponent.setCameraMode(CameraMode.TRACKING);
+ setCameraTrackingMode(CameraMode.TRACKING);
} else if (selectedTrackingType.contentEquals("Tracking Compass")) {
- locationComponent.setCameraMode(CameraMode.TRACKING_COMPASS);
+ setCameraTrackingMode(CameraMode.TRACKING_COMPASS);
} else if (selectedTrackingType.contentEquals("Tracking GPS")) {
- locationComponent.setCameraMode(CameraMode.TRACKING_GPS);
+ setCameraTrackingMode(CameraMode.TRACKING_GPS);
} else if (selectedTrackingType.contentEquals("Tracking GPS North")) {
- locationComponent.setCameraMode(CameraMode.TRACKING_GPS_NORTH);
+ setCameraTrackingMode(CameraMode.TRACKING_GPS_NORTH);
}
listPopup.dismiss();
+ });
+ listPopup.show();
+ }
- if (locationComponent.getCameraMode() != CameraMode.NONE) {
- locationComponent.zoomWhileTracking(15, 750, new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- // No impl
- }
+ private void setCameraTrackingMode(@CameraMode.Mode int mode) {
+ locationComponent.setCameraMode(mode, new OnLocationCameraTransitionListener() {
+ @Override
+ public void onLocationCameraTransitionFinished(@CameraMode.Mode int cameraMode) {
+ if (mode != 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));
+ }
+ }
- @Override
- public void onFinish() {
- locationComponent.tiltWhileTracking(45);
- }
- });
- } else {
- mapboxMap.easeCamera(CameraUpdateFactory.tiltTo(0));
+ @Override
+ public void onLocationCameraTransitionCanceled(@CameraMode.Mode int cameraMode) {
+ // No impl
}
});
- listPopup.show();
}
@Override