diff options
Diffstat (limited to 'platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java')
-rw-r--r-- | platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java | 512 |
1 files changed, 387 insertions, 125 deletions
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java index 33e13c5ecc..0fea5ce0ff 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java @@ -1,15 +1,19 @@ package com.mapbox.mapboxsdk.maps; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; import android.content.Context; import android.graphics.PointF; import android.location.Location; -import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.view.GestureDetectorCompat; import android.support.v4.view.ScaleGestureDetectorCompat; +import android.support.v4.view.animation.FastOutSlowInInterpolator; import android.view.InputDevice; import android.view.MotionEvent; import android.view.ScaleGestureDetector; +import android.view.VelocityTracker; import android.view.ViewConfiguration; import com.almeros.android.multitouch.gesturedetectors.RotateGestureDetector; @@ -22,6 +26,8 @@ import com.mapbox.services.android.telemetry.MapboxTelemetry; import com.mapbox.services.android.telemetry.utils.MathUtils; import com.mapbox.services.android.telemetry.utils.TelemetryUtils; +import java.util.concurrent.CopyOnWriteArrayList; + import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE; /** @@ -39,24 +45,46 @@ final class MapGestureDetector { private final AnnotationManager annotationManager; private final CameraChangeDispatcher cameraChangeDispatcher; - private final GestureDetectorCompat gestureDetector; - private final ScaleGestureDetector scaleGestureDetector; - private final RotateGestureDetector rotateGestureDetector; - private final ShoveGestureDetector shoveGestureDetector; + private GestureDetectorCompat gestureDetector; + private ScaleGestureDetector scaleGestureDetector; + private RotateGestureDetector rotateGestureDetector; + private ShoveGestureDetector shoveGestureDetector; + // deprecated map touch API private MapboxMap.OnMapClickListener onMapClickListener; private MapboxMap.OnMapLongClickListener onMapLongClickListener; private MapboxMap.OnFlingListener onFlingListener; private MapboxMap.OnScrollListener onScrollListener; + // new map touch API + private final CopyOnWriteArrayList<MapboxMap.OnMapClickListener> onMapClickListenerList + = new CopyOnWriteArrayList<>(); + + private final CopyOnWriteArrayList<MapboxMap.OnMapLongClickListener> onMapLongClickListenerList + = new CopyOnWriteArrayList<>(); + + private final CopyOnWriteArrayList<MapboxMap.OnFlingListener> onFlingListenerList + = new CopyOnWriteArrayList<>(); + + private final CopyOnWriteArrayList<MapboxMap.OnScrollListener> onScrollListenerList + = new CopyOnWriteArrayList<>(); + private PointF focalPoint; - private boolean twoTap = false; - private boolean zoomStarted = false; - private boolean dragStarted = false; - private boolean quickZoom = false; - private boolean scrollInProgress = false; - private boolean scaleGestureOccurred = false; + private boolean twoTap; + private boolean quickZoom; + private boolean tiltGestureOccurred; + private boolean scrollGestureOccurred; + + private boolean scaleGestureOccurred; + private boolean recentScaleGestureOccurred; + private boolean scaleAnimating; + private long scaleBeginTime; + + private VelocityTracker velocityTracker; + private boolean wasZoomingIn; + private boolean wasClockwiseRotating; + private boolean rotateGestureOccurred; MapGestureDetector(Context context, Transform transform, Projection projection, UiSettings uiSettings, TrackingSettings trackingSettings, AnnotationManager annotationManager, @@ -69,12 +97,14 @@ final class MapGestureDetector { this.cameraChangeDispatcher = cameraChangeDispatcher; // Touch gesture detectors - gestureDetector = new GestureDetectorCompat(context, new GestureListener()); - gestureDetector.setIsLongpressEnabled(true); - scaleGestureDetector = new ScaleGestureDetector(context, new ScaleGestureListener()); - ScaleGestureDetectorCompat.setQuickScaleEnabled(scaleGestureDetector, true); - rotateGestureDetector = new RotateGestureDetector(context, new RotateGestureListener()); - shoveGestureDetector = new ShoveGestureDetector(context, new ShoveGestureListener()); + if (context != null) { + gestureDetector = new GestureDetectorCompat(context, new GestureListener()); + gestureDetector.setIsLongpressEnabled(true); + scaleGestureDetector = new ScaleGestureDetector(context, new ScaleGestureListener()); + ScaleGestureDetectorCompat.setQuickScaleEnabled(scaleGestureDetector, true); + rotateGestureDetector = new RotateGestureDetector(context, new RotateGestureListener()); + shoveGestureDetector = new ShoveGestureDetector(context, new ShoveGestureListener()); + } } /** @@ -133,22 +163,33 @@ final class MapGestureDetector { * @param event the MotionEvent * @return True if touch event is handled */ - boolean onTouchEvent(@NonNull MotionEvent event) { + boolean onTouchEvent(MotionEvent event) { + // framework can return null motion events in edge cases #9432 + if (event == null) { + return false; + } + // Check and ignore non touch or left clicks if ((event.getButtonState() != 0) && (event.getButtonState() != MotionEvent.BUTTON_PRIMARY)) { return false; } // Check two finger gestures first - rotateGestureDetector.onTouchEvent(event); scaleGestureDetector.onTouchEvent(event); + rotateGestureDetector.onTouchEvent(event); shoveGestureDetector.onTouchEvent(event); // Handle two finger tap switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: + if (velocityTracker == null) { + velocityTracker = VelocityTracker.obtain(); + } else { + velocityTracker.clear(); + } + velocityTracker.addMovement(event); // First pointer down, reset scaleGestureOccurred, used to avoid triggering a fling after a scale gesture #7666 - scaleGestureOccurred = false; + recentScaleGestureOccurred = false; transform.setGestureInProgress(true); break; @@ -188,20 +229,34 @@ final class MapGestureDetector { } // Scroll / Pan Has Stopped - if (scrollInProgress) { + if (scrollGestureOccurred) { MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapDragEndEvent( getLocationFromGesture(event.getX(), event.getY()), transform)); - scrollInProgress = false; + scrollGestureOccurred = false; cameraChangeDispatcher.onCameraIdle(); } twoTap = false; transform.setGestureInProgress(false); + if (velocityTracker != null) { + velocityTracker.recycle(); + } + velocityTracker = null; break; case MotionEvent.ACTION_CANCEL: twoTap = false; transform.setGestureInProgress(false); + if (velocityTracker != null) { + velocityTracker.recycle(); + } + velocityTracker = null; + break; + case MotionEvent.ACTION_MOVE: + if (velocityTracker != null) { + velocityTracker.addMovement(event); + velocityTracker.computeCurrentVelocity(1000); + } break; } @@ -250,7 +305,6 @@ final class MapGestureDetector { return false; } - /** * Responsible for handling one finger gestures. */ @@ -274,7 +328,7 @@ final class MapGestureDetector { break; case MotionEvent.ACTION_UP: if (quickZoom) { - // insert here? + cameraChangeDispatcher.onCameraIdle(); quickZoom = false; break; } @@ -310,7 +364,7 @@ final class MapGestureDetector { @Override public boolean onSingleTapConfirmed(MotionEvent motionEvent) { PointF tapPoint = new PointF(motionEvent.getX(), motionEvent.getY()); - boolean tapHandled = annotationManager.onTap(tapPoint, uiSettings.getPixelRatio()); + boolean tapHandled = annotationManager.onTap(tapPoint); if (!tapHandled) { if (uiSettings.isDeselectMarkersOnTap()) { @@ -318,10 +372,7 @@ final class MapGestureDetector { annotationManager.deselectMarkers(); } - // notify app of map click - if (onMapClickListener != null) { - onMapClickListener.onMapClick(projection.fromScreenLocation(tapPoint)); - } + notifyOnMapClickListeners(tapPoint); } MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( @@ -333,20 +384,20 @@ final class MapGestureDetector { @Override public void onLongPress(MotionEvent motionEvent) { - if (onMapLongClickListener != null && !quickZoom) { - onMapLongClickListener.onMapLongClick( - projection.fromScreenLocation(new PointF(motionEvent.getX(), motionEvent.getY()))); + PointF longClickPoint = new PointF(motionEvent.getX(), motionEvent.getY()); + + if (!quickZoom) { + notifyOnMapLongClickListeners(longClickPoint); } } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - if ((!trackingSettings.isScrollGestureCurrentlyEnabled()) || scaleGestureOccurred) { + if ((!trackingSettings.isScrollGestureCurrentlyEnabled()) || recentScaleGestureOccurred) { // don't allow a fling is scroll is disabled // and ignore when a scale gesture has occurred return false; } - cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); float screenDensity = uiSettings.getPixelRatio(); @@ -362,9 +413,11 @@ final class MapGestureDetector { // cancel any animation transform.cancelTransitions(); + cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); + // tilt results in a bigger translation, limiting input for #5281 double tilt = transform.getTilt(); - double tiltFactor = 1 + ((tilt != 0) ? (tilt / 10) : 0); /* 1 -> 7 */ + double tiltFactor = 1.5 + ((tilt != 0) ? (tilt / 10) : 0); double offsetX = velocityX / tiltFactor / screenDensity; double offsetY = velocityY / tiltFactor / screenDensity; @@ -374,9 +427,7 @@ final class MapGestureDetector { // update transformation transform.moveBy(offsetX, offsetY, animationTime); - if (onFlingListener != null) { - onFlingListener.onFling(); - } + notifyOnFlingListeners(); return true; } @@ -387,16 +438,19 @@ final class MapGestureDetector { return false; } - if (dragStarted) { + if (tiltGestureOccurred) { return false; } - if (!scrollInProgress) { - scrollInProgress = true; + if (!scrollGestureOccurred) { + scrollGestureOccurred = true; // Cancel any animation - transform.cancelTransitions(); - cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); + if (!scaleGestureOccurred) { + transform.cancelTransitions(); + cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); + } + MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( getLocationFromGesture(e1.getX(), e1.getY()), MapboxEvent.GESTURE_PAN_START, transform)); @@ -408,20 +462,69 @@ final class MapGestureDetector { // Scroll the map transform.moveBy(-distanceX, -distanceY, 0 /*no duration*/); - if (onScrollListener != null) { - onScrollListener.onScroll(); - } + notifyOnScrollListeners(); return true; } } + void notifyOnMapClickListeners(PointF tapPoint) { + // deprecated API + if (onMapClickListener != null) { + onMapClickListener.onMapClick(projection.fromScreenLocation(tapPoint)); + } + + // new API + for (MapboxMap.OnMapClickListener listener : onMapClickListenerList) { + listener.onMapClick(projection.fromScreenLocation(tapPoint)); + } + } + + void notifyOnMapLongClickListeners(PointF longClickPoint) { + // deprecated API + if (onMapLongClickListener != null) { + onMapLongClickListener.onMapLongClick(projection.fromScreenLocation(longClickPoint)); + } + + // new API + for (MapboxMap.OnMapLongClickListener listener : onMapLongClickListenerList) { + listener.onMapLongClick(projection.fromScreenLocation(longClickPoint)); + } + } + + void notifyOnFlingListeners() { + // deprecated API + if (onFlingListener != null) { + onFlingListener.onFling(); + } + + // new API + for (MapboxMap.OnFlingListener listener : onFlingListenerList) { + listener.onFling(); + } + } + + void notifyOnScrollListeners() { + //deprecated API + if (onScrollListener != null) { + onScrollListener.onScroll(); + } + + // new API + for (MapboxMap.OnScrollListener listener : onScrollListenerList) { + listener.onScroll(); + } + } + /** * Responsible for handling two finger gestures and double-tap drag gestures. */ private class ScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { - long beginTime = 0; - float scaleFactor = 1.0f; + private static final int ANIMATION_TIME_MULTIPLIER = 77; + private static final double ZOOM_DISTANCE_DIVIDER = 5; + + private float scaleFactor = 1.0f; + private PointF scalePointBegin; // Called when two fingers first touch the screen @Override @@ -430,22 +533,15 @@ final class MapGestureDetector { return false; } - scaleGestureOccurred = true; - beginTime = detector.getEventTime(); + recentScaleGestureOccurred = true; + scalePointBegin = new PointF(detector.getFocusX(), detector.getFocusY()); + scaleBeginTime = detector.getEventTime(); MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( getLocationFromGesture(detector.getFocusX(), detector.getFocusY()), MapboxEvent.GESTURE_PINCH_START, transform)); return true; } - // Called when fingers leave screen - @Override - public void onScaleEnd(ScaleGestureDetector detector) { - beginTime = 0; - scaleFactor = 1.0f; - zoomStarted = false; - } - // Called each time a finger moves // Called for pinch zooms and quickzooms/quickscales @Override @@ -454,56 +550,126 @@ final class MapGestureDetector { return super.onScale(detector); } - // If scale is large enough ignore a tap - scaleFactor *= detector.getScaleFactor(); - if ((scaleFactor > 1.05f) || (scaleFactor < 0.95f)) { - // notify camera change listener - cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); - zoomStarted = true; + wasZoomingIn = (Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2)) >= 0; + if (tiltGestureOccurred) { + return false; } // Ignore short touches in case it is a tap // Also ignore small scales long time = detector.getEventTime(); - long interval = time - beginTime; - if (!zoomStarted && (interval <= ViewConfiguration.getTapTimeout())) { + long interval = time - scaleBeginTime; + if (!scaleGestureOccurred && (interval <= ViewConfiguration.getTapTimeout())) { return false; } - if (!zoomStarted) { - return false; + // If scale is large enough ignore a tap + scaleFactor *= detector.getScaleFactor(); + if ((scaleFactor > 1.1f) || (scaleFactor < 0.9f)) { + // notify camera change listener + cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); + scaleGestureOccurred = true; } - if (dragStarted) { + if (!scaleGestureOccurred) { return false; } // Gesture is a quickzoom if there aren't two fingers + if (!quickZoom && !twoTap) { + cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); + } quickZoom = !twoTap; // make an assumption here; if the zoom center is specified by the gesture, it's NOT going // to be in the center of the map. Therefore the zoom will translate the map center, so tracking // should be disabled. - trackingSettings.resetTrackingModesIfRequired(!quickZoom, false, false); // Scale the map if (focalPoint != null) { // arround user provided focal point - transform.zoomBy(Math.log(detector.getScaleFactor()) / Math.log(2), focalPoint.x, focalPoint.y); + transform.zoomBy(Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2), focalPoint.x, focalPoint.y); } else if (quickZoom) { + cameraChangeDispatcher.onCameraMove(); // clamp scale factors we feed to core #7514 - float scaleFactor = MathUtils.clamp(detector.getScaleFactor(), + float scaleFactor = detector.getScaleFactor(); + // around center map + double zoomBy = Math.log(scaleFactor) / Math.log(Math.PI / 2); + boolean negative = zoomBy < 0; + zoomBy = MathUtils.clamp(Math.abs(zoomBy), MapboxConstants.MINIMUM_SCALE_FACTOR_CLAMP, MapboxConstants.MAXIMUM_SCALE_FACTOR_CLAMP); - // around center map - transform.zoomBy(Math.log(scaleFactor) / Math.log(2), uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); + transform.zoomBy(negative ? -zoomBy : zoomBy, uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); + recentScaleGestureOccurred = true; } else { // around gesture - transform.zoomBy(Math.log(detector.getScaleFactor()) / Math.log(2), detector.getFocusX(), detector.getFocusY()); + transform.zoomBy(Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2), + scalePointBegin.x, scalePointBegin.y); } - return true; } + + // Called when fingers leave screen + @Override + public void onScaleEnd(final ScaleGestureDetector detector) { + if (velocityTracker == null) { + return; + } + + + if (rotateGestureOccurred || quickZoom) { + reset(); + return; + } + + double velocityXY = Math.abs(velocityTracker.getYVelocity()) + Math.abs(velocityTracker.getXVelocity()); + if (velocityXY > MapboxConstants.VELOCITY_THRESHOLD_IGNORE_FLING / 2) { + scaleAnimating = true; + double zoomAddition = calculateScale(velocityXY); + double currentZoom = transform.getRawZoom(); + long animationTime = (long) (Math.log(velocityXY) * ANIMATION_TIME_MULTIPLIER); + createScaleAnimator(currentZoom, zoomAddition, animationTime).start(); + } else if (!scaleAnimating) { + reset(); + } + } + + private void reset() { + scaleAnimating = false; + scaleGestureOccurred = false; + scaleBeginTime = 0; + scaleFactor = 1.0f; + cameraChangeDispatcher.onCameraIdle(); + } + + private double calculateScale(double velocityXY) { + double zoomAddition = (float) (Math.log(velocityXY) / ZOOM_DISTANCE_DIVIDER); + if (!wasZoomingIn) { + zoomAddition = -zoomAddition; + } + return zoomAddition; + } + + private Animator createScaleAnimator(double currentZoom, double zoomAddition, long animationTime) { + ValueAnimator animator = ValueAnimator.ofFloat((float) currentZoom, (float) (currentZoom + zoomAddition)); + animator.setDuration(animationTime); + animator.setInterpolator(new FastOutSlowInInterpolator()); + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + transform.setZoom((Float) animation.getAnimatedValue(), scalePointBegin); + } + }); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + reset(); + } + }); + return animator; + } } /** @@ -511,9 +677,13 @@ final class MapGestureDetector { */ private class RotateGestureListener extends RotateGestureDetector.SimpleOnRotateGestureListener { - long beginTime = 0; - float totalAngle = 0.0f; - boolean started = false; + private static final float ROTATE_INVOKE_ANGLE = 15.30f; + private static final float ROTATE_LIMITATION_ANGLE = 3.35f; + private static final float ROTATE_LIMITATION_DURATION = ROTATE_LIMITATION_ANGLE * 1.85f; + + private long beginTime = 0; + private boolean started = false; + private boolean animating = false; // Called when two fingers first touch the screen @Override @@ -526,54 +696,42 @@ final class MapGestureDetector { cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); beginTime = detector.getEventTime(); - MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( - getLocationFromGesture(detector.getFocusX(), detector.getFocusY()), - MapboxEvent.GESTURE_ROTATION_START, transform)); return true; } - // Called when the fingers leave the screen - @Override - public void onRotateEnd(RotateGestureDetector detector) { - // notify camera change listener - beginTime = 0; - totalAngle = 0.0f; - started = false; - } - // Called each time one of the two fingers moves // Called for rotation @Override public boolean onRotate(RotateGestureDetector detector) { - if (!trackingSettings.isRotateGestureCurrentlyEnabled() || dragStarted) { + if (!trackingSettings.isRotateGestureCurrentlyEnabled() || tiltGestureOccurred) { return false; } // If rotate is large enough ignore a tap // Also is zoom already started, don't rotate - totalAngle += detector.getRotationDegreesDelta(); - if (!zoomStarted && ((totalAngle > 20.0f) || (totalAngle < -20.0f))) { + float angle = detector.getRotationDegreesDelta(); + if (Math.abs(angle) >= ROTATE_INVOKE_ANGLE) { + MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( + getLocationFromGesture(detector.getFocusX(), detector.getFocusY()), + MapboxEvent.GESTURE_ROTATION_START, transform)); started = true; } - // Ignore short touches in case it is a tap - // Also ignore small rotate - long time = detector.getEventTime(); - long interval = time - beginTime; - if (!started && (interval <= ViewConfiguration.getTapTimeout())) { + if (!started) { return false; } - if (!started) { - return false; + wasClockwiseRotating = detector.getRotationDegreesDelta() > 0; + if (scaleBeginTime != 0) { + rotateGestureOccurred = true; } + // rotation constitutes translation of anything except the center of // rotation, so cancel both location and bearing tracking if required trackingSettings.resetTrackingModesIfRequired(true, true, false); - // Get rotate value - double bearing = transform.getRawBearing(); - bearing += detector.getRotationDegreesDelta(); + // Calculate map bearing value + double bearing = transform.getRawBearing() + angle; // Rotate the map if (focalPoint != null) { @@ -585,6 +743,83 @@ final class MapGestureDetector { } return true; } + + // Called when the fingers leave the screen + @Override + public void onRotateEnd(RotateGestureDetector detector) { + long interval = detector.getEventTime() - beginTime; + if ((!started && (interval <= ViewConfiguration.getTapTimeout())) || scaleAnimating || interval > 500) { + reset(); + return; + } + + double angularVelocity = calculateVelocityVector(detector); + if (Math.abs(angularVelocity) > 0.001 && rotateGestureOccurred && !animating) { + animateRotateVelocity(); + } else if (!animating) { + reset(); + } + } + + private void reset() { + beginTime = 0; + started = false; + animating = false; + rotateGestureOccurred = false; + } + + private void animateRotateVelocity() { + animating = true; + double currentRotation = transform.getRawBearing(); + double rotateAdditionDegrees = calculateVelocityInDegrees(); + createAnimator(currentRotation, rotateAdditionDegrees).start(); + } + + private double calculateVelocityVector(RotateGestureDetector detector) { + return ((detector.getFocusX() * velocityTracker.getYVelocity()) + + (detector.getFocusY() * velocityTracker.getXVelocity())) + / (Math.pow(detector.getFocusX(), 2) + Math.pow(detector.getFocusY(), 2)); + } + + private double calculateVelocityInDegrees() { + double angleRadians = Math.atan2(velocityTracker.getXVelocity(), velocityTracker.getYVelocity()); + double angle = angleRadians / (Math.PI / 180); + if (angle <= 0) { + angle += 360; + } + + // limit the angle + angle = angle / ROTATE_LIMITATION_ANGLE; + + // correct direction + if (!wasClockwiseRotating) { + angle = -angle; + } + + return angle; + } + + private Animator createAnimator(double currentRotation, double rotateAdditionDegrees) { + ValueAnimator animator = ValueAnimator.ofFloat( + (float) currentRotation, + (float) (currentRotation + rotateAdditionDegrees) + ); + animator.setDuration((long) (Math.abs(rotateAdditionDegrees) * ROTATE_LIMITATION_DURATION)); + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + transform.setBearing((Float) animation.getAnimatedValue()); + } + }); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + reset(); + } + }); + return animator; + } } /** @@ -592,9 +827,8 @@ final class MapGestureDetector { */ private class ShoveGestureListener implements ShoveGestureDetector.OnShoveGestureListener { - long beginTime = 0; - float totalDelta = 0.0f; - boolean started = false; + private long beginTime = 0; + private float totalDelta = 0.0f; @Override public boolean onShoveBegin(ShoveGestureDetector detector) { @@ -604,10 +838,6 @@ final class MapGestureDetector { // notify camera change listener cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); - beginTime = detector.getEventTime(); - MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( - getLocationFromGesture(detector.getFocusX(), detector.getFocusY()), - MapboxEvent.GESTURE_PITCH_START, transform)); return true; } @@ -615,8 +845,7 @@ final class MapGestureDetector { public void onShoveEnd(ShoveGestureDetector detector) { beginTime = 0; totalDelta = 0.0f; - started = false; - dragStarted = false; + tiltGestureOccurred = false; } @Override @@ -625,22 +854,26 @@ final class MapGestureDetector { return false; } - // If tilt is large enough ignore a tap - // Also if zoom already started, don't tilt - totalDelta += detector.getShovePixelsDelta(); - if (!zoomStarted && ((totalDelta > 10.0f) || (totalDelta < -10.0f))) { - started = true; - } - // Ignore short touches in case it is a tap // Also ignore small tilt long time = detector.getEventTime(); long interval = time - beginTime; - if (!started && (interval <= ViewConfiguration.getTapTimeout())) { + if (!tiltGestureOccurred && (interval <= ViewConfiguration.getTapTimeout())) { return false; } - if (!started) { + // If tilt is large enough ignore a tap + // Also if zoom already started, don't tilt + totalDelta += detector.getShovePixelsDelta(); + if (!tiltGestureOccurred && ((totalDelta > 10.0f) || (totalDelta < -10.0f))) { + tiltGestureOccurred = true; + beginTime = detector.getEventTime(); + MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( + getLocationFromGesture(detector.getFocusX(), detector.getFocusY()), + MapboxEvent.GESTURE_PITCH_START, transform)); + } + + if (!tiltGestureOccurred) { return false; } @@ -651,9 +884,6 @@ final class MapGestureDetector { // Tilt the map transform.setTilt(pitch); - - dragStarted = true; - return true; } } @@ -673,4 +903,36 @@ final class MapGestureDetector { void setOnScrollListener(MapboxMap.OnScrollListener onScrollListener) { this.onScrollListener = onScrollListener; } + + void addOnMapClickListener(MapboxMap.OnMapClickListener onMapClickListener) { + onMapClickListenerList.add(onMapClickListener); + } + + void removeOnMapClickListener(MapboxMap.OnMapClickListener onMapClickListener) { + onMapClickListenerList.remove(onMapClickListener); + } + + void addOnMapLongClickListener(MapboxMap.OnMapLongClickListener onMapLongClickListener) { + onMapLongClickListenerList.add(onMapLongClickListener); + } + + void removeOnMapLongClickListener(MapboxMap.OnMapLongClickListener onMapLongClickListener) { + onMapLongClickListenerList.remove(onMapLongClickListener); + } + + void addOnFlingListener(MapboxMap.OnFlingListener onFlingListener) { + onFlingListenerList.add(onFlingListener); + } + + void removeOnFlingListener(MapboxMap.OnFlingListener onFlingListener) { + onFlingListenerList.remove(onFlingListener); + } + + void addOnScrollListener(MapboxMap.OnScrollListener onScrollListener) { + onScrollListenerList.add(onScrollListener); + } + + void removeOnScrollListener(MapboxMap.OnScrollListener onScrollListener) { + onScrollListenerList.remove(onScrollListener); + } } |