expose option to pass intermadiate points for smooth location animation
This commit is contained in:
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user