diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index 544f7c0c8b2..290f9e74990 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -15,6 +15,7 @@ MapLibre welcomes participation and contributions from everyone. Please read [`C * Add support for the [`slice` expression](https://maplibre.org/maplibre-style-spec/expressions/#slice) ([#1133](https://github.com/maplibre/maplibre-native/pull/1133)) * Add support for [index-of expression](https://maplibre.org/maplibre-style-spec/expressions/#index-of) ([#1113](https://github.com/maplibre/maplibre-native/pull/1113)) * Change to a more natural fling animation and allow setting `flingThreshold` and `flingAnimationBaseTime` in `UiSettings` ([#963](https://github.com/maplibre/maplibre-native/pull/963)) +* Add setting padding when camera is tracking ([#2165](https://github.com/maplibre/maplibre-native/pull/2165)). ### 🐞 Bug fixes diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinator.java index 2672d64f41e..d6c78db949f 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinator.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinator.java @@ -33,6 +33,7 @@ import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_LAYER_COMPASS_BEARING; import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_LAYER_GPS_BEARING; import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_LAYER_LATLNG; +import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_PADDING; import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_PULSING_CIRCLE; import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_TILT; import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_ZOOM; @@ -216,6 +217,12 @@ void feedNewZoomLevel(double targetZoomLevel, @NonNull CameraPosition currentCam playAnimators(animationDuration, ANIMATOR_ZOOM); } + void feedNewPadding(double[] padding, @NonNull CameraPosition currentCameraPosition, long animationDuration, + @Nullable MapboxMap.CancelableCallback callback) { + updatePaddingAnimator(padding, currentCameraPosition.padding, callback); + playAnimators(animationDuration, ANIMATOR_PADDING); + } + void feedNewTilt(double targetTilt, @NonNull CameraPosition currentCameraPosition, long animationDuration, @Nullable MapboxMap.CancelableCallback callback) { updateTiltAnimator((float) targetTilt, (float) currentCameraPosition.tilt, callback); @@ -316,6 +323,11 @@ private void updateZoomAnimator(float targetZoomLevel, float previousZoomLevel, createNewCameraAdapterAnimator(ANIMATOR_ZOOM, new Float[] {previousZoomLevel, targetZoomLevel}, cancelableCallback); } + private void updatePaddingAnimator(double[] targetPadding, double[] previousPadding, + @Nullable MapboxMap.CancelableCallback cancelableCallback) { + createNewPaddingAnimator(ANIMATOR_PADDING, new double[][] {previousPadding, targetPadding}, cancelableCallback); + } + private void updateTiltAnimator(float targetTilt, float previousTiltLevel, @Nullable MapboxMap.CancelableCallback cancelableCallback) { createNewCameraAdapterAnimator(ANIMATOR_TILT, new Float[] {previousTiltLevel, targetTilt}, cancelableCallback); @@ -355,6 +367,16 @@ private void createNewCameraAdapterAnimator(@MapboxAnimator.Type int animatorTyp } } + private void createNewPaddingAnimator(@MapboxAnimator.Type int animatorType, + @NonNull @Size(min = 2) double[][] values, + @Nullable MapboxMap.CancelableCallback cancelableCallback) { + cancelAnimator(animatorType); + MapboxAnimator.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; @@ -478,6 +500,10 @@ void cancelZoomAnimation() { cancelAnimator(ANIMATOR_ZOOM); } + void cancelPaddingAnimation() { + cancelAnimator(ANIMATOR_PADDING); + } + void cancelTiltAnimation() { cancelAnimator(ANIMATOR_TILT); } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationCameraController.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationCameraController.java index 2a167b7e78a..88d6a1b2554 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationCameraController.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationCameraController.java @@ -47,12 +47,12 @@ final class LocationCameraController { private boolean isEnabled; LocationCameraController( - Context context, - MapboxMap mapboxMap, - Transform transform, - OnCameraTrackingChangedListener internalCameraTrackingChangedListener, - @NonNull LocationComponentOptions options, - OnCameraMoveInvalidateListener onCameraMoveInvalidateListener) { + Context context, + MapboxMap mapboxMap, + Transform transform, + OnCameraTrackingChangedListener internalCameraTrackingChangedListener, + @NonNull LocationComponentOptions options, + OnCameraMoveInvalidateListener onCameraMoveInvalidateListener) { this.mapboxMap = mapboxMap; this.transform = transform; @@ -123,7 +123,7 @@ void setCameraMode(@CameraMode.Mode final int cameraMode, @Nullable Location las adjustGesturesThresholds(); notifyCameraTrackingChangeListener(wasTracking); transitionToCurrentLocation( - wasTracking, lastLocation, transitionDuration, zoom, bearing, tilt, internalTransitionListener); + wasTracking, lastLocation, transitionDuration, zoom, bearing, tilt, internalTransitionListener); } /** @@ -175,15 +175,15 @@ public void onFinish() { CameraPosition currentPosition = mapboxMap.getCameraPosition(); if (Utils.immediateAnimation(mapboxMap.getProjection(), currentPosition.target, target)) { transform.moveCamera( - mapboxMap, - update, - callback); + mapboxMap, + update, + callback); } else { transform.animateCamera( - mapboxMap, - update, - (int) transitionDuration, - callback); + mapboxMap, + update, + (int) transitionDuration, + callback); } } else { if (internalTransitionListener != null) { @@ -223,6 +223,15 @@ private void setZoom(float zoom) { onCameraMoveInvalidateListener.onInvalidateCameraMove(); } + private void setPadding(double[] padding) { + if (isTransitioning) { + return; + } + + transform.moveCamera(mapboxMap, CameraUpdateFactory.paddingTo(padding), null); + onCameraMoveInvalidateListener.onInvalidateCameraMove(); + } + private void setTilt(float tilt) { if (isTransitioning) { return; @@ -233,19 +242,14 @@ private void setTilt(float tilt) { } private final MapboxAnimator.AnimationsValueChangeListener latLngValueListener = - new MapboxAnimator.AnimationsValueChangeListener() { - @Override - public void onNewAnimationValue(LatLng value) { - setLatLng(value); - } - }; + value -> setLatLng(value); private final MapboxAnimator.AnimationsValueChangeListener gpsBearingValueListener = new MapboxAnimator.AnimationsValueChangeListener() { @Override public void onNewAnimationValue(Float value) { boolean trackingNorth = cameraMode == CameraMode.TRACKING_GPS_NORTH - && mapboxMap.getCameraPosition().bearing == 0; + && mapboxMap.getCameraPosition().bearing == 0; if (!trackingNorth) { setBearing(value); @@ -258,27 +262,20 @@ public void onNewAnimationValue(Float value) { @Override public void onNewAnimationValue(Float value) { if (cameraMode == CameraMode.TRACKING_COMPASS - || cameraMode == CameraMode.NONE_COMPASS) { + || cameraMode == CameraMode.NONE_COMPASS) { setBearing(value); } } }; private final MapboxAnimator.AnimationsValueChangeListener zoomValueListener = - new MapboxAnimator.AnimationsValueChangeListener() { - @Override - public void onNewAnimationValue(Float value) { - setZoom(value); - } - }; + value -> setZoom(value); + + private final MapboxAnimator.AnimationsValueChangeListener paddingValueListener = + value -> setPadding(value); private final MapboxAnimator.AnimationsValueChangeListener tiltValueListener = - new MapboxAnimator.AnimationsValueChangeListener() { - @Override - public void onNewAnimationValue(Float value) { - setTilt(value); - } - }; + value -> setTilt(value); Set getAnimationListeners() { Set holders = new HashSet<>(); @@ -292,12 +289,13 @@ Set getAnimationListeners() { if (isConsumingCompass()) { holders.add(new AnimatorListenerHolder( - MapboxAnimator.ANIMATOR_CAMERA_COMPASS_BEARING, - compassBearingValueListener)); + MapboxAnimator.ANIMATOR_CAMERA_COMPASS_BEARING, + compassBearingValueListener)); } holders.add(new AnimatorListenerHolder(MapboxAnimator.ANIMATOR_ZOOM, zoomValueListener)); holders.add(new AnimatorListenerHolder(MapboxAnimator.ANIMATOR_TILT, tiltValueListener)); + holders.add(new AnimatorListenerHolder(MapboxAnimator.ANIMATOR_PADDING, paddingValueListener)); return holders; } @@ -318,7 +316,7 @@ private void adjustGesturesThresholds() { boolean isConsumingCompass() { return cameraMode == CameraMode.TRACKING_COMPASS - || cameraMode == CameraMode.NONE_COMPASS; + || cameraMode == CameraMode.NONE_COMPASS; } void setEnabled(boolean enabled) { @@ -327,23 +325,23 @@ void setEnabled(boolean enabled) { private boolean isLocationTracking() { return cameraMode == CameraMode.TRACKING - || cameraMode == CameraMode.TRACKING_COMPASS - || cameraMode == CameraMode.TRACKING_GPS - || cameraMode == CameraMode.TRACKING_GPS_NORTH; + || cameraMode == CameraMode.TRACKING_COMPASS + || cameraMode == CameraMode.TRACKING_GPS + || cameraMode == CameraMode.TRACKING_GPS_NORTH; } private boolean isBearingTracking() { return cameraMode == CameraMode.NONE_COMPASS - || cameraMode == CameraMode.TRACKING_COMPASS - || cameraMode == CameraMode.NONE_GPS - || cameraMode == CameraMode.TRACKING_GPS - || cameraMode == CameraMode.TRACKING_GPS_NORTH; + || cameraMode == CameraMode.TRACKING_COMPASS + || cameraMode == CameraMode.NONE_GPS + || cameraMode == CameraMode.TRACKING_GPS + || cameraMode == CameraMode.TRACKING_GPS_NORTH; } private boolean isLocationBearingTracking() { return cameraMode == CameraMode.TRACKING_GPS - || cameraMode == CameraMode.TRACKING_GPS_NORTH - || cameraMode == CameraMode.NONE_GPS; + || cameraMode == CameraMode.TRACKING_GPS_NORTH + || cameraMode == CameraMode.NONE_GPS; } private void notifyCameraTrackingChangeListener(boolean wasTracking) { diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java index cc654df57d9..aad0d47846a 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java @@ -45,6 +45,7 @@ import static android.Manifest.permission.ACCESS_FINE_LOCATION; import static com.mapbox.mapboxsdk.location.LocationComponentConstants.DEFAULT_FASTEST_INTERVAL_MILLIS; import static com.mapbox.mapboxsdk.location.LocationComponentConstants.DEFAULT_INTERVAL_MILLIS; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.DEFAULT_TRACKING_PADDING_ANIM_DURATION; import static com.mapbox.mapboxsdk.location.LocationComponentConstants.DEFAULT_TRACKING_TILT_ANIM_DURATION; import static com.mapbox.mapboxsdk.location.LocationComponentConstants.DEFAULT_TRACKING_ZOOM_ANIM_DURATION; import static com.mapbox.mapboxsdk.location.LocationComponentConstants.TRANSITION_ANIMATION_DURATION_MS; @@ -598,6 +599,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 MapboxMap#moveCamera(CameraUpdate)}, + * {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead. + *

+ * 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. + *

+ * + * @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 MapboxMap#moveCamera(CameraUpdate)}, + * {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead. + *

+ * 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. + *

+ * + * @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 MapboxMap#moveCamera(CameraUpdate)}, + * {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead. + *

+ * 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. + *

+ * + * @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 MapboxMap.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, mapboxMap.getCameraPosition(), animationDuration, callback); + } + + /** + * Cancels animation started by {@link #paddingWhileTracking(double[], long, MapboxMap.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}. diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentConstants.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentConstants.java index 13dd283f88a..a03557bf7db 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentConstants.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentConstants.java @@ -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; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimator.java index 82853ef8c3c..6e99dffdc74 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimator.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimator.java @@ -29,7 +29,8 @@ abstract class MapboxAnimator extends ValueAnimator implements ValueAnimator. ANIMATOR_LAYER_ACCURACY, ANIMATOR_ZOOM, ANIMATOR_TILT, - ANIMATOR_PULSING_CIRCLE + ANIMATOR_PULSING_CIRCLE, + ANIMATOR_PADDING }) @interface Type { } @@ -44,6 +45,8 @@ abstract class MapboxAnimator extends ValueAnimator implements ValueAnimator. 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 updateListener; private final K target; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimatorListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimatorListener.java new file mode 100644 index 00000000000..5b42e38d7d5 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimatorListener.java @@ -0,0 +1,32 @@ +package com.mapbox.mapboxsdk.location; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; + +import androidx.annotation.Nullable; + +import com.mapbox.mapboxsdk.maps.MapboxMap; + +class MapboxAnimatorListener extends AnimatorListenerAdapter { + + @Nullable + private final MapboxMap.CancelableCallback cancelableCallback; + + MapboxAnimatorListener(@Nullable MapboxMap.CancelableCallback cancelableCallback) { + this.cancelableCallback = cancelableCallback; + } + + @Override + public void onAnimationCancel(Animator animation) { + if (cancelableCallback != null) { + cancelableCallback.onCancel(); + } + } + + @Override + public void onAnimationEnd(Animator animation) { + if (cancelableCallback != null) { + cancelableCallback.onFinish(); + } + } +} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimatorProvider.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimatorProvider.java index 16324f18a49..01ec9fc0610 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimatorProvider.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimatorProvider.java @@ -39,6 +39,12 @@ MapboxCameraAnimatorAdapter cameraAnimator(Float[] values, return new MapboxCameraAnimatorAdapter(values, updateListener, cancelableCallback); } + MapboxPaddingAnimator paddingAnimator(double[][] values, + MapboxAnimator.AnimationsValueChangeListener updateListener, + @Nullable MapboxMap.CancelableCallback cancelableCallback) { + return new MapboxPaddingAnimator(values, updateListener, cancelableCallback); + } + /** * This animator is for the LocationComponent pulsing circle. * diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxCameraAnimatorAdapter.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxCameraAnimatorAdapter.java index a3b9fff42c3..564fe431984 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxCameraAnimatorAdapter.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxCameraAnimatorAdapter.java @@ -1,8 +1,5 @@ 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; @@ -10,30 +7,10 @@ import com.mapbox.mapboxsdk.maps.MapboxMap; class MapboxCameraAnimatorAdapter extends MapboxFloatAnimator { - @Nullable - private final MapboxMap.CancelableCallback cancelableCallback; - MapboxCameraAnimatorAdapter(@NonNull @Size(min = 2) Float[] values, AnimationsValueChangeListener updateListener, @Nullable MapboxMap.CancelableCallback cancelableCallback) { super(values, updateListener, Integer.MAX_VALUE); - this.cancelableCallback = cancelableCallback; - addListener(new MapboxAnimatorListener()); - } - - private final class MapboxAnimatorListener 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 MapboxAnimatorListener(cancelableCallback)); } -} +} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxPaddingAnimator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxPaddingAnimator.java new file mode 100644 index 00000000000..4d73c62f34e --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxPaddingAnimator.java @@ -0,0 +1,25 @@ +package com.mapbox.mapboxsdk.location; + +import android.animation.TypeEvaluator; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.Size; + +import com.mapbox.mapboxsdk.maps.MapboxMap; + +public class MapboxPaddingAnimator extends MapboxAnimator { + + MapboxPaddingAnimator(@NonNull @Size(min = 2) double[][] values, + @NonNull AnimationsValueChangeListener updateListener, + @Nullable MapboxMap.CancelableCallback cancelableCallback) { + super(values, updateListener, Integer.MAX_VALUE); + addListener(new MapboxAnimatorListener(cancelableCallback)); + } + + @NonNull + @Override + TypeEvaluator provideEvaluator() { + return new PaddingEvaluator(); + } +} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/PaddingEvaluator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/PaddingEvaluator.java new file mode 100644 index 00000000000..5bb3751264d --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/PaddingEvaluator.java @@ -0,0 +1,21 @@ +package com.mapbox.mapboxsdk.location; + +import android.animation.TypeEvaluator; + +import androidx.annotation.NonNull; +import androidx.annotation.Size; + +class PaddingEvaluator implements TypeEvaluator { + private final double[] padding = new double[4]; + + @NonNull + @Override + public double[] evaluate(float fraction, @NonNull @Size(min = 4) double[] startValue, + @NonNull @Size(min = 4) double[] endValue) { + 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; + } +} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinatorTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinatorTest.kt index d6bd72ebfc1..c7b91fa1c25 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinatorTest.kt +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinatorTest.kt @@ -7,6 +7,7 @@ import android.util.SparseArray import android.view.animation.LinearInterpolator import com.mapbox.mapboxsdk.camera.CameraPosition import com.mapbox.mapboxsdk.geometry.LatLng +import com.mapbox.mapboxsdk.location.LocationComponentConstants.DEFAULT_TRACKING_PADDING_ANIM_DURATION import com.mapbox.mapboxsdk.location.LocationComponentConstants.DEFAULT_TRACKING_TILT_ANIM_DURATION import com.mapbox.mapboxsdk.location.LocationComponentConstants.DEFAULT_TRACKING_ZOOM_ANIM_DURATION import com.mapbox.mapboxsdk.location.MapboxAnimator.* @@ -58,7 +59,8 @@ class LocationAnimatorCoordinatorTest { ANIMATOR_CAMERA_COMPASS_BEARING, ANIMATOR_LAYER_ACCURACY, ANIMATOR_ZOOM, - ANIMATOR_TILT + ANIMATOR_TILT, + ANIMATOR_PADDING ) ) } @@ -94,6 +96,20 @@ class LocationAnimatorCoordinatorTest { } answers { MapboxCameraAnimatorAdapter(floatsSlot.captured, listenerSlot.captured, null) } + + + val doubleArraySlot = slot>() + val doubleArrayListenerSlot = slot>() + every { + animatorProvider.paddingAnimator(capture(doubleArraySlot), capture(doubleArrayListenerSlot), capture(callback)) + } answers { + MapboxPaddingAnimator(doubleArraySlot.captured, doubleArrayListenerSlot.captured, callback.captured) + } + every { + animatorProvider.paddingAnimator(capture(doubleArraySlot), capture(doubleArrayListenerSlot), null) + } answers { + MapboxPaddingAnimator(doubleArraySlot.captured, doubleArrayListenerSlot.captured, null) + } } @Test @@ -488,6 +504,33 @@ class LocationAnimatorCoordinatorTest { verify { animatorSetProvider.startAnimation(eq(listOf(animator)), any(), DEFAULT_TRACKING_ZOOM_ANIM_DURATION) } } + @Test + fun feedNewPadding_animatorsCreated() { + locationAnimatorCoordinator.feedNewPadding( + doubleArrayOf(100.0, 200.0, 300.0, 400.0), + cameraPosition, + DEFAULT_TRACKING_PADDING_ANIM_DURATION, + null + ) + + assertTrue(locationAnimatorCoordinator.animatorArray[ANIMATOR_PADDING] != null) + } + + @Test + fun feedNewPadding_animatorValue() { + val padding = doubleArrayOf(100.0, 200.0, 300.0, 400.0) + locationAnimatorCoordinator.feedNewPadding( + padding, + cameraPosition, + DEFAULT_TRACKING_PADDING_ANIM_DURATION, + null + ) + + val animator = locationAnimatorCoordinator.animatorArray[ANIMATOR_PADDING] + assertTrue(padding.contentEquals(animator.target as DoubleArray)) + verify { animatorSetProvider.startAnimation(eq(listOf(animator)), any(), DEFAULT_TRACKING_PADDING_ANIM_DURATION) } + } + @Test fun feedNewTiltLevel_animatorsCreated() { locationAnimatorCoordinator.feedNewTilt( @@ -540,6 +583,21 @@ class LocationAnimatorCoordinatorTest { assertFalse(locationAnimatorCoordinator.animatorArray[ANIMATOR_ZOOM].isStarted) } + @Test + fun cancelPaddingAnimators() { + locationAnimatorCoordinator.feedNewPadding( + doubleArrayOf(100.0, 200.0, 300.0, 400.0), + cameraPosition, + DEFAULT_TRACKING_PADDING_ANIM_DURATION, + null + ) + assertTrue(locationAnimatorCoordinator.animatorArray[ANIMATOR_PADDING].isStarted) + + locationAnimatorCoordinator.cancelPaddingAnimation() + + assertFalse(locationAnimatorCoordinator.animatorArray[ANIMATOR_PADDING].isStarted) + } + @Test fun cancelTiltAnimation() { locationAnimatorCoordinator.feedNewTilt( diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentTest.kt index 29eb1814a44..3ebe3bbc420 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentTest.kt +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentTest.kt @@ -639,4 +639,5 @@ class LocationComponentTest { verify(locationAnimatorCoordinator).feedNewAccuracyRadius(location.accuracy, false) } + } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/LocationComponentTest.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/LocationComponentTest.kt index c426d0ed7a1..eb823b63b87 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/LocationComponentTest.kt +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/LocationComponentTest.kt @@ -31,6 +31,7 @@ import com.mapbox.mapboxsdk.utils.BitmapUtils import com.mapbox.mapboxsdk.utils.ColorUtils import org.hamcrest.CoreMatchers.* import org.junit.* +import org.junit.Assert.assertArrayEquals import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.runner.RunWith @@ -1608,6 +1609,122 @@ class LocationComponentTest : EspressoTest() { executeComponentTest(componentAction) } + @Test + fun animators_dontPaddingWhileNotTracking() { + val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction { + override fun onLocationComponentAction( + component: LocationComponent, + mapboxMap: MapboxMap, + style: Style, + uiController: UiController, + context: Context + ) { + component.activateLocationComponent(LocationComponentActivationOptions + .builder(context, style) + .useDefaultLocationEngine(false) + .build()) + component.isLocationComponentEnabled = true + component.cameraMode = CameraMode.NONE + val padding = mapboxMap.cameraPosition.padding + component.paddingWhileTracking(doubleArrayOf(100.0, 200.0, 300.0, 400.0)) + uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_PADDING_ANIM_DURATION) + TestingAsyncUtils.waitForLayer(uiController, mapView) + + assertArrayEquals(padding, mapboxMap.cameraPosition.padding, 0.1) + } + } + + executeComponentTest(componentAction) + } + + @Test + fun animators_dontPaddingWhileStopped() { + val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction { + override fun onLocationComponentAction( + component: LocationComponent, + mapboxMap: MapboxMap, + style: Style, + uiController: UiController, + context: Context + ) { + component.activateLocationComponent(LocationComponentActivationOptions + .builder(context, style) + .useDefaultLocationEngine(false) + .build()) + component.isLocationComponentEnabled = true + component.cameraMode = CameraMode.TRACKING + val padding = mapboxMap.cameraPosition.padding + + component.onStop() + component.paddingWhileTracking(doubleArrayOf(100.0, 200.0, 300.0, 400.0)) + uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_PADDING_ANIM_DURATION) + TestingAsyncUtils.waitForLayer(uiController, mapView) + + assertArrayEquals(padding, mapboxMap.cameraPosition.padding, 0.1) + } + } + + executeComponentTest(componentAction) + } + + @Test + fun animators_dontPaddingWhileTransitioning() { + val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction { + override fun onLocationComponentAction( + component: LocationComponent, + mapboxMap: MapboxMap, + style: Style, + uiController: UiController, + context: Context + ) { + component.activateLocationComponent(LocationComponentActivationOptions + .builder(context, style) + .useDefaultLocationEngine(false) + .build()) + component.isLocationComponentEnabled = true + component.forceLocationUpdate(location) + + val padding = mapboxMap.cameraPosition.padding + component.setCameraMode(CameraMode.TRACKING_GPS, 500L, null, null, null, null) + component.paddingWhileTracking(doubleArrayOf(100.0, 200.0, 300.0, 400.0), 1000L) + uiController.loopMainThreadForAtLeast(1000L) + TestingAsyncUtils.waitForLayer(uiController, mapView) + + assertArrayEquals(padding, mapboxMap.cameraPosition.padding, 0.1) + } + } + + executeComponentTest(componentAction) + } + + @Test + fun animators_paddingWhileTracking() { + val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction { + override fun onLocationComponentAction( + component: LocationComponent, + mapboxMap: MapboxMap, + style: Style, + uiController: UiController, + context: Context + ) { + component.activateLocationComponent(LocationComponentActivationOptions + .builder(context, style) + .useDefaultLocationEngine(false) + .build()) + component.isLocationComponentEnabled = true + component.cameraMode = CameraMode.TRACKING + val padding = doubleArrayOf(100.0, 200.0, 300.0, 400.0) + component.paddingWhileTracking(padding) + uiController.loopMainThreadForAtLeast(DEFAULT_TRACKING_PADDING_ANIM_DURATION) + TestingAsyncUtils.waitForLayer(uiController, mapView) + + assertTrue(padding.contentEquals(mapboxMap.cameraPosition.padding)) + } + } + + executeComponentTest(componentAction) + } + @Test fun cameraPositionAdjustedToTrackingModeWhenComponentEnabled() { val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction { diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationModesActivity.kt b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationModesActivity.kt index 65000818390..39bdfb73328 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationModesActivity.kt +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationModesActivity.kt @@ -31,6 +31,7 @@ import com.mapbox.mapboxsdk.maps.MapboxMap.CancelableCallback import com.mapbox.mapboxsdk.maps.OnMapReadyCallback import com.mapbox.mapboxsdk.maps.Style import com.mapbox.mapboxsdk.testapp.R +import java.util.Random class LocationModesActivity : AppCompatActivity(), @@ -193,6 +194,27 @@ class LocationModesActivity : Toast.makeText(this, "Not possible to animate - not tracking", Toast.LENGTH_SHORT) .show() } + } else if (id == R.id.action_component_padding_animation_while_tracking) { + val paddingRandom = Random() + locationComponent!!.paddingWhileTracking( + doubleArrayOf( + paddingRandom.nextDouble() * 500, + paddingRandom.nextDouble() * 500, + paddingRandom.nextDouble() * 500, + paddingRandom.nextDouble() * 500 + ), 1000L, object : CancelableCallback { + override fun onCancel() { + // No impl + } + + override fun onFinish() { + locationComponent!!.zoomWhileTracking(16.0) + } + }) + if (locationComponent!!.getCameraMode() == CameraMode.NONE) { + Toast.makeText(this, "Not possible to animate - not tracking", Toast.LENGTH_SHORT) + .show() + } } return super.onOptionsItemSelected(item) } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_location_mode.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_location_mode.xml index 535679a1c4a..97d7b78455a 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_location_mode.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_location_mode.xml @@ -36,4 +36,8 @@ + + \ No newline at end of file diff --git a/platform/android/gradle/publish-root.gradle b/platform/android/gradle/publish-root.gradle index 467ecfa1153..1d1fbdeae71 100644 --- a/platform/android/gradle/publish-root.gradle +++ b/platform/android/gradle/publish-root.gradle @@ -5,17 +5,9 @@ ext["ossrhUsername"] = '' ext["ossrhPassword"] = '' ext["sonatypeStagingProfileId"] = '' -File secretPropsFile = project.rootProject.file('local.properties') -if (secretPropsFile.exists()) { - // Read local.properties file first if it exists - Properties p = new Properties() - new FileInputStream(secretPropsFile).withCloseable { is -> p.load(is) } - p.each { name, value -> ext[name] = value } -} else { - // Use system environment variables - ext["ossrhUsername"] = System.getenv('OSSRH_USERNAME') - ext["ossrhPassword"] = System.getenv('OSSRH_PASSWORD') - ext["sonatypeStagingProfileId"] = System.getenv('SONATYPE_STAGING_PROFILE_ID') - ext["signing.keyId"] = System.getenv('SIGNING_KEY_ID') - ext["signing.password"] = System.getenv('SIGNING_PASSWORD') -} +// Use system environment variables +ext["ossrhUsername"] = System.getenv('OSSRH_USERNAME') +ext["ossrhPassword"] = System.getenv('OSSRH_PASSWORD') +ext["sonatypeStagingProfileId"] = System.getenv('SONATYPE_STAGING_PROFILE_ID') +ext["signing.keyId"] = System.getenv('SIGNING_KEY_ID') +ext["signing.password"] = System.getenv('SIGNING_PASSWORD')