expose option to pass intermadiate points for smooth location animation

This commit is contained in:
Łukasz Paczos
2020-02-06 15:13:42 +01:00
parent b15e9f88fc
commit 5b1a4ff265
9 changed files with 262 additions and 65 deletions

View File

@@ -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));
}
}

View File

@@ -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.
* <p>
* 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<Location> 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<Location> 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<Location> 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) {

View File

@@ -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<K> extends ValueAnimator implements ValueAnimator.
private final double minUpdateInterval;
private long timeElapsed;
MapboxAnimator(@NonNull K previous, @NonNull K target, @NonNull AnimationsValueChangeListener<K> updateListener,
MapboxAnimator(@NonNull @Size(min = 2) K[] values, @NonNull AnimationsValueChangeListener<K> 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());
}

View File

@@ -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);
}
}

View File

@@ -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());
}

View File

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

View File

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

View File

@@ -56,34 +56,32 @@ class LocationAnimatorCoordinatorTest {
// workaround https://github.com/mockk/mockk/issues/229#issuecomment-457816131
registerInstanceFactory { AnimationsValueChangeListener<Float> {} }
registerInstanceFactory { AnimationsValueChangeListener<LatLng> {} }
val previousFloatSlot = slot<Float>()
val targetFloatSlot = slot<Float>()
val floatsSlot = slot<Array<Float>>()
val listenerSlot = slot<MapboxAnimator.AnimationsValueChangeListener<*>>()
val maxFpsSlot = slot<Int>()
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<LatLng>()
val targetLatLngSlot = slot<LatLng>()
val latLngsSlot = slot<Array<LatLng>>()
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<MapboxMap.CancelableCallback>()
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<LinearInterpolator>(), 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<LinearInterpolator>(), 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<AnimatorListenerHolder> {

View File

@@ -17,7 +17,7 @@ class MapboxAnimatorTest {
every { valueAnimator.animatedValue } answers { 5f }
val listener = mockk<MapboxAnimator.AnimationsValueChangeListener<Float>>()
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<MapboxAnimator.AnimationsValueChangeListener<Float>>()
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)