From 907612e93d8a2b156d4604fda348707ccb347836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Paczos?= Date: Wed, 5 Sep 2018 14:30:36 +0200 Subject: [android] updated naming scheme and packages structure for LocationLayerPlugin, now called LocationComponent --- .../location/LocationAnimatorCoordinator.java | 375 +++++++++++++++++++++ 1 file changed, 375 insertions(+) create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinator.java (limited to 'platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinator.java') 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 new file mode 100644 index 0000000000..67a9727f5c --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinator.java @@ -0,0 +1,375 @@ +package com.mapbox.mapboxsdk.location; + +import android.animation.Animator; +import android.animation.AnimatorSet; +import android.annotation.SuppressLint; +import android.location.Location; +import android.os.SystemClock; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.view.animation.LinearInterpolator; + +import com.mapbox.mapboxsdk.camera.CameraPosition; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapboxMap; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.ACCURACY_RADIUS_ANIMATION_DURATION; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.COMPASS_UPDATE_RATE_MS; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.MAX_ANIMATION_DURATION_MS; +import static com.mapbox.mapboxsdk.location.LocationComponentConstants.TRANSITION_ANIMATION_DURATION_MS; +import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_CAMERA_COMPASS_BEARING; +import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_CAMERA_GPS_BEARING; +import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_CAMERA_LATLNG; +import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_LAYER_ACCURACY; +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_TILT; +import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_ZOOM; + +final class LocationAnimatorCoordinator { + + @SuppressLint("UseSparseArrays") + final Map animatorMap = new HashMap<>(); + + final List layerListeners = new ArrayList<>(); + final List cameraListeners = new ArrayList<>(); + + private Location previousLocation; + private float previousAccuracyRadius = -1; + private float previousCompassBearing = -1; + private long locationUpdateTimestamp = -1; + + void addLayerListener(MapboxAnimator.OnLayerAnimationsValuesChangeListener listener) { + layerListeners.add(listener); + } + + void removeLayerListener(MapboxAnimator.OnLayerAnimationsValuesChangeListener listener) { + layerListeners.remove(listener); + } + + void addCameraListener(MapboxAnimator.OnCameraAnimationsValuesChangeListener listener) { + cameraListeners.add(listener); + } + + void removeCameraListener(MapboxAnimator.OnCameraAnimationsValuesChangeListener listener) { + cameraListeners.remove(listener); + } + + void feedNewLocation(@NonNull Location newLocation, @NonNull CameraPosition currentCameraPosition, + boolean isGpsNorth) { + if (previousLocation == null) { + previousLocation = newLocation; + locationUpdateTimestamp = SystemClock.elapsedRealtime() - TRANSITION_ANIMATION_DURATION_MS; + } + + LatLng previousLayerLatLng = getPreviousLayerLatLng(); + float previousLayerBearing = getPreviousLayerGpsBearing(); + LatLng previousCameraLatLng = currentCameraPosition.target; + float previousCameraBearing = (float) currentCameraPosition.bearing; + + LatLng targetLatLng = new LatLng(newLocation); + float targetLayerBearing = newLocation.getBearing(); + float targetCameraBearing = newLocation.getBearing(); + targetCameraBearing = checkGpsNorth(isGpsNorth, targetCameraBearing); + + updateLayerAnimators(previousLayerLatLng, targetLatLng, previousLayerBearing, targetLayerBearing); + updateCameraAnimators(previousCameraLatLng, previousCameraBearing, targetLatLng, targetCameraBearing); + + playLocationAnimators(getAnimationDuration()); + + previousLocation = newLocation; + } + + void feedNewCompassBearing(float targetCompassBearing, @NonNull CameraPosition currentCameraPosition) { + if (previousCompassBearing < 0) { + previousCompassBearing = targetCompassBearing; + } + + float previousLayerBearing = getPreviousLayerCompassBearing(); + float previousCameraBearing = (float) currentCameraPosition.bearing; + + updateCompassAnimators(targetCompassBearing, previousLayerBearing, previousCameraBearing); + playCompassAnimators(COMPASS_UPDATE_RATE_MS); + + previousCompassBearing = targetCompassBearing; + } + + void feedNewAccuracyRadius(float targetAccuracyRadius, boolean noAnimation) { + if (previousAccuracyRadius < 0) { + previousAccuracyRadius = targetAccuracyRadius; + } + + float previousAccuracyRadius = getPreviousAccuracyRadius(); + updateAccuracyAnimators(targetAccuracyRadius, previousAccuracyRadius); + playAccuracyAnimator(noAnimation ? 0 : ACCURACY_RADIUS_ANIMATION_DURATION); + + this.previousAccuracyRadius = targetAccuracyRadius; + } + + void feedNewZoomLevel(double targetZoomLevel, @NonNull CameraPosition currentCameraPosition, long animationDuration, + @Nullable MapboxMap.CancelableCallback callback) { + updateZoomAnimator((float) targetZoomLevel, (float) currentCameraPosition.zoom, callback); + playZoomAnimator(animationDuration); + } + + void feedNewTilt(double targetTilt, @NonNull CameraPosition currentCameraPosition, long animationDuration, + @Nullable MapboxMap.CancelableCallback callback) { + updateTiltAnimator((float) targetTilt, (float) currentCameraPosition.tilt, callback); + playTiltAnimator(animationDuration); + } + + private LatLng getPreviousLayerLatLng() { + LatLng previousLatLng; + MapboxAnimator latLngAnimator = animatorMap.get(ANIMATOR_LAYER_LATLNG); + if (latLngAnimator != null) { + previousLatLng = (LatLng) latLngAnimator.getAnimatedValue(); + } else { + previousLatLng = new LatLng(previousLocation); + } + return previousLatLng; + } + + private float getPreviousLayerGpsBearing() { + LayerGpsBearingAnimator animator = (LayerGpsBearingAnimator) animatorMap.get(ANIMATOR_LAYER_GPS_BEARING); + float previousBearing; + if (animator != null) { + previousBearing = (float) animator.getAnimatedValue(); + } else { + previousBearing = previousLocation.getBearing(); + } + return previousBearing; + } + + private float getPreviousLayerCompassBearing() { + LayerCompassBearingAnimator animator = + (LayerCompassBearingAnimator) animatorMap.get(ANIMATOR_LAYER_COMPASS_BEARING); + + float previousBearing; + if (animator != null) { + previousBearing = (float) animator.getAnimatedValue(); + } else { + previousBearing = previousCompassBearing; + } + return previousBearing; + } + + private float getPreviousAccuracyRadius() { + LayerAccuracyAnimator animator = (LayerAccuracyAnimator) animatorMap.get(ANIMATOR_LAYER_ACCURACY); + float previousRadius; + if (animator != null) { + previousRadius = (float) animator.getAnimatedValue(); + } else { + previousRadius = previousAccuracyRadius; + } + return previousRadius; + } + + private void updateLayerAnimators(LatLng previousLatLng, LatLng targetLatLng, + float previousBearing, float targetBearing) { + createNewAnimator(ANIMATOR_LAYER_LATLNG, new LayerLatLngAnimator(previousLatLng, targetLatLng, layerListeners)); + + float normalizedLayerBearing = Utils.shortestRotation(targetBearing, previousBearing); + createNewAnimator(ANIMATOR_LAYER_GPS_BEARING, + new LayerGpsBearingAnimator(previousBearing, normalizedLayerBearing, layerListeners)); + } + + private void updateCameraAnimators(LatLng previousCameraLatLng, float previousCameraBearing, + LatLng targetLatLng, float targetBearing) { + createNewAnimator(ANIMATOR_CAMERA_LATLNG, + new CameraLatLngAnimator(previousCameraLatLng, targetLatLng, cameraListeners)); + + float normalizedCameraBearing = Utils.shortestRotation(targetBearing, previousCameraBearing); + createNewAnimator(ANIMATOR_CAMERA_GPS_BEARING, + new CameraGpsBearingAnimator(previousCameraBearing, normalizedCameraBearing, cameraListeners)); + } + + 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)); + + float normalizedCameraBearing = Utils.shortestRotation(targetCompassBearing, previousCameraBearing); + createNewAnimator(ANIMATOR_CAMERA_COMPASS_BEARING, + new CameraCompassBearingAnimator(previousCameraBearing, normalizedCameraBearing, cameraListeners)); + } + + private void updateAccuracyAnimators(float targetAccuracyRadius, float previousAccuracyRadius) { + createNewAnimator(ANIMATOR_LAYER_ACCURACY, + new LayerAccuracyAnimator(previousAccuracyRadius, targetAccuracyRadius, layerListeners)); + } + + private void updateZoomAnimator(float targetZoomLevel, float previousZoomLevel, + @Nullable MapboxMap.CancelableCallback cancelableCallback) { + createNewAnimator(ANIMATOR_ZOOM, + new ZoomAnimator(previousZoomLevel, targetZoomLevel, cameraListeners, cancelableCallback)); + } + + private void updateTiltAnimator(float targetTilt, float previousTiltLevel, + @Nullable MapboxMap.CancelableCallback cancelableCallback) { + createNewAnimator(ANIMATOR_TILT, + new TiltAnimator(previousTiltLevel, targetTilt, cameraListeners, cancelableCallback)); + } + + private long getAnimationDuration() { + long previousUpdateTimeStamp = locationUpdateTimestamp; + locationUpdateTimestamp = SystemClock.elapsedRealtime(); + + long animationDuration; + if (previousUpdateTimeStamp == 0) { + animationDuration = 0; + } else { + animationDuration = (long) ((locationUpdateTimestamp - previousUpdateTimeStamp) * 1.1f) + /*make animation slightly longer*/; + } + + animationDuration = Math.min(animationDuration, MAX_ANIMATION_DURATION_MS); + + return animationDuration; + } + + private float checkGpsNorth(boolean isGpsNorth, float targetCameraBearing) { + if (isGpsNorth) { + targetCameraBearing = 0; + } + return targetCameraBearing; + } + + private void playLocationAnimators(long duration) { + List locationAnimators = new ArrayList<>(); + locationAnimators.add(animatorMap.get(ANIMATOR_LAYER_LATLNG)); + locationAnimators.add(animatorMap.get(ANIMATOR_LAYER_GPS_BEARING)); + locationAnimators.add(animatorMap.get(ANIMATOR_CAMERA_LATLNG)); + locationAnimators.add(animatorMap.get(ANIMATOR_CAMERA_GPS_BEARING)); + AnimatorSet locationAnimatorSet = new AnimatorSet(); + locationAnimatorSet.playTogether(locationAnimators); + locationAnimatorSet.setInterpolator(new LinearInterpolator()); + locationAnimatorSet.setDuration(duration); + locationAnimatorSet.start(); + } + + private void playCompassAnimators(long duration) { + List compassAnimators = new ArrayList<>(); + compassAnimators.add(animatorMap.get(ANIMATOR_LAYER_COMPASS_BEARING)); + compassAnimators.add(animatorMap.get(ANIMATOR_CAMERA_COMPASS_BEARING)); + AnimatorSet compassAnimatorSet = new AnimatorSet(); + compassAnimatorSet.playTogether(compassAnimators); + compassAnimatorSet.setDuration(duration); + compassAnimatorSet.start(); + } + + private void playAccuracyAnimator(long duration) { + MapboxAnimator animator = animatorMap.get(ANIMATOR_LAYER_ACCURACY); + animator.setDuration(duration); + animator.start(); + } + + private void playZoomAnimator(long duration) { + MapboxAnimator animator = animatorMap.get(ANIMATOR_ZOOM); + animator.setDuration(duration); + animator.start(); + } + + private void playTiltAnimator(long duration) { + MapboxAnimator animator = animatorMap.get(ANIMATOR_TILT); + animator.setDuration(duration); + animator.start(); + } + + private void playCameraLocationAnimators(long duration) { + List locationAnimators = new ArrayList<>(); + locationAnimators.add(animatorMap.get(ANIMATOR_CAMERA_LATLNG)); + locationAnimators.add(animatorMap.get(ANIMATOR_CAMERA_GPS_BEARING)); + AnimatorSet locationAnimatorSet = new AnimatorSet(); + locationAnimatorSet.playTogether(locationAnimators); + locationAnimatorSet.setInterpolator(new LinearInterpolator()); + locationAnimatorSet.setDuration(duration); + locationAnimatorSet.start(); + } + + void resetAllCameraAnimations(CameraPosition currentCameraPosition, boolean isGpsNorth) { + resetCameraCompassAnimation(currentCameraPosition); + resetCameraLocationAnimations(currentCameraPosition, isGpsNorth); + playCameraLocationAnimators(TRANSITION_ANIMATION_DURATION_MS); + } + + private void resetCameraLocationAnimations(CameraPosition currentCameraPosition, boolean isGpsNorth) { + resetCameraLatLngAnimation(currentCameraPosition); + resetCameraGpsBearingAnimation(currentCameraPosition, isGpsNorth); + } + + private void resetCameraLatLngAnimation(CameraPosition currentCameraPosition) { + CameraLatLngAnimator animator = (CameraLatLngAnimator) animatorMap.get(ANIMATOR_CAMERA_LATLNG); + if (animator == null) { + return; + } + + LatLng currentTarget = animator.getTarget(); + LatLng previousCameraTarget = currentCameraPosition.target; + createNewAnimator(ANIMATOR_CAMERA_LATLNG, + new CameraLatLngAnimator(previousCameraTarget, currentTarget, cameraListeners)); + } + + private void resetCameraGpsBearingAnimation(CameraPosition currentCameraPosition, boolean isGpsNorth) { + CameraGpsBearingAnimator animator = (CameraGpsBearingAnimator) animatorMap.get(ANIMATOR_CAMERA_GPS_BEARING); + if (animator == null) { + return; + } + + float currentTargetBearing = animator.getTarget(); + 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)); + } + + private void resetCameraCompassAnimation(CameraPosition currentCameraPosition) { + CameraCompassBearingAnimator animator = + (CameraCompassBearingAnimator) animatorMap.get(ANIMATOR_CAMERA_COMPASS_BEARING); + if (animator == null) { + return; + } + + 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); + animatorMap.put(animatorType, animator); + } + + void cancelZoomAnimation() { + cancelAnimator(ANIMATOR_ZOOM); + } + + void cancelTiltAnimation() { + cancelAnimator(ANIMATOR_TILT); + } + + void cancelAllAnimations() { + for (@MapboxAnimator.Type int animatorType : animatorMap.keySet()) { + cancelAnimator(animatorType); + } + } + + private void cancelAnimator(@MapboxAnimator.Type int animatorType) { + MapboxAnimator animator = animatorMap.get(animatorType); + if (animator != null) { + animator.cancel(); + animator.removeAllUpdateListeners(); + animator.removeAllListeners(); + animatorMap.put(animatorType, null); + } + } +} -- cgit v1.2.1