Skip to content

Commit

Permalink
Port: Allow setting padding when camera is tracking (#2165)
Browse files Browse the repository at this point in the history
  • Loading branch information
louwers authored Mar 4, 2024
1 parent d261bf5 commit 69f983b
Show file tree
Hide file tree
Showing 14 changed files with 399 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import static org.maplibre.android.location.MapLibreAnimator.ANIMATOR_LAYER_COMPASS_BEARING;
import static org.maplibre.android.location.MapLibreAnimator.ANIMATOR_LAYER_GPS_BEARING;
import static org.maplibre.android.location.MapLibreAnimator.ANIMATOR_LAYER_LATLNG;
import static org.maplibre.android.location.MapLibreAnimator.ANIMATOR_PADDING;
import static org.maplibre.android.location.MapLibreAnimator.ANIMATOR_PULSING_CIRCLE;
import static org.maplibre.android.location.MapLibreAnimator.ANIMATOR_TILT;
import static org.maplibre.android.location.MapLibreAnimator.ANIMATOR_ZOOM;
Expand Down Expand Up @@ -217,6 +218,12 @@ void feedNewZoomLevel(double targetZoomLevel, @NonNull CameraPosition currentCam
playAnimators(animationDuration, ANIMATOR_ZOOM);
}

void feedNewPadding(double[] padding, @NonNull CameraPosition currentCameraPosition, long animationDuration,
@Nullable MapLibreMap.CancelableCallback callback) {
updatePaddingAnimator(padding, currentCameraPosition.padding, callback);
playAnimators(animationDuration, ANIMATOR_PADDING);
}

void feedNewTilt(double targetTilt, @NonNull CameraPosition currentCameraPosition, long animationDuration,
@Nullable MapLibreMap.CancelableCallback callback) {
updateTiltAnimator((float) targetTilt, (float) currentCameraPosition.tilt, callback);
Expand Down Expand Up @@ -317,6 +324,11 @@ private void updateZoomAnimator(float targetZoomLevel, float previousZoomLevel,
createNewCameraAdapterAnimator(ANIMATOR_ZOOM, new Float[] {previousZoomLevel, targetZoomLevel}, cancelableCallback);
}

private void updatePaddingAnimator(double[] targetPadding, double[] previousPadding,
@Nullable MapLibreMap.CancelableCallback cancelableCallback) {
createNewPaddingAnimator(ANIMATOR_PADDING, new double[][] {previousPadding, targetPadding}, cancelableCallback);
}

private void updateTiltAnimator(float targetTilt, float previousTiltLevel,
@Nullable MapLibreMap.CancelableCallback cancelableCallback) {
createNewCameraAdapterAnimator(ANIMATOR_TILT, new Float[] {previousTiltLevel, targetTilt}, cancelableCallback);
Expand Down Expand Up @@ -356,6 +368,16 @@ private void createNewCameraAdapterAnimator(@MapLibreAnimator.Type int animatorT
}
}

private void createNewPaddingAnimator(@MapLibreAnimator.Type int animatorType,
@NonNull @Size(min = 2) double[][] values,
@Nullable MapLibreMap.CancelableCallback cancelableCallback) {
cancelAnimator(animatorType);
MapLibreAnimator.AnimationsValueChangeListener listener = listeners.get(animatorType);
if (listener != null) {
animatorArray.put(animatorType, animatorProvider.paddingAnimator(values, listener, cancelableCallback));
}
}

private float checkGpsNorth(boolean isGpsNorth, float targetCameraBearing) {
if (isGpsNorth) {
targetCameraBearing = 0;
Expand Down Expand Up @@ -479,6 +501,10 @@ void cancelZoomAnimation() {
cancelAnimator(ANIMATOR_ZOOM);
}

void cancelPaddingAnimation() {
cancelAnimator(ANIMATOR_PADDING);
}

void cancelTiltAnimation() {
cancelAnimator(ANIMATOR_TILT);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,15 @@ private void setZoom(float zoom) {
onCameraMoveInvalidateListener.onInvalidateCameraMove();
}

private void setPadding(double[] padding) {
if (isTransitioning) {
return;
}

transform.moveCamera(maplibreMap, CameraUpdateFactory.paddingTo(padding), null);
onCameraMoveInvalidateListener.onInvalidateCameraMove();
}

private void setTilt(float tilt) {
if (isTransitioning) {
return;
Expand Down Expand Up @@ -266,20 +275,13 @@ public void onNewAnimationValue(Float value) {
};

private final MapLibreAnimator.AnimationsValueChangeListener<Float> zoomValueListener =
new MapLibreAnimator.AnimationsValueChangeListener<Float>() {
@Override
public void onNewAnimationValue(Float value) {
setZoom(value);
}
};
value -> setZoom(value);

private final MapLibreAnimator.AnimationsValueChangeListener<double[]> paddingValueListener =
value -> setPadding(value);

private final MapLibreAnimator.AnimationsValueChangeListener<Float> tiltValueListener =
new MapLibreAnimator.AnimationsValueChangeListener<Float>() {
@Override
public void onNewAnimationValue(Float value) {
setTilt(value);
}
};
value -> setTilt(value);

Set<AnimatorListenerHolder> getAnimationListeners() {
Set<AnimatorListenerHolder> holders = new HashSet<>();
Expand All @@ -299,6 +301,7 @@ Set<AnimatorListenerHolder> getAnimationListeners() {

holders.add(new AnimatorListenerHolder(MapLibreAnimator.ANIMATOR_ZOOM, zoomValueListener));
holders.add(new AnimatorListenerHolder(MapLibreAnimator.ANIMATOR_TILT, tiltValueListener));
holders.add(new AnimatorListenerHolder(MapLibreAnimator.ANIMATOR_PADDING, paddingValueListener));
return holders;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static org.maplibre.android.location.LocationComponentConstants.DEFAULT_FASTEST_INTERVAL_MILLIS;
import static org.maplibre.android.location.LocationComponentConstants.DEFAULT_INTERVAL_MILLIS;
import static org.maplibre.android.location.LocationComponentConstants.DEFAULT_TRACKING_PADDING_ANIM_DURATION;
import static org.maplibre.android.location.LocationComponentConstants.DEFAULT_TRACKING_TILT_ANIM_DURATION;
import static org.maplibre.android.location.LocationComponentConstants.DEFAULT_TRACKING_ZOOM_ANIM_DURATION;
import static org.maplibre.android.location.LocationComponentConstants.TRANSITION_ANIMATION_DURATION_MS;
Expand Down Expand Up @@ -603,6 +604,84 @@ public void cancelZoomWhileTrackingAnimation() {
locationAnimatorCoordinator.cancelZoomAnimation();
}

/**
* Sets the padding.
* This API can only be used in pair with camera modes other than {@link CameraMode#NONE}.
* If you are not using any of {@link CameraMode} modes,
* use one of {@link MapLibreMap#moveCamera(CameraUpdate)},
* {@link MapLibreMap#easeCamera(CameraUpdate)} or {@link MapLibreMap#animateCamera(CameraUpdate)} instead.
* <p>
* If the camera is transitioning when the padding change is requested, the call is going to be ignored.
* Use {@link CameraTransitionListener} to chain the animations, or provide the padding as a camera change argument.
* </p>
*
* @param padding The desired padding.
*/
public void paddingWhileTracking(double[] padding) {
paddingWhileTracking(padding, DEFAULT_TRACKING_PADDING_ANIM_DURATION, null);
}

/**
* Sets the padding.
* This API can only be used in pair with camera modes other than {@link CameraMode#NONE}.
* If you are not using any of {@link CameraMode} modes,
* use one of {@link MapLibreMap#moveCamera(CameraUpdate)},
* {@link MapLibreMap#easeCamera(CameraUpdate)} or {@link MapLibreMap#animateCamera(CameraUpdate)} instead.
* <p>
* If the camera is transitioning when the padding change is requested, the call is going to be ignored.
* Use {@link CameraTransitionListener} to chain the animations, or provide the padding as a camera change argument.
* </p>
*
* @param padding The desired padding.
* @param animationDuration The padding animation duration.
*/
public void paddingWhileTracking(double[] padding, long animationDuration) {
paddingWhileTracking(padding, animationDuration, null);
}

/**
* Sets the padding.
* This API can only be used in pair with camera modes other than {@link CameraMode#NONE}.
* If you are not using any of {@link CameraMode} modes,
* use one of {@link MapLibreMap#moveCamera(CameraUpdate)},
* {@link MapLibreMap#easeCamera(CameraUpdate)} or {@link MapLibreMap#animateCamera(CameraUpdate)} instead.
* <p>
* If the camera is transitioning when the padding change is requested, the call is going to be ignored.
* Use {@link CameraTransitionListener} to chain the animations, or provide the padding as a camera change argument.
* </p>
*
* @param padding The desired padding.
* @param animationDuration The padding animation duration.
* @param callback The callback with finish/cancel information
*/
public void paddingWhileTracking(double[] padding, long animationDuration,
@Nullable MapLibreMap.CancelableCallback callback) {
checkActivationState();
if (!isLayerReady) {
notifyUnsuccessfulCameraOperation(callback, null);
return;
} else if (getCameraMode() == CameraMode.NONE) {
notifyUnsuccessfulCameraOperation(callback, String.format("%s%s",
"LocationComponent#paddingWhileTracking method can only be used",
" when a camera mode other than CameraMode#NONE is engaged."));
return;
} else if (locationCameraController.isTransitioning()) {
notifyUnsuccessfulCameraOperation(callback,
"LocationComponent#paddingWhileTracking method call is ignored because the camera mode is transitioning");
return;
}

locationAnimatorCoordinator.feedNewPadding(padding, maplibreMap.getCameraPosition(), animationDuration, callback);
}

/**
* Cancels animation started by {@link #paddingWhileTracking(double[], long, MapLibreMap.CancelableCallback)}.
*/
public void cancelPaddingWhileTrackingAnimation() {
checkActivationState();
locationAnimatorCoordinator.cancelPaddingAnimation();
}

/**
* Tilts the camera.
* This API can only be used in pair with camera modes other than {@link CameraMode#NONE}.
Expand Down Expand Up @@ -732,10 +811,10 @@ public void forceLocationUpdate(@Nullable List<Location> locations, boolean look
* Example usage:
* <pre>
* {@code
* mapboxMap.addOnCameraIdleListener(new MapboxMap.OnCameraIdleListener() {
* MapLibreMap.addOnCameraIdleListener(new MapLibreMap.OnCameraIdleListener() {
* {@literal @}Override
* public void onCameraIdle() {
* double zoom = mapboxMap.getCameraPosition().zoom;
* double zoom = MapLibreMap.getCameraPosition().zoom;
* int maxAnimationFps;
* if (zoom < 5) {
* maxAnimationFps = 3;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ public final class LocationComponentConstants {
// Default animation duration for zooming while tracking.
static final long DEFAULT_TRACKING_ZOOM_ANIM_DURATION = 750;

// Default animation duration for updating padding while tracking.
static final long DEFAULT_TRACKING_PADDING_ANIM_DURATION = 750;

// Default animation duration for tilting while tracking.
static final long DEFAULT_TRACKING_TILT_ANIM_DURATION = 1250;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
*
* @param <K> Data type that will be animated.
*/
abstract class MapLibreAnimator<K> extends ValueAnimator implements ValueAnimator.AnimatorUpdateListener {
public abstract class MapLibreAnimator<K> extends ValueAnimator implements ValueAnimator.AnimatorUpdateListener {
@Retention(RetentionPolicy.SOURCE)
@IntDef( {
ANIMATOR_LAYER_LATLNG,
Expand All @@ -29,7 +29,8 @@ abstract class MapLibreAnimator<K> extends ValueAnimator implements ValueAnimato
ANIMATOR_LAYER_ACCURACY,
ANIMATOR_ZOOM,
ANIMATOR_TILT,
ANIMATOR_PULSING_CIRCLE
ANIMATOR_PULSING_CIRCLE,
ANIMATOR_PADDING
})
@interface Type {
}
Expand All @@ -44,6 +45,7 @@ abstract class MapLibreAnimator<K> extends ValueAnimator implements ValueAnimato
static final int ANIMATOR_ZOOM = 7;
static final int ANIMATOR_TILT = 8;
static final int ANIMATOR_PULSING_CIRCLE = 9;
static final int ANIMATOR_PADDING = 10;

private final AnimationsValueChangeListener<K> updateListener;
private final K target;
Expand All @@ -59,7 +61,7 @@ abstract class MapLibreAnimator<K> extends ValueAnimator implements ValueAnimato
*/
private boolean invalid;

MapLibreAnimator(@NonNull @Size(min = 2) K[] values, @NonNull AnimationsValueChangeListener<K> updateListener,
public MapLibreAnimator(@NonNull @Size(min = 2) K[] values, @NonNull AnimationsValueChangeListener<K> updateListener,
int maxAnimationFps) {
minUpdateInterval = 1E9 / maxAnimationFps;
setObjectValues((Object[]) values);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.maplibre.android.location

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import org.maplibre.android.maps.MapLibreMap

internal class MapLibreAnimatorListener(cancelableCallback: MapLibreMap.CancelableCallback?) :
AnimatorListenerAdapter() {
private val cancelableCallback: MapLibreMap.CancelableCallback?

init {
this.cancelableCallback = cancelableCallback
}

override fun onAnimationCancel(animation: Animator) {
cancelableCallback?.onCancel()
}

override fun onAnimationEnd(animation: Animator) {
cancelableCallback?.onFinish()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ MapLibreCameraAnimatorAdapter cameraAnimator(Float[] values,
return new MapLibreCameraAnimatorAdapter(values, updateListener, cancelableCallback);
}

MapLibrePaddingAnimator paddingAnimator(double[][] values,
MapLibreAnimator.AnimationsValueChangeListener<double[]> updateListener,
@Nullable MapLibreMap.CancelableCallback cancelableCallback) {
return new MapLibrePaddingAnimator(values, updateListener, cancelableCallback);
}

/**
* This animator is for the LocationComponent pulsing circle.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,39 +1,17 @@
package org.maplibre.android.location;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Size;

import org.maplibre.android.maps.MapLibreMap;

class MapLibreCameraAnimatorAdapter extends MapLibreFloatAnimator {
@Nullable
private final MapLibreMap.CancelableCallback cancelableCallback;

MapLibreCameraAnimatorAdapter(@NonNull @Size(min = 2) Float[] values,
AnimationsValueChangeListener updateListener,
@Nullable MapLibreMap.CancelableCallback cancelableCallback) {
super(values, updateListener, Integer.MAX_VALUE);
this.cancelableCallback = cancelableCallback;
addListener(new MapLibreAnimatorListener());
}

private final class MapLibreAnimatorListener extends AnimatorListenerAdapter {
@Override
public void onAnimationCancel(Animator animation) {
if (cancelableCallback != null) {
cancelableCallback.onCancel();
}
}

@Override
public void onAnimationEnd(Animator animation) {
if (cancelableCallback != null) {
cancelableCallback.onFinish();
}
}
addListener(new MapLibreAnimatorListener(cancelableCallback));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.maplibre.android.location

import android.animation.TypeEvaluator
import androidx.annotation.Size
import org.maplibre.android.maps.MapLibreMap.CancelableCallback

class MapLibrePaddingAnimator internal constructor(
@Size(min = 2) values: Array<DoubleArray>,
updateListener: AnimationsValueChangeListener<DoubleArray>,
cancelableCallback: CancelableCallback?
) :
MapLibreAnimator<DoubleArray>(values, updateListener, Int.MAX_VALUE) {
init {
addListener(MapLibreAnimatorListener(cancelableCallback))
}

public override fun provideEvaluator(): TypeEvaluator<DoubleArray> {
return PaddingEvaluator()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.maplibre.android.location

import android.animation.TypeEvaluator
import androidx.annotation.Size

internal class PaddingEvaluator : TypeEvaluator<DoubleArray> {
private val padding = DoubleArray(4)
override fun evaluate(
fraction: Float, @Size(min = 4) startValue: DoubleArray,
@Size(min = 4) endValue: DoubleArray
): DoubleArray {
padding[0] = startValue[0] + (endValue[0] - startValue[0]) * fraction
padding[1] = startValue[1] + (endValue[1] - startValue[1]) * fraction
padding[2] = startValue[2] + (endValue[2] - startValue[2]) * fraction
padding[3] = startValue[3] + (endValue[3] - startValue[3]) * fraction
return padding
}
}
Loading

0 comments on commit 69f983b

Please sign in to comment.