diff --git a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinator.java b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinator.java
index 248c7664b..474e524d1 100644
--- a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinator.java
+++ b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinator.java
@@ -3,12 +3,14 @@ package com.mapbox.mapboxsdk.location;
import android.animation.Animator;
import android.location.Location;
import android.os.SystemClock;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
import android.util.SparseArray;
import android.view.animation.LinearInterpolator;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.Size;
+import androidx.annotation.VisibleForTesting;
+
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.log.Logger;
@@ -33,6 +35,8 @@ 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;
+import static com.mapbox.mapboxsdk.location.Utils.normalize;
+import static com.mapbox.mapboxsdk.location.Utils.shortestRotation;
final class LocationAnimatorCoordinator {
@@ -74,6 +78,12 @@ final class LocationAnimatorCoordinator {
void feedNewLocation(@NonNull Location newLocation, @NonNull CameraPosition currentCameraPosition,
boolean isGpsNorth) {
+ feedNewLocation(new Location[] {newLocation}, currentCameraPosition, isGpsNorth, false);
+ }
+
+ void feedNewLocation(@NonNull @Size(min = 1) Location[] newLocations,
+ @NonNull CameraPosition currentCameraPosition, boolean isGpsNorth, boolean lookAheadUpdate) {
+ Location newLocation = newLocations[newLocations.length - 1];
if (previousLocation == null) {
previousLocation = newLocation;
locationUpdateTimestamp = SystemClock.elapsedRealtime() - TRANSITION_ANIMATION_DURATION_MS;
@@ -82,16 +92,23 @@ final class LocationAnimatorCoordinator {
LatLng previousLayerLatLng = getPreviousLayerLatLng();
float previousLayerBearing = getPreviousLayerGpsBearing();
LatLng previousCameraLatLng = currentCameraPosition.target;
- float previousCameraBearing = (float) currentCameraPosition.bearing;
+ float previousCameraBearing = normalize((float) currentCameraPosition.bearing);
+
+ // generate targets for layer
+ LatLng[] latLngValues = getLatLngValues(previousLayerLatLng, newLocations);
+ Float[] bearingValues = getBearingValues(previousLayerBearing, newLocations);
+ updateLayerAnimators(latLngValues, bearingValues);
+
+ // replace the animation start with the camera's previous value
+ latLngValues[0] = previousCameraLatLng;
+ if (isGpsNorth) {
+ bearingValues = new Float[] {previousCameraBearing, 0f};
+ } else {
+ bearingValues[0] = previousCameraBearing;
+ }
+ updateCameraAnimators(latLngValues, bearingValues);
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);
-
boolean snap = immediateAnimation(projection, previousCameraLatLng, targetLatLng)
|| immediateAnimation(projection, previousLayerLatLng, targetLatLng);
@@ -102,6 +119,15 @@ final class LocationAnimatorCoordinator {
if (previousUpdateTimeStamp == 0) {
animationDuration = 0;
+ } else if (lookAheadUpdate) {
+ long currentTimestamp = System.currentTimeMillis();
+ if (currentTimestamp > newLocation.getTime()) {
+ animationDuration = 0;
+ Logger.e("LocationAnimatorCoordinator",
+ "Lookahead enabled, but the target location's timestamp is smaller than current timestamp");
+ } else {
+ animationDuration = newLocation.getTime() - currentTimestamp;
+ }
} else {
animationDuration = (long) ((locationUpdateTimestamp - previousUpdateTimeStamp) * durationMultiplier)
/* make animation slightly longer with durationMultiplier, defaults to 1.1f */;
@@ -207,23 +233,35 @@ final class LocationAnimatorCoordinator {
return previousRadius;
}
- private void updateLayerAnimators(LatLng previousLatLng, LatLng targetLatLng,
- float previousBearing, float targetBearing) {
- createNewLatLngAnimator(ANIMATOR_LAYER_LATLNG, previousLatLng, targetLatLng);
+ private LatLng[] getLatLngValues(LatLng previousLatLng, Location[] targetLocations) {
+ LatLng[] latLngs = new LatLng[targetLocations.length + 1];
+ latLngs[0] = previousLatLng;
+ for (int i = 1; i < latLngs.length; i++) {
+ latLngs[i] = new LatLng(targetLocations[i - 1]);
+ }
+ return latLngs;
+ }
+
+ private Float[] getBearingValues(Float previousBearing, Location[] targetLocations) {
+ Float[] bearings = new Float[targetLocations.length + 1];
// Because Location bearing values are normalized to [0, 360]
// we need to do the same for the previous bearing value to determine the shortest path
- previousBearing = Utils.normalize(previousBearing);
- float normalizedLayerBearing = Utils.shortestRotation(targetBearing, previousBearing);
- createNewFloatAnimator(ANIMATOR_LAYER_GPS_BEARING, previousBearing, normalizedLayerBearing);
+ bearings[0] = normalize(previousBearing);
+ for (int i = 1; i < bearings.length; i++) {
+ bearings[i] = shortestRotation(targetLocations[i - 1].getBearing(), bearings[i - 1]);
+ }
+ return bearings;
}
- private void updateCameraAnimators(LatLng previousCameraLatLng, float previousCameraBearing,
- LatLng targetLatLng, float targetBearing) {
- createNewLatLngAnimator(ANIMATOR_CAMERA_LATLNG, previousCameraLatLng, targetLatLng);
+ private void updateLayerAnimators(LatLng[] latLngValues, Float[] bearingValues) {
+ createNewLatLngAnimator(ANIMATOR_LAYER_LATLNG, latLngValues);
+ createNewFloatAnimator(ANIMATOR_LAYER_GPS_BEARING, bearingValues);
+ }
- float normalizedCameraBearing = Utils.shortestRotation(targetBearing, previousCameraBearing);
- createNewFloatAnimator(ANIMATOR_CAMERA_GPS_BEARING, previousCameraBearing, normalizedCameraBearing);
+ private void updateCameraAnimators(LatLng[] latLngValues, Float[] bearingValues) {
+ createNewLatLngAnimator(ANIMATOR_CAMERA_LATLNG, latLngValues);
+ createNewFloatAnimator(ANIMATOR_CAMERA_GPS_BEARING, bearingValues);
}
private void updateCompassAnimators(float targetCompassBearing, float previousLayerBearing,
@@ -241,36 +279,45 @@ final class LocationAnimatorCoordinator {
private void updateZoomAnimator(float targetZoomLevel, float previousZoomLevel,
@Nullable MapboxMap.CancelableCallback cancelableCallback) {
- createNewCameraAdapterAnimator(ANIMATOR_ZOOM, previousZoomLevel, targetZoomLevel, cancelableCallback);
+ createNewCameraAdapterAnimator(ANIMATOR_ZOOM, new Float[] {previousZoomLevel, targetZoomLevel}, cancelableCallback);
}
private void updateTiltAnimator(float targetTilt, float previousTiltLevel,
@Nullable MapboxMap.CancelableCallback cancelableCallback) {
- createNewCameraAdapterAnimator(ANIMATOR_TILT, previousTiltLevel, targetTilt, cancelableCallback);
+ createNewCameraAdapterAnimator(ANIMATOR_TILT, new Float[] {previousTiltLevel, targetTilt}, cancelableCallback);
}
private void createNewLatLngAnimator(@MapboxAnimator.Type int animatorType, LatLng previous, LatLng target) {
+ createNewLatLngAnimator(animatorType, new LatLng[] {previous, target});
+ }
+
+ private void createNewLatLngAnimator(@MapboxAnimator.Type int animatorType, LatLng[] values) {
cancelAnimator(animatorType);
MapboxAnimator.AnimationsValueChangeListener listener = listeners.get(animatorType);
if (listener != null) {
- animatorArray.put(animatorType, animatorProvider.latLngAnimator(previous, target, listener, maxAnimationFps));
+ animatorArray.put(animatorType, animatorProvider.latLngAnimator(values, listener, maxAnimationFps));
}
}
private void createNewFloatAnimator(@MapboxAnimator.Type int animatorType, float previous, float target) {
+ createNewFloatAnimator(animatorType, new Float[] {previous, target});
+ }
+
+ private void createNewFloatAnimator(@MapboxAnimator.Type int animatorType, @NonNull @Size(min = 2) Float[] values) {
cancelAnimator(animatorType);
MapboxAnimator.AnimationsValueChangeListener listener = listeners.get(animatorType);
if (listener != null) {
- animatorArray.put(animatorType, animatorProvider.floatAnimator(previous, target, listener, maxAnimationFps));
+ animatorArray.put(animatorType, animatorProvider.floatAnimator(values, listener, maxAnimationFps));
}
}
- private void createNewCameraAdapterAnimator(@MapboxAnimator.Type int animatorType, float previous, float target,
+ private void createNewCameraAdapterAnimator(@MapboxAnimator.Type int animatorType,
+ @NonNull @Size(min = 2) Float[] values,
@Nullable MapboxMap.CancelableCallback cancelableCallback) {
cancelAnimator(animatorType);
MapboxAnimator.AnimationsValueChangeListener listener = listeners.get(animatorType);
if (listener != null) {
- animatorArray.put(animatorType, animatorProvider.cameraAnimator(previous, target, listener, cancelableCallback));
+ animatorArray.put(animatorType, animatorProvider.cameraAnimator(values, listener, cancelableCallback));
}
}
diff --git a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java
index 9df7a1456..0f7dfded0 100644
--- a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java
+++ b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java
@@ -6,12 +6,13 @@ import android.hardware.SensorManager;
import android.location.Location;
import android.os.Looper;
import android.os.SystemClock;
+import android.view.WindowManager;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresPermission;
import androidx.annotation.StyleRes;
import androidx.annotation.VisibleForTesting;
-import android.view.WindowManager;
import com.mapbox.android.core.location.LocationEngine;
import com.mapbox.android.core.location.LocationEngineCallback;
@@ -889,6 +890,32 @@ public final class LocationComponent {
updateLocation(location, false);
}
+ /**
+ * Use to either force a location update or to manually control when the user location gets
+ * updated.
+ *
+ * This method can be used to provide the list of locations where the last one is the target location
+ * and the rest are intermediate points used as the animation path.
+ * The puck and the camera will be animated between each of the points linearly until reaching the target.
+ *
+ * @param locations where the location icon is placed on the map
+ * @param lookAheadUpdate If set to true, the last location's timestamp has to be greater than current timestamp and
+ * should represent the time at which the animation should actually reach this position,
+ * cutting out the time interpolation delay.
+ */
+ public void forceLocationUpdate(@Nullable List locations, boolean lookAheadUpdate) {
+ checkActivationState();
+ if (locations != null && locations.size() >= 1) {
+ updateLocation(
+ locations.get(locations.size() - 1), // target location
+ locations.subList(0, locations.size() - 1), // intermediate locations
+ false,
+ lookAheadUpdate);
+ } else {
+ updateLocation(null, false);
+ }
+ }
+
/**
* Set max FPS at which location animators can output updates. The throttling will only impact the location puck
* and camera tracking smooth animations.
@@ -1341,6 +1368,11 @@ public final class LocationComponent {
* @param location the latest user location
*/
private void updateLocation(@Nullable final Location location, boolean fromLastLocation) {
+ updateLocation(location, null, fromLastLocation, false);
+ }
+
+ private void updateLocation(@Nullable final Location location, @Nullable List intermediatePoints,
+ boolean fromLastLocation, boolean lookAheadUpdate) {
if (location == null) {
return;
} else if (!isLayerReady) {
@@ -1362,11 +1394,28 @@ public final class LocationComponent {
}
CameraPosition currentCameraPosition = mapboxMap.getCameraPosition();
boolean isGpsNorth = getCameraMode() == CameraMode.TRACKING_GPS_NORTH;
- locationAnimatorCoordinator.feedNewLocation(location, currentCameraPosition, isGpsNorth);
+ if (intermediatePoints != null) {
+ locationAnimatorCoordinator.feedNewLocation(
+ getTargetLocationWithIntermediates(location, intermediatePoints),
+ currentCameraPosition,
+ isGpsNorth,
+ lookAheadUpdate);
+ } else {
+ locationAnimatorCoordinator.feedNewLocation(location, currentCameraPosition, isGpsNorth);
+ }
updateAccuracyRadius(location, false);
lastLocation = location;
}
+ private Location[] getTargetLocationWithIntermediates(Location location, List intermediatePoints) {
+ Location[] locations = new Location[intermediatePoints.size() + 1];
+ locations[locations.length - 1] = location;
+ for (int i = 0; i < intermediatePoints.size(); i++) {
+ locations[i] = intermediatePoints.get(i);
+ }
+ return locations;
+ }
+
private void showLocationLayerIfHidden() {
boolean isLocationLayerHidden = locationLayerController.isHidden();
if (isEnabled && isComponentStarted && isLocationLayerHidden) {
diff --git a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimator.java b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimator.java
index 7038367e3..f9a213241 100644
--- a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimator.java
+++ b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimator.java
@@ -4,8 +4,10 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
+
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
+import androidx.annotation.Size;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -48,13 +50,13 @@ abstract class MapboxAnimator extends ValueAnimator implements ValueAnimator.
private final double minUpdateInterval;
private long timeElapsed;
- MapboxAnimator(@NonNull K previous, @NonNull K target, @NonNull AnimationsValueChangeListener updateListener,
+ MapboxAnimator(@NonNull @Size(min = 2) K[] values, @NonNull AnimationsValueChangeListener updateListener,
int maxAnimationFps) {
minUpdateInterval = 1E9 / maxAnimationFps;
- setObjectValues(previous, target);
+ setObjectValues((Object[]) values);
setEvaluator(provideEvaluator());
this.updateListener = updateListener;
- this.target = target;
+ this.target = values[values.length - 1];
addUpdateListener(this);
addListener(new AnimatorListener());
}
diff --git a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimatorProvider.java b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimatorProvider.java
index 7782d8822..c1109b959 100644
--- a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimatorProvider.java
+++ b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimatorProvider.java
@@ -20,21 +20,19 @@ final class MapboxAnimatorProvider {
return INSTANCE;
}
- MapboxLatLngAnimator latLngAnimator(LatLng previous, LatLng target,
- MapboxAnimator.AnimationsValueChangeListener updateListener,
+ MapboxLatLngAnimator latLngAnimator(LatLng[] values, MapboxAnimator.AnimationsValueChangeListener updateListener,
int maxAnimationFps) {
- return new MapboxLatLngAnimator(previous, target, updateListener, maxAnimationFps);
+ return new MapboxLatLngAnimator(values, updateListener, maxAnimationFps);
}
- MapboxFloatAnimator floatAnimator(Float previous, Float target,
- MapboxAnimator.AnimationsValueChangeListener updateListener,
+ MapboxFloatAnimator floatAnimator(Float[] values, MapboxAnimator.AnimationsValueChangeListener updateListener,
int maxAnimationFps) {
- return new MapboxFloatAnimator(previous, target, updateListener, maxAnimationFps);
+ return new MapboxFloatAnimator(values, updateListener, maxAnimationFps);
}
- MapboxCameraAnimatorAdapter cameraAnimator(Float previous, Float target,
+ MapboxCameraAnimatorAdapter cameraAnimator(Float[] values,
MapboxAnimator.AnimationsValueChangeListener updateListener,
@Nullable MapboxMap.CancelableCallback cancelableCallback) {
- return new MapboxCameraAnimatorAdapter(previous, target, updateListener, cancelableCallback);
+ return new MapboxCameraAnimatorAdapter(values, updateListener, cancelableCallback);
}
}
diff --git a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxCameraAnimatorAdapter.java b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxCameraAnimatorAdapter.java
index 52ab7c4fc..a3b9fff42 100644
--- a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxCameraAnimatorAdapter.java
+++ b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxCameraAnimatorAdapter.java
@@ -2,7 +2,10 @@ package com.mapbox.mapboxsdk.location;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.Size;
import com.mapbox.mapboxsdk.maps.MapboxMap;
@@ -10,10 +13,10 @@ class MapboxCameraAnimatorAdapter extends MapboxFloatAnimator {
@Nullable
private final MapboxMap.CancelableCallback cancelableCallback;
- MapboxCameraAnimatorAdapter(Float previous, Float target,
+ MapboxCameraAnimatorAdapter(@NonNull @Size(min = 2) Float[] values,
AnimationsValueChangeListener updateListener,
@Nullable MapboxMap.CancelableCallback cancelableCallback) {
- super(previous, target, updateListener, Integer.MAX_VALUE);
+ super(values, updateListener, Integer.MAX_VALUE);
this.cancelableCallback = cancelableCallback;
addListener(new MapboxAnimatorListener());
}
diff --git a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxFloatAnimator.java b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxFloatAnimator.java
index 94f405b69..cad5fc983 100644
--- a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxFloatAnimator.java
+++ b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxFloatAnimator.java
@@ -2,11 +2,14 @@ package com.mapbox.mapboxsdk.location;
import android.animation.FloatEvaluator;
import android.animation.TypeEvaluator;
+
import androidx.annotation.NonNull;
+import androidx.annotation.Size;
class MapboxFloatAnimator extends MapboxAnimator {
- MapboxFloatAnimator(Float previous, Float target, AnimationsValueChangeListener updateListener, int maxAnimationFps) {
- super(previous, target, updateListener, maxAnimationFps);
+ MapboxFloatAnimator(@NonNull @Size(min = 2) Float[] values,
+ @NonNull AnimationsValueChangeListener updateListener, int maxAnimationFps) {
+ super(values, updateListener, maxAnimationFps);
}
@NonNull
diff --git a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxLatLngAnimator.java b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxLatLngAnimator.java
index 524847036..a32bf63ef 100644
--- a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxLatLngAnimator.java
+++ b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxLatLngAnimator.java
@@ -1,15 +1,16 @@
package com.mapbox.mapboxsdk.location;
import android.animation.TypeEvaluator;
+
import androidx.annotation.NonNull;
import com.mapbox.mapboxsdk.geometry.LatLng;
class MapboxLatLngAnimator extends MapboxAnimator {
- MapboxLatLngAnimator(LatLng previous, LatLng target, AnimationsValueChangeListener updateListener,
+ MapboxLatLngAnimator(@NonNull LatLng[] values, @NonNull AnimationsValueChangeListener updateListener,
int maxAnimationFps) {
- super(previous, target, updateListener, maxAnimationFps);
+ super(values, updateListener, maxAnimationFps);
}
@NonNull
diff --git a/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinatorTest.kt b/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinatorTest.kt
index 1b927d213..bd741e5b9 100644
--- a/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinatorTest.kt
+++ b/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinatorTest.kt
@@ -56,34 +56,32 @@ class LocationAnimatorCoordinatorTest {
// workaround https://github.com/mockk/mockk/issues/229#issuecomment-457816131
registerInstanceFactory { AnimationsValueChangeListener {} }
registerInstanceFactory { AnimationsValueChangeListener {} }
- val previousFloatSlot = slot()
- val targetFloatSlot = slot()
+ val floatsSlot = slot>()
val listenerSlot = slot>()
val maxFpsSlot = slot()
every {
- animatorProvider.floatAnimator(capture(previousFloatSlot), capture(targetFloatSlot), capture(listenerSlot), capture(maxFpsSlot))
+ animatorProvider.floatAnimator(capture(floatsSlot), capture(listenerSlot), capture(maxFpsSlot))
} answers {
- MapboxFloatAnimator(previousFloatSlot.captured, targetFloatSlot.captured, listenerSlot.captured, maxFpsSlot.captured)
+ MapboxFloatAnimator(floatsSlot.captured, listenerSlot.captured, maxFpsSlot.captured)
}
- val previousLatLngSlot = slot()
- val targetLatLngSlot = slot()
+ val latLngsSlot = slot>()
every {
- animatorProvider.latLngAnimator(capture(previousLatLngSlot), capture(targetLatLngSlot), capture(listenerSlot), capture(maxFpsSlot))
+ animatorProvider.latLngAnimator(capture(latLngsSlot), capture(listenerSlot), capture(maxFpsSlot))
} answers {
- MapboxLatLngAnimator(previousLatLngSlot.captured, targetLatLngSlot.captured, listenerSlot.captured, maxFpsSlot.captured)
+ MapboxLatLngAnimator(latLngsSlot.captured, listenerSlot.captured, maxFpsSlot.captured)
}
val callback = slot()
every {
- animatorProvider.cameraAnimator(capture(previousFloatSlot), capture(targetFloatSlot), capture(listenerSlot), capture(callback))
+ animatorProvider.cameraAnimator(capture(floatsSlot), capture(listenerSlot), capture(callback))
} answers {
- MapboxCameraAnimatorAdapter(previousFloatSlot.captured, targetFloatSlot.captured, listenerSlot.captured, callback.captured)
+ MapboxCameraAnimatorAdapter(floatsSlot.captured, listenerSlot.captured, callback.captured)
}
every {
- animatorProvider.cameraAnimator(capture(previousFloatSlot), capture(targetFloatSlot), capture(listenerSlot), null)
+ animatorProvider.cameraAnimator(capture(floatsSlot), capture(listenerSlot), null)
} answers {
- MapboxCameraAnimatorAdapter(previousFloatSlot.captured, targetFloatSlot.captured, listenerSlot.captured, null)
+ MapboxCameraAnimatorAdapter(floatsSlot.captured, listenerSlot.captured, null)
}
}
@@ -106,10 +104,12 @@ class LocationAnimatorCoordinatorTest {
locationAnimatorCoordinator.feedNewLocation(location, cameraPosition, false)
val cameraLatLngTarget = locationAnimatorCoordinator.animatorArray[ANIMATOR_CAMERA_LATLNG]?.target as LatLng
- assertEquals(cameraLatLngTarget.latitude, cameraLatLngTarget.latitude)
+ assertEquals(location.latitude, cameraLatLngTarget.latitude)
+ assertEquals(location.longitude, cameraLatLngTarget.longitude)
val layerLatLngTarget = locationAnimatorCoordinator.animatorArray[ANIMATOR_LAYER_LATLNG]?.target as LatLng
- assertEquals(layerLatLngTarget.latitude, layerLatLngTarget.latitude)
+ assertEquals(location.latitude, layerLatLngTarget.latitude)
+ assertEquals(location.longitude, layerLatLngTarget.longitude)
val cameraBearingTarget = locationAnimatorCoordinator.animatorArray[ANIMATOR_CAMERA_GPS_BEARING]?.target as Float
assertEquals(location.bearing, cameraBearingTarget)
@@ -118,6 +118,100 @@ class LocationAnimatorCoordinatorTest {
assertEquals(location.bearing, layerBearingTarget)
}
+ @Test
+ fun feedNewLocation_animatorValue_multiplePoints() {
+ val previousLocation = Location("")
+ previousLocation.latitude = 0.0
+ previousLocation.longitude = 0.0
+ previousLocation.bearing = 0f
+
+ val locationInter = Location("")
+ locationInter.latitude = 51.1
+ locationInter.longitude = 17.1
+ locationInter.bearing = 35f
+ val location = Location("")
+ location.latitude = 51.2
+ location.longitude = 17.2
+ location.bearing = 36f
+ locationAnimatorCoordinator.feedNewLocation(arrayOf(locationInter, location), cameraPosition, false, false)
+
+ val cameraLatLngTarget = locationAnimatorCoordinator.animatorArray[ANIMATOR_CAMERA_LATLNG]?.target as LatLng
+ assertEquals(location.latitude, cameraLatLngTarget.latitude)
+ assertEquals(location.longitude, cameraLatLngTarget.longitude)
+
+ val layerLatLngTarget = locationAnimatorCoordinator.animatorArray[ANIMATOR_LAYER_LATLNG]?.target as LatLng
+ assertEquals(location.latitude, layerLatLngTarget.latitude)
+ assertEquals(location.longitude, layerLatLngTarget.longitude)
+
+ val cameraBearingTarget = locationAnimatorCoordinator.animatorArray[ANIMATOR_CAMERA_GPS_BEARING]?.target as Float
+ assertEquals(location.bearing, cameraBearingTarget)
+
+ val layerBearingTarget = locationAnimatorCoordinator.animatorArray[ANIMATOR_LAYER_GPS_BEARING]?.target as Float
+ assertEquals(location.bearing, layerBearingTarget)
+
+ verify {
+ animatorProvider.latLngAnimator(
+ arrayOf(
+ LatLng(previousLocation.latitude, previousLocation.longitude),
+ LatLng(locationInter.latitude, locationInter.longitude),
+ LatLng(location.latitude, location.longitude)
+ ), any(), any()
+ )
+ }
+ verify {
+ animatorProvider.floatAnimator(
+ arrayOf(previousLocation.bearing, locationInter.bearing, location.bearing), any(), any()
+ )
+ }
+ }
+
+ @Test
+ fun feedNewLocation_animatorValue_multiplePoints_animationDuration() {
+ every { projection.getMetersPerPixelAtLatitude(any()) } answers { 10000.0 } // disable snap
+ val locationInter = Location("")
+ locationInter.latitude = 51.1
+ locationInter.longitude = 17.1
+ locationInter.bearing = 35f
+ val location = Location("")
+ location.latitude = 51.2
+ location.longitude = 17.2
+ location.bearing = 36f
+ locationAnimatorCoordinator.feedNewLocation(arrayOf(locationInter, location), cameraPosition, false, false)
+
+ verify {
+ animatorSetProvider.startAnimation(eq(listOf(
+ locationAnimatorCoordinator.animatorArray[ANIMATOR_LAYER_LATLNG],
+ locationAnimatorCoordinator.animatorArray[ANIMATOR_LAYER_GPS_BEARING],
+ locationAnimatorCoordinator.animatorArray[ANIMATOR_CAMERA_LATLNG],
+ locationAnimatorCoordinator.animatorArray[ANIMATOR_CAMERA_GPS_BEARING]
+ )), any(), 0)
+ }
+ }
+
+ @Test
+ fun feedNewLocation_animatorValue_multiplePoints_animationDuration_lookAhead() {
+ every { projection.getMetersPerPixelAtLatitude(any()) } answers { 10000.0 } // disable snap
+ val locationInter = Location("")
+ locationInter.latitude = 51.1
+ locationInter.longitude = 17.1
+ locationInter.bearing = 35f
+ val location = Location("")
+ location.latitude = 51.2
+ location.longitude = 17.2
+ location.bearing = 36f
+ location.time = System.currentTimeMillis() + 2000
+ locationAnimatorCoordinator.feedNewLocation(arrayOf(locationInter, location), cameraPosition, false, true)
+
+ verify {
+ animatorSetProvider.startAnimation(eq(listOf(
+ locationAnimatorCoordinator.animatorArray[ANIMATOR_LAYER_LATLNG],
+ locationAnimatorCoordinator.animatorArray[ANIMATOR_LAYER_GPS_BEARING],
+ locationAnimatorCoordinator.animatorArray[ANIMATOR_CAMERA_LATLNG],
+ locationAnimatorCoordinator.animatorArray[ANIMATOR_CAMERA_GPS_BEARING]
+ )), any(), more(1500L))
+ }
+ }
+
@Test
fun feedNewLocation_animatorValue_correctRotation_1() {
val location = Location("")
@@ -508,8 +602,8 @@ class LocationAnimatorCoordinatorTest {
fun maxFps_givenToAnimator() {
locationAnimatorCoordinator.setMaxAnimationFps(5)
locationAnimatorCoordinator.feedNewLocation(Location(""), cameraPosition, false)
- verify { animatorProvider.latLngAnimator(any(), any(), any(), 5) }
- verify { animatorProvider.floatAnimator(any(), any(), any(), 5) }
+ verify { animatorProvider.latLngAnimator(any(), any(), 5) }
+ verify { animatorProvider.floatAnimator(any(), any(), 5) }
}
private fun getListenerHoldersSet(vararg animatorTypes: Int): Set {
diff --git a/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/MapboxAnimatorTest.kt b/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/MapboxAnimatorTest.kt
index 7c126c783..0c5b770b4 100644
--- a/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/MapboxAnimatorTest.kt
+++ b/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/MapboxAnimatorTest.kt
@@ -17,7 +17,7 @@ class MapboxAnimatorTest {
every { valueAnimator.animatedValue } answers { 5f }
val listener = mockk>()
every { listener.onNewAnimationValue(any()) } answers {}
- val mapboxAnimator = MapboxFloatAnimator(0f, 10f, listener, Int.MAX_VALUE)
+ val mapboxAnimator = MapboxFloatAnimator(floatArrayOf(0f, 10f).toTypedArray(), listener, Int.MAX_VALUE)
for (i in 0 until 5)
mapboxAnimator.onAnimationUpdate(valueAnimator)
@@ -31,7 +31,7 @@ class MapboxAnimatorTest {
every { valueAnimator.animatedValue } answers { 5f }
val listener = mockk>()
every { listener.onNewAnimationValue(any()) } answers {}
- val mapboxAnimator = MapboxFloatAnimator(0f, 10f, listener, 5)
+ val mapboxAnimator = MapboxFloatAnimator(floatArrayOf(0f, 10f).toTypedArray(), listener, 5)
for (i in 0 until 5) {
mapboxAnimator.onAnimationUpdate(valueAnimator)