[android] optimize location animators memory footprint and add animation rate throttle option

This commit is contained in:
Łukasz Paczos
2019-01-03 15:59:39 +01:00
parent 2a535a8f5e
commit 9830c8309e
19 changed files with 354 additions and 445 deletions

View File

@@ -0,0 +1,45 @@
package com.mapbox.mapboxsdk.location;
class AnimatorListenerHolder {
@MapboxAnimator.Type
private final int animatorType;
private final MapboxAnimator.AnimationsValueChangeListener listener;
AnimatorListenerHolder(@MapboxAnimator.Type int animatorType, MapboxAnimator.AnimationsValueChangeListener listener) {
this.animatorType = animatorType;
this.listener = listener;
}
@MapboxAnimator.Type
public int getAnimatorType() {
return animatorType;
}
public MapboxAnimator.AnimationsValueChangeListener getListener() {
return listener;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
AnimatorListenerHolder that = (AnimatorListenerHolder) o;
if (animatorType != that.animatorType) {
return false;
}
return listener != null ? listener.equals(that.listener) : that.listener == null;
}
@Override
public int hashCode() {
int result = animatorType;
result = 31 * result + (listener != null ? listener.hashCode() : 0);
return result;
}
}

View File

@@ -1,25 +0,0 @@
package com.mapbox.mapboxsdk.location;
import android.animation.ValueAnimator;
import android.support.annotation.NonNull;
import java.util.List;
class CameraCompassBearingAnimator extends MapboxFloatAnimator<MapboxAnimator.OnCameraAnimationsValuesChangeListener> {
CameraCompassBearingAnimator(Float previous, Float target,
List<OnCameraAnimationsValuesChangeListener> updateListeners) {
super(previous, target, updateListeners);
}
@Override
int provideAnimatorType() {
return ANIMATOR_CAMERA_COMPASS_BEARING;
}
@Override
public void onAnimationUpdate(@NonNull ValueAnimator animation) {
for (OnCameraAnimationsValuesChangeListener listener : updateListeners) {
listener.onNewCompassBearingValue((Float) animation.getAnimatedValue());
}
}
}

View File

@@ -1,24 +0,0 @@
package com.mapbox.mapboxsdk.location;
import android.animation.ValueAnimator;
import android.support.annotation.NonNull;
import java.util.List;
class CameraGpsBearingAnimator extends MapboxFloatAnimator<MapboxAnimator.OnCameraAnimationsValuesChangeListener> {
CameraGpsBearingAnimator(Float previous, Float target, List<OnCameraAnimationsValuesChangeListener> updateListeners) {
super(previous, target, updateListeners);
}
@Override
int provideAnimatorType() {
return ANIMATOR_CAMERA_GPS_BEARING;
}
@Override
public void onAnimationUpdate(@NonNull ValueAnimator animation) {
for (OnCameraAnimationsValuesChangeListener listener : updateListeners) {
listener.onNewGpsBearingValue((Float) animation.getAnimatedValue());
}
}
}

View File

@@ -1,26 +0,0 @@
package com.mapbox.mapboxsdk.location;
import android.animation.ValueAnimator;
import android.support.annotation.NonNull;
import com.mapbox.mapboxsdk.geometry.LatLng;
import java.util.List;
class CameraLatLngAnimator extends MapboxLatLngAnimator<MapboxAnimator.OnCameraAnimationsValuesChangeListener> {
CameraLatLngAnimator(LatLng previous, LatLng target, List<OnCameraAnimationsValuesChangeListener> updateListeners) {
super(previous, target, updateListeners);
}
@Override
int provideAnimatorType() {
return ANIMATOR_CAMERA_LATLNG;
}
@Override
public void onAnimationUpdate(@NonNull ValueAnimator animation) {
for (OnCameraAnimationsValuesChangeListener listener : updateListeners) {
listener.onNewLatLngValue((LatLng) animation.getAnimatedValue());
}
}
}

View File

@@ -1,25 +0,0 @@
package com.mapbox.mapboxsdk.location;
import android.animation.ValueAnimator;
import android.support.annotation.NonNull;
import java.util.List;
class LayerAccuracyAnimator extends MapboxFloatAnimator<MapboxAnimator.OnLayerAnimationsValuesChangeListener> {
LayerAccuracyAnimator(Float previous, Float target, List<OnLayerAnimationsValuesChangeListener> updateListeners) {
super(previous, target, updateListeners);
}
@Override
int provideAnimatorType() {
return ANIMATOR_LAYER_ACCURACY;
}
@Override
public void onAnimationUpdate(@NonNull ValueAnimator animation) {
for (OnLayerAnimationsValuesChangeListener listener : updateListeners) {
listener.onNewAccuracyRadiusValue((Float) animation.getAnimatedValue());
}
}
}

View File

@@ -1,25 +0,0 @@
package com.mapbox.mapboxsdk.location;
import android.animation.ValueAnimator;
import android.support.annotation.NonNull;
import java.util.List;
class LayerCompassBearingAnimator extends MapboxFloatAnimator<MapboxAnimator.OnLayerAnimationsValuesChangeListener> {
LayerCompassBearingAnimator(Float previous,
Float target, List<OnLayerAnimationsValuesChangeListener> updateListeners) {
super(previous, target, updateListeners);
}
@Override
int provideAnimatorType() {
return ANIMATOR_LAYER_COMPASS_BEARING;
}
@Override
public void onAnimationUpdate(@NonNull ValueAnimator animation) {
for (OnLayerAnimationsValuesChangeListener listener : updateListeners) {
listener.onNewCompassBearingValue((Float) animation.getAnimatedValue());
}
}
}

View File

@@ -1,24 +0,0 @@
package com.mapbox.mapboxsdk.location;
import android.animation.ValueAnimator;
import android.support.annotation.NonNull;
import java.util.List;
class LayerGpsBearingAnimator extends MapboxFloatAnimator<MapboxAnimator.OnLayerAnimationsValuesChangeListener> {
LayerGpsBearingAnimator(Float previous, Float target, List<OnLayerAnimationsValuesChangeListener> updateListeners) {
super(previous, target, updateListeners);
}
@Override
int provideAnimatorType() {
return ANIMATOR_LAYER_GPS_BEARING;
}
@Override
public void onAnimationUpdate(@NonNull ValueAnimator animation) {
for (OnLayerAnimationsValuesChangeListener listener : updateListeners) {
listener.onNewGpsBearingValue((Float) animation.getAnimatedValue());
}
}
}

View File

@@ -1,26 +0,0 @@
package com.mapbox.mapboxsdk.location;
import android.animation.ValueAnimator;
import android.support.annotation.NonNull;
import com.mapbox.mapboxsdk.geometry.LatLng;
import java.util.List;
class LayerLatLngAnimator extends MapboxLatLngAnimator<MapboxAnimator.OnLayerAnimationsValuesChangeListener> {
LayerLatLngAnimator(LatLng previous, LatLng target, List<OnLayerAnimationsValuesChangeListener> updateListeners) {
super(previous, target, updateListeners);
}
@Override
int provideAnimatorType() {
return ANIMATOR_LAYER_LATLNG;
}
@Override
public void onAnimationUpdate(@NonNull ValueAnimator animation) {
for (OnLayerAnimationsValuesChangeListener listener : updateListeners) {
listener.onNewLatLngValue((LatLng) animation.getAnimatedValue());
}
}
}

View File

@@ -5,7 +5,6 @@ import android.location.Location;
import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.animation.FastOutSlowInInterpolator;
import android.util.SparseArray;
import android.view.animation.LinearInterpolator;
@@ -16,6 +15,7 @@ import com.mapbox.mapboxsdk.maps.Projection;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.ACCURACY_RADIUS_ANIMATION_DURATION;
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.COMPASS_UPDATE_RATE_MS;
@@ -36,9 +36,6 @@ final class LocationAnimatorCoordinator {
final SparseArray<MapboxAnimator> animatorArray = new SparseArray<>();
final List<MapboxAnimator.OnLayerAnimationsValuesChangeListener> layerListeners = new ArrayList<>();
final List<MapboxAnimator.OnCameraAnimationsValuesChangeListener> cameraListeners = new ArrayList<>();
private final Projection projection;
private Location previousLocation;
private float previousAccuracyRadius = -1;
@@ -48,26 +45,27 @@ final class LocationAnimatorCoordinator {
private final MapboxAnimatorSetProvider animatorSetProvider;
private boolean compassAnimationEnabled;
private boolean accuracyAnimationEnabled;
private int maxAnimationFps = Integer.MAX_VALUE;
private MapboxAnimator.AnimationsValueChangeListener[] listeners;
LocationAnimatorCoordinator(@NonNull Projection projection, @NonNull MapboxAnimatorSetProvider animatorSetProvider) {
this.projection = projection;
this.animatorSetProvider = animatorSetProvider;
}
void addLayerListener(MapboxAnimator.OnLayerAnimationsValuesChangeListener listener) {
layerListeners.add(listener);
}
void updateAnimatorListenerHolders(@NonNull Set<AnimatorListenerHolder> listenerHolders) {
int maxIndex = 0;
for (AnimatorListenerHolder holder : listenerHolders) {
if (holder.getAnimatorType() > maxIndex) {
maxIndex = holder.getAnimatorType();
}
}
void removeLayerListener(MapboxAnimator.OnLayerAnimationsValuesChangeListener listener) {
layerListeners.remove(listener);
}
void addCameraListener(MapboxAnimator.OnCameraAnimationsValuesChangeListener listener) {
cameraListeners.add(listener);
}
void removeCameraListener(MapboxAnimator.OnCameraAnimationsValuesChangeListener listener) {
cameraListeners.remove(listener);
listeners = new MapboxAnimator.AnimationsValueChangeListener[maxIndex + 1];
for (AnimatorListenerHolder holder : listenerHolders) {
listeners[holder.getAnimatorType()] = holder.getListener();
}
}
void feedNewLocation(@NonNull Location newLocation, @NonNull CameraPosition currentCameraPosition,
@@ -92,7 +90,12 @@ final class LocationAnimatorCoordinator {
boolean snap = immediateAnimation(projection, previousCameraLatLng, targetLatLng)
|| immediateAnimation(projection, previousLayerLatLng, targetLatLng);
playLocationAnimators(snap ? 0 : getAnimationDuration());
playAnimators(
snap ? 0 : getAnimationDuration(),
ANIMATOR_LAYER_LATLNG,
ANIMATOR_LAYER_GPS_BEARING,
ANIMATOR_CAMERA_LATLNG,
ANIMATOR_CAMERA_GPS_BEARING);
previousLocation = newLocation;
}
@@ -106,7 +109,10 @@ final class LocationAnimatorCoordinator {
float previousCameraBearing = (float) currentCameraPosition.bearing;
updateCompassAnimators(targetCompassBearing, previousLayerBearing, previousCameraBearing);
playCompassAnimators(compassAnimationEnabled ? COMPASS_UPDATE_RATE_MS : 0);
playAnimators(
compassAnimationEnabled ? COMPASS_UPDATE_RATE_MS : 0,
ANIMATOR_LAYER_COMPASS_BEARING,
ANIMATOR_CAMERA_COMPASS_BEARING);
previousCompassBearing = targetCompassBearing;
}
@@ -118,7 +124,9 @@ final class LocationAnimatorCoordinator {
float previousAccuracyRadius = getPreviousAccuracyRadius();
updateAccuracyAnimators(targetAccuracyRadius, previousAccuracyRadius);
playAccuracyAnimator(noAnimation || !accuracyAnimationEnabled ? 0 : ACCURACY_RADIUS_ANIMATION_DURATION);
playAnimators(
noAnimation || !accuracyAnimationEnabled ? 0 : ACCURACY_RADIUS_ANIMATION_DURATION,
ANIMATOR_LAYER_ACCURACY);
this.previousAccuracyRadius = targetAccuracyRadius;
}
@@ -126,13 +134,13 @@ final class LocationAnimatorCoordinator {
void feedNewZoomLevel(double targetZoomLevel, @NonNull CameraPosition currentCameraPosition, long animationDuration,
@Nullable MapboxMap.CancelableCallback callback) {
updateZoomAnimator((float) targetZoomLevel, (float) currentCameraPosition.zoom, callback);
playZoomAnimator(animationDuration);
playAnimators(animationDuration, ANIMATOR_ZOOM);
}
void feedNewTilt(double targetTilt, @NonNull CameraPosition currentCameraPosition, long animationDuration,
@Nullable MapboxMap.CancelableCallback callback) {
updateTiltAnimator((float) targetTilt, (float) currentCameraPosition.tilt, callback);
playTiltAnimator(animationDuration);
playAnimators(animationDuration, ANIMATOR_TILT);
}
private LatLng getPreviousLayerLatLng() {
@@ -147,7 +155,7 @@ final class LocationAnimatorCoordinator {
}
private float getPreviousLayerGpsBearing() {
LayerGpsBearingAnimator animator = (LayerGpsBearingAnimator) animatorArray.get(ANIMATOR_LAYER_GPS_BEARING);
MapboxFloatAnimator animator = (MapboxFloatAnimator) animatorArray.get(ANIMATOR_LAYER_GPS_BEARING);
float previousBearing;
if (animator != null) {
previousBearing = (float) animator.getAnimatedValue();
@@ -158,8 +166,7 @@ final class LocationAnimatorCoordinator {
}
private float getPreviousLayerCompassBearing() {
LayerCompassBearingAnimator animator =
(LayerCompassBearingAnimator) animatorArray.get(ANIMATOR_LAYER_COMPASS_BEARING);
MapboxFloatAnimator animator = (MapboxFloatAnimator) animatorArray.get(ANIMATOR_LAYER_COMPASS_BEARING);
float previousBearing;
if (animator != null) {
@@ -171,7 +178,7 @@ final class LocationAnimatorCoordinator {
}
private float getPreviousAccuracyRadius() {
LayerAccuracyAnimator animator = (LayerAccuracyAnimator) animatorArray.get(ANIMATOR_LAYER_ACCURACY);
MapboxAnimator animator = animatorArray.get(ANIMATOR_LAYER_ACCURACY);
float previousRadius;
if (animator != null) {
previousRadius = (float) animator.getAnimatedValue();
@@ -183,49 +190,66 @@ final class LocationAnimatorCoordinator {
private void updateLayerAnimators(LatLng previousLatLng, LatLng targetLatLng,
float previousBearing, float targetBearing) {
createNewAnimator(ANIMATOR_LAYER_LATLNG, new LayerLatLngAnimator(previousLatLng, targetLatLng, layerListeners));
createNewLatLngAnimator(ANIMATOR_LAYER_LATLNG, previousLatLng, targetLatLng);
float normalizedLayerBearing = Utils.shortestRotation(targetBearing, previousBearing);
createNewAnimator(ANIMATOR_LAYER_GPS_BEARING,
new LayerGpsBearingAnimator(previousBearing, normalizedLayerBearing, layerListeners));
createNewFloatAnimator(ANIMATOR_LAYER_GPS_BEARING, previousBearing, normalizedLayerBearing);
}
private void updateCameraAnimators(LatLng previousCameraLatLng, float previousCameraBearing,
LatLng targetLatLng, float targetBearing) {
createNewAnimator(ANIMATOR_CAMERA_LATLNG,
new CameraLatLngAnimator(previousCameraLatLng, targetLatLng, cameraListeners));
createNewLatLngAnimator(ANIMATOR_CAMERA_LATLNG, previousCameraLatLng, targetLatLng);
float normalizedCameraBearing = Utils.shortestRotation(targetBearing, previousCameraBearing);
createNewAnimator(ANIMATOR_CAMERA_GPS_BEARING,
new CameraGpsBearingAnimator(previousCameraBearing, normalizedCameraBearing, cameraListeners));
createNewFloatAnimator(ANIMATOR_CAMERA_GPS_BEARING, previousCameraBearing, normalizedCameraBearing);
}
private void updateCompassAnimators(float targetCompassBearing, float previousLayerBearing,
float previousCameraBearing) {
float normalizedLayerBearing = Utils.shortestRotation(targetCompassBearing, previousLayerBearing);
createNewAnimator(ANIMATOR_LAYER_COMPASS_BEARING,
new LayerCompassBearingAnimator(previousLayerBearing, normalizedLayerBearing, layerListeners));
createNewFloatAnimator(ANIMATOR_LAYER_COMPASS_BEARING, previousLayerBearing, normalizedLayerBearing);
float normalizedCameraBearing = Utils.shortestRotation(targetCompassBearing, previousCameraBearing);
createNewAnimator(ANIMATOR_CAMERA_COMPASS_BEARING,
new CameraCompassBearingAnimator(previousCameraBearing, normalizedCameraBearing, cameraListeners));
createNewFloatAnimator(ANIMATOR_CAMERA_COMPASS_BEARING, previousCameraBearing, normalizedCameraBearing);
}
private void updateAccuracyAnimators(float targetAccuracyRadius, float previousAccuracyRadius) {
createNewAnimator(ANIMATOR_LAYER_ACCURACY,
new LayerAccuracyAnimator(previousAccuracyRadius, targetAccuracyRadius, layerListeners));
createNewFloatAnimator(ANIMATOR_LAYER_ACCURACY, previousAccuracyRadius, targetAccuracyRadius);
}
private void updateZoomAnimator(float targetZoomLevel, float previousZoomLevel,
@Nullable MapboxMap.CancelableCallback cancelableCallback) {
createNewAnimator(ANIMATOR_ZOOM,
new ZoomAnimator(previousZoomLevel, targetZoomLevel, cameraListeners, cancelableCallback));
createNewCameraAdapterAnimator(ANIMATOR_ZOOM, previousZoomLevel, targetZoomLevel, cancelableCallback);
}
private void updateTiltAnimator(float targetTilt, float previousTiltLevel,
@Nullable MapboxMap.CancelableCallback cancelableCallback) {
createNewAnimator(ANIMATOR_TILT,
new TiltAnimator(previousTiltLevel, targetTilt, cameraListeners, cancelableCallback));
createNewCameraAdapterAnimator(ANIMATOR_TILT, previousTiltLevel, targetTilt, cancelableCallback);
}
private void createNewLatLngAnimator(@MapboxAnimator.Type int animatorType, LatLng previous, LatLng target) {
cancelAnimator(animatorType);
MapboxAnimator.AnimationsValueChangeListener listener = listeners[animatorType];
if (listener != null) {
animatorArray.put(animatorType, new MapboxLatLngAnimator(previous, target, listener, maxAnimationFps));
}
}
private void createNewFloatAnimator(@MapboxAnimator.Type int animatorType, float previous, float target) {
cancelAnimator(animatorType);
MapboxAnimator.AnimationsValueChangeListener listener = listeners[animatorType];
if (listener != null) {
animatorArray.put(animatorType, new MapboxFloatAnimator(previous, target, listener, maxAnimationFps));
}
}
private void createNewCameraAdapterAnimator(@MapboxAnimator.Type int animatorType, float previous, float target,
@Nullable MapboxMap.CancelableCallback cancelableCallback) {
cancelAnimator(animatorType);
MapboxAnimator.AnimationsValueChangeListener listener = listeners[animatorType];
if (listener != null) {
animatorArray.put(animatorType, new MapboxCameraAnimatorAdapter(previous, target, listener, cancelableCallback));
}
}
private long getAnimationDuration() {
@@ -252,51 +276,24 @@ final class LocationAnimatorCoordinator {
return targetCameraBearing;
}
private void playLocationAnimators(long duration) {
List<Animator> locationAnimators = new ArrayList<>();
locationAnimators.add(animatorArray.get(ANIMATOR_LAYER_LATLNG));
locationAnimators.add(animatorArray.get(ANIMATOR_LAYER_GPS_BEARING));
locationAnimators.add(animatorArray.get(ANIMATOR_CAMERA_LATLNG));
locationAnimators.add(animatorArray.get(ANIMATOR_CAMERA_GPS_BEARING));
animatorSetProvider.startAnimation(locationAnimators, new LinearInterpolator(), duration);
}
private void playCompassAnimators(long duration) {
List<Animator> compassAnimators = new ArrayList<>();
compassAnimators.add(animatorArray.get(ANIMATOR_LAYER_COMPASS_BEARING));
compassAnimators.add(animatorArray.get(ANIMATOR_CAMERA_COMPASS_BEARING));
animatorSetProvider.startAnimation(compassAnimators, new LinearInterpolator(), duration);
}
private void playAccuracyAnimator(long duration) {
List<Animator> accuracyAnimators = new ArrayList<>();
accuracyAnimators.add(animatorArray.get(ANIMATOR_LAYER_ACCURACY));
animatorSetProvider.startAnimation(accuracyAnimators, new LinearInterpolator(), duration);
}
private void playZoomAnimator(long duration) {
MapboxAnimator animator = animatorArray.get(ANIMATOR_ZOOM);
animator.setDuration(duration);
animator.start();
}
private void playTiltAnimator(long duration) {
MapboxAnimator animator = animatorArray.get(ANIMATOR_TILT);
animator.setDuration(duration);
animator.start();
}
private void playCameraLocationAnimators(long duration) {
List<Animator> locationAnimators = new ArrayList<>();
locationAnimators.add(animatorArray.get(ANIMATOR_CAMERA_LATLNG));
locationAnimators.add(animatorArray.get(ANIMATOR_CAMERA_GPS_BEARING));
animatorSetProvider.startAnimation(locationAnimators, new FastOutSlowInInterpolator(), duration);
private void playAnimators(long duration, @MapboxAnimator.Type int... animatorTypes) {
List<Animator> animators = new ArrayList<>();
for (@MapboxAnimator.Type int animatorType : animatorTypes) {
Animator animator = animatorArray.get(animatorType);
if (animator != null) {
animators.add(animator);
}
}
animatorSetProvider.startAnimation(animators, new LinearInterpolator(), duration);
}
void resetAllCameraAnimations(@NonNull CameraPosition currentCameraPosition, boolean isGpsNorth) {
resetCameraCompassAnimation(currentCameraPosition);
boolean snap = resetCameraLocationAnimations(currentCameraPosition, isGpsNorth);
playCameraLocationAnimators(snap ? 0 : TRANSITION_ANIMATION_DURATION_MS);
playAnimators(
snap ? 0 : TRANSITION_ANIMATION_DURATION_MS,
ANIMATOR_CAMERA_LATLNG,
ANIMATOR_CAMERA_GPS_BEARING);
}
private boolean resetCameraLocationAnimations(@NonNull CameraPosition currentCameraPosition, boolean isGpsNorth) {
@@ -305,21 +302,20 @@ final class LocationAnimatorCoordinator {
}
private boolean resetCameraLatLngAnimation(@NonNull CameraPosition currentCameraPosition) {
CameraLatLngAnimator animator = (CameraLatLngAnimator) animatorArray.get(ANIMATOR_CAMERA_LATLNG);
MapboxLatLngAnimator animator = (MapboxLatLngAnimator) animatorArray.get(ANIMATOR_CAMERA_LATLNG);
if (animator == null) {
return false;
}
LatLng currentTarget = animator.getTarget();
LatLng previousCameraTarget = currentCameraPosition.target;
createNewAnimator(ANIMATOR_CAMERA_LATLNG,
new CameraLatLngAnimator(previousCameraTarget, currentTarget, cameraListeners));
createNewLatLngAnimator(ANIMATOR_CAMERA_LATLNG, previousCameraTarget, currentTarget);
return immediateAnimation(projection, previousCameraTarget, currentTarget);
}
private void resetCameraGpsBearingAnimation(@NonNull CameraPosition currentCameraPosition, boolean isGpsNorth) {
CameraGpsBearingAnimator animator = (CameraGpsBearingAnimator) animatorArray.get(ANIMATOR_CAMERA_GPS_BEARING);
MapboxFloatAnimator animator = (MapboxFloatAnimator) animatorArray.get(ANIMATOR_CAMERA_GPS_BEARING);
if (animator == null) {
return;
}
@@ -328,13 +324,12 @@ final class LocationAnimatorCoordinator {
currentTargetBearing = checkGpsNorth(isGpsNorth, currentTargetBearing);
float previousCameraBearing = (float) currentCameraPosition.bearing;
float normalizedCameraBearing = Utils.shortestRotation(currentTargetBearing, previousCameraBearing);
createNewAnimator(ANIMATOR_CAMERA_GPS_BEARING,
new CameraGpsBearingAnimator(previousCameraBearing, normalizedCameraBearing, cameraListeners));
createNewFloatAnimator(ANIMATOR_CAMERA_GPS_BEARING, previousCameraBearing, normalizedCameraBearing);
}
private void resetCameraCompassAnimation(@NonNull CameraPosition currentCameraPosition) {
CameraCompassBearingAnimator animator =
(CameraCompassBearingAnimator) animatorArray.get(ANIMATOR_CAMERA_COMPASS_BEARING);
MapboxFloatAnimator animator =
(MapboxFloatAnimator) animatorArray.get(ANIMATOR_CAMERA_COMPASS_BEARING);
if (animator == null) {
return;
}
@@ -342,13 +337,7 @@ final class LocationAnimatorCoordinator {
float currentTargetBearing = animator.getTarget();
float previousCameraBearing = (float) currentCameraPosition.bearing;
float normalizedCameraBearing = Utils.shortestRotation(currentTargetBearing, previousCameraBearing);
createNewAnimator(ANIMATOR_CAMERA_COMPASS_BEARING,
new CameraCompassBearingAnimator(previousCameraBearing, normalizedCameraBearing, cameraListeners));
}
private void createNewAnimator(@MapboxAnimator.Type int animatorType, MapboxAnimator animator) {
cancelAnimator(animatorType);
animatorArray.put(animatorType, animator);
createNewFloatAnimator(ANIMATOR_CAMERA_COMPASS_BEARING, previousCameraBearing, normalizedCameraBearing);
}
void cancelZoomAnimation() {
@@ -387,4 +376,11 @@ final class LocationAnimatorCoordinator {
void setAccuracyAnimationEnabled(boolean accuracyAnimationEnabled) {
this.accuracyAnimationEnabled = accuracyAnimationEnabled;
}
void setMaxAnimationFps(int maxAnimationFps) {
if (maxAnimationFps <= 0) {
return;
}
this.maxAnimationFps = maxAnimationFps;
}
}

View File

@@ -18,7 +18,10 @@ import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.location.modes.CameraMode;
import com.mapbox.mapboxsdk.maps.MapboxMap;
final class LocationCameraController implements MapboxAnimator.OnCameraAnimationsValuesChangeListener {
import java.util.HashSet;
import java.util.Set;
final class LocationCameraController {
@CameraMode.Mode
private int cameraMode;
@@ -196,44 +199,73 @@ final class LocationCameraController implements MapboxAnimator.OnCameraAnimation
onCameraMoveInvalidateListener.onInvalidateCameraMove();
}
@Override
public void onNewLatLngValue(@NonNull LatLng latLng) {
if (cameraMode == CameraMode.TRACKING
|| cameraMode == CameraMode.TRACKING_COMPASS
|| cameraMode == CameraMode.TRACKING_GPS
|| cameraMode == CameraMode.TRACKING_GPS_NORTH) {
setLatLng(latLng);
private final MapboxAnimator.AnimationsValueChangeListener<LatLng> latLngValueListener =
new MapboxAnimator.AnimationsValueChangeListener<LatLng>() {
@Override
public void onNewAnimationValue(LatLng value) {
setLatLng(value);
}
};
private final MapboxAnimator.AnimationsValueChangeListener<Float> gpsBearingValueListener =
new MapboxAnimator.AnimationsValueChangeListener<Float>() {
@Override
public void onNewAnimationValue(Float value) {
boolean trackingNorth = cameraMode == CameraMode.TRACKING_GPS_NORTH
&& mapboxMap.getCameraPosition().bearing == 0;
if (!trackingNorth) {
setBearing(value);
}
}
};
private final MapboxAnimator.AnimationsValueChangeListener<Float> compassBearingValueListener =
new MapboxAnimator.AnimationsValueChangeListener<Float>() {
@Override
public void onNewAnimationValue(Float value) {
if (cameraMode == CameraMode.TRACKING_COMPASS
|| cameraMode == CameraMode.NONE_COMPASS) {
setBearing(value);
}
}
};
private final MapboxAnimator.AnimationsValueChangeListener<Float> zoomValueListener =
new MapboxAnimator.AnimationsValueChangeListener<Float>() {
@Override
public void onNewAnimationValue(Float value) {
setZoom(value);
}
};
private final MapboxAnimator.AnimationsValueChangeListener<Float> tiltValueListener =
new MapboxAnimator.AnimationsValueChangeListener<Float>() {
@Override
public void onNewAnimationValue(Float value) {
setTilt(value);
}
};
Set<AnimatorListenerHolder> getAnimationListeners() {
Set<AnimatorListenerHolder> holders = new HashSet<>();
if (isLocationTracking()) {
holders.add(new AnimatorListenerHolder(MapboxAnimator.ANIMATOR_CAMERA_LATLNG, latLngValueListener));
}
}
@Override
public void onNewGpsBearingValue(float gpsBearing) {
boolean trackingNorth = cameraMode == CameraMode.TRACKING_GPS_NORTH
&& mapboxMap.getCameraPosition().bearing != 0;
if (cameraMode == CameraMode.TRACKING_GPS
|| cameraMode == CameraMode.NONE_GPS
|| trackingNorth) {
setBearing(gpsBearing);
if (isLocationBearingTracking()) {
holders.add(new AnimatorListenerHolder(MapboxAnimator.ANIMATOR_CAMERA_GPS_BEARING, gpsBearingValueListener));
}
}
@Override
public void onNewCompassBearingValue(float compassBearing) {
if (cameraMode == CameraMode.TRACKING_COMPASS
|| cameraMode == CameraMode.NONE_COMPASS) {
setBearing(compassBearing);
if (isConsumingCompass()) {
holders.add(new AnimatorListenerHolder(
MapboxAnimator.ANIMATOR_CAMERA_COMPASS_BEARING,
compassBearingValueListener));
}
}
@Override
public void onNewZoomValue(float zoom) {
setZoom(zoom);
}
@Override
public void onNewTiltValue(float tilt) {
setTilt(tilt);
holders.add(new AnimatorListenerHolder(MapboxAnimator.ANIMATOR_ZOOM, zoomValueListener));
holders.add(new AnimatorListenerHolder(MapboxAnimator.ANIMATOR_TILT, tiltValueListener));
return holders;
}
boolean isTransitioning() {
@@ -273,7 +305,8 @@ final class LocationCameraController implements MapboxAnimator.OnCameraAnimation
private boolean isLocationBearingTracking() {
return cameraMode == CameraMode.TRACKING_GPS
|| cameraMode == CameraMode.TRACKING_GPS_NORTH;
|| cameraMode == CameraMode.TRACKING_GPS_NORTH
|| cameraMode == CameraMode.NONE_GPS;
}
private void notifyCameraTrackingChangeListener(boolean wasTracking) {

View File

@@ -32,6 +32,8 @@ import com.mapbox.mapboxsdk.maps.MapboxMap.OnMapClickListener;
import com.mapbox.mapboxsdk.maps.Style;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
@@ -698,6 +700,10 @@ public final class LocationComponent {
updateLocation(location, false);
}
public void setMaxAnimationFps(int maxAnimationFps) {
locationAnimatorCoordinator.setMaxAnimationFps(maxAnimationFps);
}
/**
* Set the location engine to update the current user location.
* <p>
@@ -989,7 +995,7 @@ public final class LocationComponent {
LayerFeatureProvider featureProvider = new LayerFeatureProvider();
LayerBitmapProvider bitmapProvider = new LayerBitmapProvider(context);
locationLayerController = new LocationLayerController(mapboxMap, style, sourceProvider, featureProvider,
bitmapProvider, options);
bitmapProvider, options, renderModeChangedListener);
locationCameraController = new LocationCameraController(
context, mapboxMap, cameraTrackingChangedListener, options, onCameraMoveInvalidateListener);
@@ -997,8 +1003,6 @@ public final class LocationComponent {
mapboxMap.getProjection(),
MapboxAnimatorSetProvider.getInstance()
);
locationAnimatorCoordinator.addLayerListener(locationLayerController);
locationAnimatorCoordinator.addCameraListener(locationCameraController);
locationAnimatorCoordinator.setTrackingAnimationDurationMultiplier(options
.trackingAnimationDurationMultiplier());
@@ -1164,6 +1168,13 @@ public final class LocationComponent {
locationAnimatorCoordinator.feedNewAccuracyRadius(Utils.calculateZoomLevelRadius(mapboxMap, location), noAnimation);
}
private void updateAnimatorListenerHolders() {
Set<AnimatorListenerHolder> animationsValueChangeListeners = new HashSet<>();
animationsValueChangeListeners.addAll(locationLayerController.getAnimationListeners());
animationsValueChangeListeners.addAll(locationCameraController.getAnimationListeners());
locationAnimatorCoordinator.updateAnimatorListenerHolders(animationsValueChangeListeners);
}
@NonNull
private OnCameraMoveListener onCameraMoveListener = new OnCameraMoveListener() {
@Override
@@ -1298,12 +1309,23 @@ public final class LocationComponent {
public void onCameraTrackingChanged(int currentMode) {
locationAnimatorCoordinator.cancelZoomAnimation();
locationAnimatorCoordinator.cancelTiltAnimation();
updateAnimatorListenerHolders();
locationAnimatorCoordinator.resetAllCameraAnimations(mapboxMap.getCameraPosition(),
locationCameraController.getCameraMode() == CameraMode.TRACKING_GPS_NORTH);
for (OnCameraTrackingChangedListener listener : onCameraTrackingChangedListeners) {
listener.onCameraTrackingChanged(currentMode);
}
}
};
@NonNull
private OnRenderModeChangedListener renderModeChangedListener = new OnRenderModeChangedListener() {
@Override
public void onRenderModeChanged(int currentMode) {
updateAnimatorListenerHolders();
}
};
static class InternalLocationEngineProvider {
LocationEngine getBestLocationEngine(@NonNull Context context, boolean background) {
return LocationEngineProvider.getBestLocationEngine(context, background);

View File

@@ -19,7 +19,9 @@ import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.ACCURACY_LAYER;
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.BACKGROUND_ICON;
@@ -56,7 +58,7 @@ import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconSize;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility;
import static com.mapbox.mapboxsdk.utils.ColorUtils.colorToRgbaString;
final class LocationLayerController implements MapboxAnimator.OnLayerAnimationsValuesChangeListener {
final class LocationLayerController {
@RenderMode.Mode
private int renderMode;
@@ -66,6 +68,7 @@ final class LocationLayerController implements MapboxAnimator.OnLayerAnimationsV
private final LayerSourceProvider layerSourceProvider;
private final LayerBitmapProvider bitmapProvider;
private LocationComponentOptions options;
private final OnRenderModeChangedListener internalRenderModeChangedListener;
private final List<String> layerMap = new ArrayList<>();
private Feature locationFeature;
@@ -78,14 +81,15 @@ final class LocationLayerController implements MapboxAnimator.OnLayerAnimationsV
LocationLayerController(MapboxMap mapboxMap, Style style, LayerSourceProvider layerSourceProvider,
LayerFeatureProvider featureProvider, LayerBitmapProvider bitmapProvider,
@NonNull LocationComponentOptions options) {
@NonNull LocationComponentOptions options,
@NonNull OnRenderModeChangedListener internalRenderModeChangedListener) {
this.mapboxMap = mapboxMap;
this.style = style;
this.layerSourceProvider = layerSourceProvider;
this.bitmapProvider = bitmapProvider;
this.locationFeature = featureProvider.generateLocationFeature(locationFeature, options);
this.internalRenderModeChangedListener = internalRenderModeChangedListener;
initializeComponents(style, options);
setRenderMode(RenderMode.NORMAL);
}
void initializeComponents(Style style, LocationComponentOptions options) {
@@ -127,6 +131,7 @@ final class LocationLayerController implements MapboxAnimator.OnLayerAnimationsV
}
void setRenderMode(@RenderMode.Mode int renderMode) {
int previousMode = this.renderMode;
this.renderMode = renderMode;
if (!isHidden) {
@@ -162,6 +167,10 @@ final class LocationLayerController implements MapboxAnimator.OnLayerAnimationsV
determineIconsSource(options);
}
if (previousMode != renderMode) {
internalRenderModeChangedListener.onRenderModeChanged(renderMode);
}
}
int getRenderMode() {
@@ -259,10 +268,8 @@ final class LocationLayerController implements MapboxAnimator.OnLayerAnimationsV
}
private void updateAccuracyRadius(float accuracy) {
if (renderMode == RenderMode.COMPASS || renderMode == RenderMode.NORMAL) {
locationFeature.addNumberProperty(PROPERTY_ACCURACY_RADIUS, accuracy);
refreshSource();
}
locationFeature.addNumberProperty(PROPERTY_ACCURACY_RADIUS, accuracy);
refreshSource();
}
//
@@ -400,28 +407,53 @@ final class LocationLayerController implements MapboxAnimator.OnLayerAnimationsV
return !features.isEmpty();
}
@Override
public void onNewLatLngValue(LatLng latLng) {
Point point = Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude());
setLocationPoint(point);
}
private final MapboxAnimator.AnimationsValueChangeListener<LatLng> latLngValueListener =
new MapboxAnimator.AnimationsValueChangeListener<LatLng>() {
@Override
public void onNewAnimationValue(LatLng value) {
Point point = Point.fromLngLat(value.getLongitude(), value.getLatitude());
setLocationPoint(point);
}
};
private final MapboxAnimator.AnimationsValueChangeListener<Float> gpsBearingValueListener =
new MapboxAnimator.AnimationsValueChangeListener<Float>() {
@Override
public void onNewAnimationValue(Float value) {
setBearingProperty(PROPERTY_GPS_BEARING, value);
}
};
private final MapboxAnimator.AnimationsValueChangeListener<Float> compassBearingValueListener =
new MapboxAnimator.AnimationsValueChangeListener<Float>() {
@Override
public void onNewAnimationValue(Float value) {
setBearingProperty(PROPERTY_COMPASS_BEARING, value);
}
};
private final MapboxAnimator.AnimationsValueChangeListener<Float> accuracyValueListener =
new MapboxAnimator.AnimationsValueChangeListener<Float>() {
@Override
public void onNewAnimationValue(Float value) {
updateAccuracyRadius(value);
}
};
Set<AnimatorListenerHolder> getAnimationListeners() {
Set<AnimatorListenerHolder> holders = new HashSet<>();
holders.add(new AnimatorListenerHolder(MapboxAnimator.ANIMATOR_LAYER_LATLNG, latLngValueListener));
@Override
public void onNewGpsBearingValue(float gpsBearing) {
if (renderMode == RenderMode.GPS) {
setBearingProperty(PROPERTY_GPS_BEARING, gpsBearing);
holders.add(new AnimatorListenerHolder(MapboxAnimator.ANIMATOR_LAYER_GPS_BEARING, gpsBearingValueListener));
} else if (renderMode == RenderMode.COMPASS) {
holders.add(new AnimatorListenerHolder(MapboxAnimator.ANIMATOR_LAYER_COMPASS_BEARING, compassBearingValueListener));
}
}
@Override
public void onNewCompassBearingValue(float compassBearing) {
if (renderMode == RenderMode.COMPASS) {
setBearingProperty(PROPERTY_COMPASS_BEARING, compassBearing);
if (renderMode == RenderMode.COMPASS || renderMode == RenderMode.NORMAL) {
holders.add(new AnimatorListenerHolder(MapboxAnimator.ANIMATOR_LAYER_ACCURACY, accuracyValueListener));
}
}
@Override
public void onNewAccuracyRadiusValue(float accuracyRadiusValue) {
updateAccuracyRadius(accuracyRadiusValue);
return holders;
}
}

View File

@@ -1,22 +1,21 @@
package com.mapbox.mapboxsdk.location;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
import android.support.annotation.IntDef;
import com.mapbox.mapboxsdk.geometry.LatLng;
import android.support.annotation.NonNull;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
/**
* Abstract class for all of the location component animators.
*
* @param <K> Data type that will be animated.
* @param <L> Listener of animation updates.
*/
abstract class MapboxAnimator<K, L> extends ValueAnimator implements ValueAnimator.AnimatorUpdateListener {
abstract class MapboxAnimator<K> extends ValueAnimator implements ValueAnimator.AnimatorUpdateListener {
@Retention(RetentionPolicy.SOURCE)
@IntDef( {
ANIMATOR_LAYER_LATLNG,
@@ -42,51 +41,55 @@ abstract class MapboxAnimator<K, L> extends ValueAnimator implements ValueAnimat
static final int ANIMATOR_ZOOM = 7;
static final int ANIMATOR_TILT = 8;
private final int animatorType = provideAnimatorType();
final List<L> updateListeners;
private final AnimationsValueChangeListener<K> updateListener;
private final K target;
private K animatedValue;
MapboxAnimator(K previous, K target, List<L> updateListeners) {
private double minUpdateInterval;
private long timeElapsed;
MapboxAnimator(@NonNull K previous, @NonNull K target, @NonNull AnimationsValueChangeListener<K> updateListener,
int maxAnimationFps) {
minUpdateInterval = 1E9 / maxAnimationFps;
setObjectValues(previous, target);
setEvaluator(provideEvaluator());
this.updateListeners = updateListeners;
this.updateListener = updateListener;
this.target = target;
addUpdateListener(this);
addListener(new AnimatorListener());
}
@Override
public void onAnimationUpdate(ValueAnimator animation) {
animatedValue = (K) animation.getAnimatedValue();
long currentTime = System.nanoTime();
if ((currentTime - timeElapsed) < minUpdateInterval) {
return;
}
postUpdates();
timeElapsed = currentTime;
}
private class AnimatorListener extends AnimatorListenerAdapter {
@Override
public void onAnimationEnd(Animator animation) {
postUpdates();
}
}
private void postUpdates() {
updateListener.onNewAnimationValue(animatedValue);
}
K getTarget() {
return target;
}
@Type
int getAnimatorType() {
return animatorType;
}
@Type
abstract int provideAnimatorType();
abstract TypeEvaluator provideEvaluator();
interface OnLayerAnimationsValuesChangeListener {
void onNewLatLngValue(LatLng latLng);
void onNewGpsBearingValue(float gpsBearing);
void onNewCompassBearingValue(float compassBearing);
void onNewAccuracyRadiusValue(float accuracyRadiusValue);
}
interface OnCameraAnimationsValuesChangeListener {
void onNewLatLngValue(LatLng latLng);
void onNewGpsBearingValue(float gpsBearing);
void onNewCompassBearingValue(float compassBearing);
void onNewZoomValue(float zoom);
void onNewTiltValue(float tilt);
interface AnimationsValueChangeListener<K> {
void onNewAnimationValue(K value);
}
}

View File

@@ -6,17 +6,14 @@ import android.support.annotation.Nullable;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import java.util.List;
abstract class MapboxCameraAnimatorAdapter extends
MapboxFloatAnimator<MapboxAnimator.OnCameraAnimationsValuesChangeListener> {
class MapboxCameraAnimatorAdapter extends MapboxFloatAnimator {
@Nullable
private final MapboxMap.CancelableCallback cancelableCallback;
MapboxCameraAnimatorAdapter(Float previous, Float target,
List<OnCameraAnimationsValuesChangeListener> updateListeners,
AnimationsValueChangeListener updateListener,
@Nullable MapboxMap.CancelableCallback cancelableCallback) {
super(previous, target, updateListeners);
super(previous, target, updateListener, Integer.MAX_VALUE);
this.cancelableCallback = cancelableCallback;
addListener(new MapboxAnimatorListener());
}

View File

@@ -4,11 +4,9 @@ import android.animation.FloatEvaluator;
import android.animation.TypeEvaluator;
import android.support.annotation.NonNull;
import java.util.List;
abstract class MapboxFloatAnimator<L> extends MapboxAnimator<Float, L> {
MapboxFloatAnimator(Float previous, Float target, List<L> updateListeners) {
super(previous, target, updateListeners);
class MapboxFloatAnimator extends MapboxAnimator<Float> {
MapboxFloatAnimator(Float previous, Float target, AnimationsValueChangeListener updateListener, int maxAnimationFps) {
super(previous, target, updateListener, maxAnimationFps);
}
@NonNull

View File

@@ -1,16 +1,15 @@
package com.mapbox.mapboxsdk.location;
import android.animation.TypeEvaluator;
import android.support.annotation.NonNull;
import com.mapbox.mapboxsdk.geometry.LatLng;
import java.util.List;
class MapboxLatLngAnimator extends MapboxAnimator<LatLng> {
abstract class MapboxLatLngAnimator<L> extends MapboxAnimator<LatLng, L> {
MapboxLatLngAnimator(LatLng previous, LatLng target, List<L> updateListeners) {
super(previous, target, updateListeners);
MapboxLatLngAnimator(LatLng previous, LatLng target, AnimationsValueChangeListener updateListener,
int maxAnimationFps) {
super(previous, target, updateListener, maxAnimationFps);
}
@NonNull

View File

@@ -0,0 +1,16 @@
package com.mapbox.mapboxsdk.location;
import com.mapbox.mapboxsdk.location.modes.RenderMode;
/**
* Listener that gets invoked when layer render mode changes.
*/
interface OnRenderModeChangedListener {
/**
* Invoked on every {@link RenderMode} change.
*
* @param currentMode current active {@link RenderMode}.
*/
void onRenderModeChanged(@RenderMode.Mode int currentMode);
}

View File

@@ -1,28 +0,0 @@
package com.mapbox.mapboxsdk.location;
import android.animation.ValueAnimator;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import java.util.List;
class TiltAnimator extends MapboxCameraAnimatorAdapter {
TiltAnimator(Float previous, Float target, List<OnCameraAnimationsValuesChangeListener> updateListeners,
@Nullable MapboxMap.CancelableCallback cancelableCallback) {
super(previous, target, updateListeners, cancelableCallback);
}
@Override
int provideAnimatorType() {
return ANIMATOR_TILT;
}
@Override
public void onAnimationUpdate(@NonNull ValueAnimator animation) {
for (OnCameraAnimationsValuesChangeListener listener : updateListeners) {
listener.onNewTiltValue((Float) animation.getAnimatedValue());
}
}
}

View File

@@ -1,29 +0,0 @@
package com.mapbox.mapboxsdk.location;
import android.animation.ValueAnimator;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import java.util.List;
class ZoomAnimator extends MapboxCameraAnimatorAdapter {
ZoomAnimator(Float previous, Float target, List<OnCameraAnimationsValuesChangeListener> updateListeners,
@Nullable MapboxMap.CancelableCallback cancelableCallback) {
super(previous, target, updateListeners, cancelableCallback);
}
@Override
int provideAnimatorType() {
return ANIMATOR_ZOOM;
}
@Override
public void onAnimationUpdate(@NonNull ValueAnimator animation) {
for (OnCameraAnimationsValuesChangeListener listener : updateListeners) {
listener.onNewZoomValue((Float) animation.getAnimatedValue());
}
}
}