summaryrefslogtreecommitdiff
path: root/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java
diff options
context:
space:
mode:
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.java1140
1 files changed, 0 insertions, 1140 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
deleted file mode 100644
index 90e3934f7c..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java
+++ /dev/null
@@ -1,1140 +0,0 @@
-package com.mapbox.mapboxsdk.maps;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.PointF;
-import android.os.Handler;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.view.InputDevice;
-import android.view.MotionEvent;
-import android.view.animation.DecelerateInterpolator;
-
-import com.mapbox.android.gestures.AndroidGesturesManager;
-import com.mapbox.android.gestures.MoveGestureDetector;
-import com.mapbox.android.gestures.MultiFingerTapGestureDetector;
-import com.mapbox.android.gestures.RotateGestureDetector;
-import com.mapbox.android.gestures.ShoveGestureDetector;
-import com.mapbox.android.gestures.StandardGestureDetector;
-import com.mapbox.android.gestures.StandardScaleGestureDetector;
-import com.mapbox.mapboxsdk.R;
-import com.mapbox.mapboxsdk.constants.MapboxConstants;
-import com.mapbox.mapboxsdk.utils.MathUtils;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-import static com.mapbox.mapboxsdk.constants.MapboxConstants.MAXIMUM_ANGULAR_VELOCITY;
-import static com.mapbox.mapboxsdk.constants.MapboxConstants.MAX_ABSOLUTE_SCALE_VELOCITY_CHANGE;
-import static com.mapbox.mapboxsdk.constants.MapboxConstants.QUICK_ZOOM_MAX_ZOOM_CHANGE;
-import static com.mapbox.mapboxsdk.constants.MapboxConstants.ROTATE_VELOCITY_RATIO_THRESHOLD;
-import static com.mapbox.mapboxsdk.constants.MapboxConstants.SCALE_VELOCITY_ANIMATION_DURATION_MULTIPLIER;
-import static com.mapbox.mapboxsdk.constants.MapboxConstants.SCALE_VELOCITY_RATIO_THRESHOLD;
-import static com.mapbox.mapboxsdk.constants.MapboxConstants.ZOOM_RATE;
-import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE;
-import static com.mapbox.mapboxsdk.utils.MathUtils.normalize;
-
-/**
- * Manages gestures events on a MapView.
- */
-final class MapGestureDetector {
-
- private final Transform transform;
- private final Projection projection;
- private final UiSettings uiSettings;
- private final AnnotationManager annotationManager;
- private final CameraChangeDispatcher cameraChangeDispatcher;
-
- // 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.OnMoveListener> onMoveListenerList
- = new CopyOnWriteArrayList<>();
-
- private final CopyOnWriteArrayList<MapboxMap.OnRotateListener> onRotateListenerList
- = new CopyOnWriteArrayList<>();
-
- private final CopyOnWriteArrayList<MapboxMap.OnScaleListener> onScaleListenerList
- = new CopyOnWriteArrayList<>();
-
- private final CopyOnWriteArrayList<MapboxMap.OnShoveListener> onShoveListenerList
- = new CopyOnWriteArrayList<>();
-
- /**
- * User-set focal point.
- */
- @Nullable
- private PointF constantFocalPoint;
-
- @NonNull
- private PointF doubleTapFocalPoint = new PointF();
-
- private AndroidGesturesManager gesturesManager;
-
- private Animator scaleAnimator;
- private Animator rotateAnimator;
- private final List<Animator> scheduledAnimators = new ArrayList<>();
-
- /**
- * Cancels scheduled velocity animations if user doesn't lift fingers within
- * {@link MapboxConstants#SCHEDULED_ANIMATION_TIMEOUT}
- */
- @NonNull
- private Handler animationsTimeoutHandler = new Handler();
-
- private boolean doubleTapRegistered;
-
- MapGestureDetector(@Nullable Context context, Transform transform, Projection projection, UiSettings uiSettings,
- AnnotationManager annotationManager, CameraChangeDispatcher cameraChangeDispatcher) {
- this.annotationManager = annotationManager;
- this.transform = transform;
- this.projection = projection;
- this.uiSettings = uiSettings;
- this.cameraChangeDispatcher = cameraChangeDispatcher;
-
- // Checking for context != null for testing purposes
- if (context != null) {
- // Initialize gestures manager
- AndroidGesturesManager androidGesturesManager = new AndroidGesturesManager(context);
- initializeGesturesManager(androidGesturesManager, true);
-
- // Initialize gesture listeners
- initializeGestureListeners(context, true);
- }
- }
-
- private void initializeGestureListeners(@NonNull Context context, boolean attachDefaultListeners) {
- if (attachDefaultListeners) {
- StandardGestureListener standardGestureListener = new StandardGestureListener(
- context.getResources().getDimension(
- com.mapbox.android.gestures.R.dimen.mapbox_defaultScaleSpanSinceStartThreshold));
- MoveGestureListener moveGestureListener = new MoveGestureListener();
- ScaleGestureListener scaleGestureListener = new ScaleGestureListener(
- context.getResources().getDimension(R.dimen.mapbox_density_constant),
- context.getResources().getDimension(R.dimen.mapbox_minimum_scale_speed),
- context.getResources().getDimension(R.dimen.mapbox_minimum_angled_scale_speed),
- context.getResources().getDimension(R.dimen.mapbox_minimum_scale_velocity)
- );
- RotateGestureListener rotateGestureListener = new RotateGestureListener(
- context.getResources().getDimension(R.dimen.mapbox_minimum_scale_span_when_rotating),
- context.getResources().getDimension(R.dimen.mapbox_density_constant),
- context.getResources().getDimension(R.dimen.mapbox_angular_velocity_multiplier),
- context.getResources().getDimension(R.dimen.mapbox_minimum_angular_velocity),
- context.getResources().getDimension(
- com.mapbox.android.gestures.R.dimen.mapbox_defaultScaleSpanSinceStartThreshold));
- ShoveGestureListener shoveGestureListener = new ShoveGestureListener();
- TapGestureListener tapGestureListener = new TapGestureListener();
-
- gesturesManager.setStandardGestureListener(standardGestureListener);
- gesturesManager.setMoveGestureListener(moveGestureListener);
- gesturesManager.setStandardScaleGestureListener(scaleGestureListener);
- gesturesManager.setRotateGestureListener(rotateGestureListener);
- gesturesManager.setShoveGestureListener(shoveGestureListener);
- gesturesManager.setMultiFingerTapGestureListener(tapGestureListener);
- }
- }
-
- private void initializeGesturesManager(@NonNull AndroidGesturesManager androidGesturesManager,
- boolean setDefaultMutuallyExclusives) {
- if (setDefaultMutuallyExclusives) {
- Set<Integer> shoveScaleSet = new HashSet<>();
- shoveScaleSet.add(AndroidGesturesManager.GESTURE_TYPE_SHOVE);
- shoveScaleSet.add(AndroidGesturesManager.GESTURE_TYPE_SCALE);
-
- Set<Integer> shoveRotateSet = new HashSet<>();
- shoveRotateSet.add(AndroidGesturesManager.GESTURE_TYPE_SHOVE);
- shoveRotateSet.add(AndroidGesturesManager.GESTURE_TYPE_ROTATE);
-
- Set<Integer> ScaleLongPressSet = new HashSet<>();
- ScaleLongPressSet.add(AndroidGesturesManager.GESTURE_TYPE_SCALE);
- ScaleLongPressSet.add(AndroidGesturesManager.GESTURE_TYPE_LONG_PRESS);
-
- androidGesturesManager.setMutuallyExclusiveGestures(shoveScaleSet, shoveRotateSet, ScaleLongPressSet);
- }
-
- gesturesManager = androidGesturesManager;
- gesturesManager.getRotateGestureDetector().setAngleThreshold(3f);
- }
-
- /**
- * Set the gesture focal point.
- * <p>
- * This is the center point used for calculate transformations from gestures, value is
- * overridden if end user provides his own through {@link UiSettings#setFocalPoint(PointF)}.
- * </p>
- *
- * @param focalPoint the center point for gestures
- */
- void setFocalPoint(@Nullable PointF focalPoint) {
- if (focalPoint == null) {
- // resetting focal point,
- if (uiSettings.getFocalPoint() != null) {
- // using user provided one to reset
- focalPoint = uiSettings.getFocalPoint();
- }
- }
- this.constantFocalPoint = focalPoint;
- }
-
- /**
- * Called when user touches the screen, all positions are absolute.
- * <p>
- * Forwards event to the related gesture detectors.
- * </p>
- *
- * @param motionEvent the MotionEvent
- * @return True if touch event is handled
- */
- boolean onTouchEvent(@Nullable MotionEvent motionEvent) {
- // Framework can return null motion events in edge cases #9432
- if (motionEvent == null) {
- return false;
- }
-
- // Check and ignore non touch or left clicks
- if ((motionEvent.getButtonState() != 0) && (motionEvent.getButtonState() != MotionEvent.BUTTON_PRIMARY)) {
- return false;
- }
-
- if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
- cancelAnimators();
- transform.setGestureInProgress(true);
- }
-
- boolean result = gesturesManager.onTouchEvent(motionEvent);
-
- switch (motionEvent.getActionMasked()) {
- case MotionEvent.ACTION_POINTER_DOWN:
- doubleTapFinished();
- break;
-
- case MotionEvent.ACTION_UP:
- doubleTapFinished();
- transform.setGestureInProgress(false);
-
- if (!scheduledAnimators.isEmpty()) {
- // Start all awaiting velocity animations
- animationsTimeoutHandler.removeCallbacksAndMessages(null);
- for (Animator animator : scheduledAnimators) {
- animator.start();
- }
- scheduledAnimators.clear();
- }
- break;
-
- case MotionEvent.ACTION_CANCEL:
- scheduledAnimators.clear();
- transform.setGestureInProgress(false);
- doubleTapFinished();
- break;
- }
-
- return result;
- }
-
- void cancelAnimators() {
- animationsTimeoutHandler.removeCallbacksAndMessages(null);
- scheduledAnimators.clear();
-
- cancelAnimator(scaleAnimator);
- cancelAnimator(rotateAnimator);
-
- dispatchCameraIdle();
- }
-
- private void cancelAnimator(@Nullable Animator animator) {
- if (animator != null && animator.isStarted()) {
- animator.cancel();
- }
- }
-
- /**
- * Posted on main thread with {@link #animationsTimeoutHandler}. Cancels all scheduled animators if needed.
- */
- @NonNull
- private final Runnable cancelAnimatorsRunnable = new Runnable() {
- @Override
- public void run() {
- cancelAnimators();
- }
- };
-
- /**
- * Schedules a velocity animator to be executed when user lifts fingers,
- * unless canceled by the {@link #cancelAnimatorsRunnable}.
- *
- * @param animator animator ot be scheduled
- */
- private void scheduleAnimator(Animator animator) {
- scheduledAnimators.add(animator);
- animationsTimeoutHandler.removeCallbacksAndMessages(null);
- animationsTimeoutHandler.postDelayed(cancelAnimatorsRunnable, MapboxConstants.SCHEDULED_ANIMATION_TIMEOUT);
- }
-
- /**
- * Called for events that don't fit the other handlers.
- * <p>
- * Examples of such events are mouse scroll events, mouse moves, joystick & trackpad.
- * </p>
- *
- * @param event The MotionEvent occurred
- * @return True is the event is handled
- */
- boolean onGenericMotionEvent(MotionEvent event) {
- // Mouse events
- // if (event.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) { // this is not available before API 18
- if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == InputDevice.SOURCE_CLASS_POINTER) {
- // Choose the action
- switch (event.getActionMasked()) {
- // Mouse scrolls
- case MotionEvent.ACTION_SCROLL:
- if (!uiSettings.isZoomGesturesEnabled()) {
- return false;
- }
-
- // Cancel any animation
- transform.cancelTransitions();
-
- // Get the vertical scroll amount, one click = 1
- float scrollDist = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
-
- // Scale the map by the appropriate power of two factor
- transform.zoomBy(scrollDist, new PointF(event.getX(), event.getY()));
-
- return true;
-
- default:
- // We are not interested in this event
- return false;
- }
- }
-
- // We are not interested in this event
- return false;
- }
-
- private final class StandardGestureListener extends StandardGestureDetector.SimpleStandardOnGestureListener {
- private final float doubleTapMovementThreshold;
-
- StandardGestureListener(float doubleTapMovementThreshold) {
- this.doubleTapMovementThreshold = doubleTapMovementThreshold;
- }
-
- @Override
- public boolean onDown(MotionEvent motionEvent) {
- return true;
- }
-
- @Override
- public boolean onSingleTapUp(MotionEvent motionEvent) {
- transform.cancelTransitions();
- return true;
- }
-
- @Override
- public boolean onSingleTapConfirmed(MotionEvent motionEvent) {
- PointF tapPoint = new PointF(motionEvent.getX(), motionEvent.getY());
- boolean tapHandled = annotationManager.onTap(tapPoint);
-
- if (!tapHandled) {
- if (uiSettings.isDeselectMarkersOnTap()) {
- // deselect any selected marker
- annotationManager.deselectMarkers();
- }
-
- notifyOnMapClickListeners(tapPoint);
- }
-
- return true;
- }
-
- @Override
- public boolean onDoubleTapEvent(MotionEvent motionEvent) {
- int action = motionEvent.getActionMasked();
- if (action == MotionEvent.ACTION_DOWN) {
- doubleTapFocalPoint = new PointF(motionEvent.getX(), motionEvent.getY());
- doubleTapStarted();
- }
-
- if (motionEvent.getActionMasked() == MotionEvent.ACTION_UP) {
- float diffX = Math.abs(motionEvent.getX() - doubleTapFocalPoint.x);
- float diffY = Math.abs(motionEvent.getY() - doubleTapFocalPoint.y);
- if (diffX > doubleTapMovementThreshold || diffY > doubleTapMovementThreshold) {
- // Ignore double-tap event because we've started the quick-zoom. See #14013.
- return false;
- }
-
- if (!uiSettings.isZoomGesturesEnabled() || !uiSettings.isDoubleTapGesturesEnabled()) {
- return false;
- }
-
- // Single finger double tap
- if (constantFocalPoint != null) {
- // User provided focal point
- doubleTapFocalPoint = constantFocalPoint;
- }
-
- zoomInAnimated(doubleTapFocalPoint, false);
-
- return true;
- }
-
- return super.onDoubleTapEvent(motionEvent);
- }
-
- @Override
- public void onLongPress(MotionEvent motionEvent) {
- PointF longClickPoint = new PointF(motionEvent.getX(), motionEvent.getY());
- notifyOnMapLongClickListeners(longClickPoint);
- }
-
- @Override
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
- if (!uiSettings.isScrollGesturesEnabled()) {
- // don't allow a fling if scroll is disabled
- return false;
- }
-
- notifyOnFlingListeners();
-
- if (!uiSettings.isFlingVelocityAnimationEnabled()) {
- return false;
- }
-
- float screenDensity = uiSettings.getPixelRatio();
-
- // calculate velocity vector for xy dimensions, independent from screen size
- double velocityXY = Math.hypot(velocityX / screenDensity, velocityY / screenDensity);
- if (velocityXY < MapboxConstants.VELOCITY_THRESHOLD_IGNORE_FLING) {
- // ignore short flings, these can occur when other gestures just have finished executing
- return false;
- }
-
- transform.cancelTransitions();
- cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
-
- // tilt results in a bigger translation, limiting input for #5281
- double tilt = transform.getTilt();
- double tiltFactor = 1.5 + ((tilt != 0) ? (tilt / 10) : 0);
- double offsetX = velocityX / tiltFactor / screenDensity;
- double offsetY = velocityY / tiltFactor / screenDensity;
-
- // calculate animation time based on displacement
- long animationTime = (long) (velocityXY / 7 / tiltFactor + MapboxConstants.ANIMATION_DURATION_FLING_BASE);
-
- // update transformation
- transform.moveBy(offsetX, offsetY, animationTime);
-
- return true;
- }
- }
-
- private void doubleTapStarted() {
- // disable the move detector in preparation for the quickzoom,
- // so that we don't move the map's center slightly before the quickzoom is started (see #14227)
- gesturesManager.getMoveGestureDetector().setEnabled(false);
- doubleTapRegistered = true;
- }
-
- private void doubleTapFinished() {
- if (doubleTapRegistered) {
- // re-enable the move detector in case of double tap
- gesturesManager.getMoveGestureDetector().setEnabled(true);
- doubleTapRegistered = false;
- }
- }
-
- private final class MoveGestureListener extends MoveGestureDetector.SimpleOnMoveGestureListener {
- @Override
- public boolean onMoveBegin(@NonNull MoveGestureDetector detector) {
- if (!uiSettings.isScrollGesturesEnabled()) {
- return false;
- }
-
- cancelTransitionsIfRequired();
- notifyOnMoveBeginListeners(detector);
- return true;
- }
-
- @Override
- public boolean onMove(@NonNull MoveGestureDetector detector, float distanceX, float distanceY) {
- // first move event is often delivered with no displacement
- if (distanceX != 0 || distanceY != 0) {
- // dispatching camera start event only when the movement actually occurred
- cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE);
-
- // Scroll the map
- transform.moveBy(-distanceX, -distanceY, 0 /*no duration*/);
-
- notifyOnMoveListeners(detector);
- }
- return true;
- }
-
- @Override
- public void onMoveEnd(@NonNull MoveGestureDetector detector, float velocityX, float velocityY) {
- dispatchCameraIdle();
- notifyOnMoveEndListeners(detector);
- }
- }
-
- private final class ScaleGestureListener extends StandardScaleGestureDetector.SimpleStandardOnScaleGestureListener {
-
- private final float minimumGestureSpeed;
- private final float minimumAngledGestureSpeed;
- private final float minimumVelocity;
- private final double scaleVelocityRatioThreshold;
- private boolean quickZoom;
- private float spanSinceLast;
- private double screenHeight;
- private double startZoom;
-
- ScaleGestureListener(double densityMultiplier, float minimumGestureSpeed, float minimumAngledGestureSpeed,
- float minimumVelocity) {
- this.minimumGestureSpeed = minimumGestureSpeed;
- this.minimumAngledGestureSpeed = minimumAngledGestureSpeed;
- this.minimumVelocity = minimumVelocity;
- this.scaleVelocityRatioThreshold = SCALE_VELOCITY_RATIO_THRESHOLD * densityMultiplier;
- }
-
- @Override
- public boolean onScaleBegin(@NonNull StandardScaleGestureDetector detector) {
- quickZoom = detector.getPointersCount() == 1;
-
- if (!uiSettings.isZoomGesturesEnabled()) {
- return false;
- }
-
- if (quickZoom) {
- if (!uiSettings.isQuickZoomGesturesEnabled()) {
- return false;
- }
- // re-try disabling the move detector in case double tap has been interrupted before quickzoom started
- gesturesManager.getMoveGestureDetector().setEnabled(false);
- } else {
- if (detector.getPreviousSpan() > 0) {
- float currSpan = detector.getCurrentSpan();
- float prevSpan = detector.getPreviousSpan();
- double currTime = detector.getCurrentEvent().getEventTime();
- double prevTime = detector.getPreviousEvent().getEventTime();
- if (currTime == prevTime) {
- return false;
- }
- double speed = Math.abs(currSpan - prevSpan) / (currTime - prevTime);
- if (speed < minimumGestureSpeed) {
- // do not scale if the minimal gesture speed is not met
- return false;
- } else if (!gesturesManager.getRotateGestureDetector().isInProgress()) {
- float rotationDeltaSinceLast = gesturesManager.getRotateGestureDetector().getDeltaSinceLast();
- if (Math.abs(rotationDeltaSinceLast) > 0.4 && speed < minimumAngledGestureSpeed) {
- // do not scale in case we're preferring to start rotation
- return false;
- }
-
- if (uiSettings.isDisableRotateWhenScaling()) {
- // disable rotate gesture when scale is detected first
- gesturesManager.getRotateGestureDetector().setEnabled(false);
- }
- }
- } else {
- return false;
- }
- }
-
- screenHeight = Resources.getSystem().getDisplayMetrics().heightPixels;
- startZoom = transform.getRawZoom();
-
- cancelTransitionsIfRequired();
-
- notifyOnScaleBeginListeners(detector);
-
- spanSinceLast = Math.abs(detector.getCurrentSpan() - detector.getPreviousSpan());
-
- return true;
- }
-
- @Override
- public boolean onScale(@NonNull StandardScaleGestureDetector detector) {
- // dispatching camera start event only when the movement actually occurred
- cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE);
-
- PointF focalPoint = getScaleFocalPoint(detector);
- if (quickZoom) {
- double pixelDeltaChange = Math.abs(detector.getCurrentEvent().getY() - doubleTapFocalPoint.y);
- boolean zoomedOut = detector.getCurrentEvent().getY() < doubleTapFocalPoint.y;
-
- // normalize the pixel delta change, ranging from 0 to screen height, to a constant zoom change range
- double normalizedDeltaChange = normalize(pixelDeltaChange, 0, screenHeight, 0, QUICK_ZOOM_MAX_ZOOM_CHANGE);
-
- // calculate target zoom and adjust for a multiplier
- double targetZoom = (zoomedOut ? startZoom - normalizedDeltaChange : startZoom + normalizedDeltaChange);
- targetZoom *= uiSettings.getZoomRate();
-
- transform.setZoom(targetZoom, focalPoint);
- } else {
- double zoomBy =
- (Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2)) * ZOOM_RATE * uiSettings.getZoomRate();
- transform.zoomBy(zoomBy, focalPoint);
- }
-
- notifyOnScaleListeners(detector);
-
- spanSinceLast = Math.abs(detector.getCurrentSpan() - detector.getPreviousSpan());
-
- return true;
- }
-
- @Override
- public void onScaleEnd(@NonNull StandardScaleGestureDetector detector, float velocityX, float velocityY) {
- if (quickZoom) {
- // re-enabled the move detector if the quickzoom happened
- gesturesManager.getMoveGestureDetector().setEnabled(true);
- } else {
- // re-enable rotation in case it's been disabled
- gesturesManager.getRotateGestureDetector().setEnabled(true);
- }
-
- notifyOnScaleEndListeners(detector);
-
- float velocityXY = Math.abs(velocityX) + Math.abs(velocityY);
-
- if (!uiSettings.isScaleVelocityAnimationEnabled()
- || velocityXY < minimumVelocity
- || spanSinceLast / velocityXY < scaleVelocityRatioThreshold) {
- // notifying listeners that camera is idle only if there is no follow-up animation
- dispatchCameraIdle();
- return;
- }
-
- double zoomAddition = calculateScale(velocityXY, detector.isScalingOut());
- double currentZoom = transform.getRawZoom();
- PointF focalPoint = getScaleFocalPoint(detector);
- // (log(x + 1 / e^2) + 2) * 150, x=0 to 2.5 (MapboxConstants#MAX_ABSOLUTE_SCALE_VELOCITY_CHANGE)
- long animationTime = (long) ((Math.log((Math.abs(zoomAddition)) + 1 / Math.pow(Math.E, 2)) + 2)
- * SCALE_VELOCITY_ANIMATION_DURATION_MULTIPLIER);
- scaleAnimator = createScaleAnimator(currentZoom, zoomAddition, focalPoint, animationTime);
- scheduleAnimator(scaleAnimator);
- }
-
- @NonNull
- private PointF getScaleFocalPoint(@NonNull StandardScaleGestureDetector detector) {
- if (constantFocalPoint != null) {
- // around user provided focal point
- return constantFocalPoint;
- } else if (quickZoom) {
- // around center
- return new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2);
- } else {
- // around gesture
- return detector.getFocalPoint();
- }
- }
-
- private double calculateScale(double velocityXY, boolean isScalingOut) {
- double zoomAddition = velocityXY * MAX_ABSOLUTE_SCALE_VELOCITY_CHANGE * 1e-4;
- zoomAddition = MathUtils.clamp(zoomAddition, 0, MAX_ABSOLUTE_SCALE_VELOCITY_CHANGE);
- if (isScalingOut) {
- zoomAddition = -zoomAddition;
- }
- return zoomAddition;
- }
- }
-
- private final class RotateGestureListener extends RotateGestureDetector.SimpleOnRotateGestureListener {
- private final float minimumScaleSpanWhenRotating;
- private final float angularVelocityMultiplier;
- private final float minimumAngularVelocity;
- private final double rotateVelocityRatioThreshold;
- private final float defaultSpanSinceStartThreshold;
-
- RotateGestureListener(float minimumScaleSpanWhenRotating, double densityMultiplier, float angularVelocityMultiplier,
- float minimumAngularVelocity, float defaultSpanSinceStartThreshold) {
- this.minimumScaleSpanWhenRotating = minimumScaleSpanWhenRotating;
- this.angularVelocityMultiplier = angularVelocityMultiplier;
- this.minimumAngularVelocity = minimumAngularVelocity;
- this.rotateVelocityRatioThreshold = ROTATE_VELOCITY_RATIO_THRESHOLD * densityMultiplier;
- this.defaultSpanSinceStartThreshold = defaultSpanSinceStartThreshold;
- }
-
- @Override
- public boolean onRotateBegin(@NonNull RotateGestureDetector detector) {
- if (!uiSettings.isRotateGesturesEnabled()) {
- return false;
- }
-
- float deltaSinceLast = Math.abs(detector.getDeltaSinceLast());
- double currTime = detector.getCurrentEvent().getEventTime();
- double prevTime = detector.getPreviousEvent().getEventTime();
- if (currTime == prevTime) {
- return false;
- }
- double speed = deltaSinceLast / (currTime - prevTime);
- float deltaSinceStart = Math.abs(detector.getDeltaSinceStart());
-
- // adjust the responsiveness of a rotation gesture - the higher the speed, the bigger the threshold
- if (speed < 0.04
- || (speed > 0.07 && deltaSinceStart < 5)
- || (speed > 0.15 && deltaSinceStart < 7)
- || (speed > 0.5 && deltaSinceStart < 15)
- ) {
- return false;
- }
-
- if (uiSettings.isIncreaseScaleThresholdWhenRotating()) {
- // when rotation starts, interrupting scale and increasing the threshold
- // to make rotation without scaling easier
- gesturesManager.getStandardScaleGestureDetector().setSpanSinceStartThreshold(minimumScaleSpanWhenRotating);
- gesturesManager.getStandardScaleGestureDetector().interrupt();
- }
-
- cancelTransitionsIfRequired();
-
- notifyOnRotateBeginListeners(detector);
-
- return true;
- }
-
- @Override
- public boolean onRotate(@NonNull RotateGestureDetector detector, float rotationDegreesSinceLast,
- float rotationDegreesSinceFirst) {
- // dispatching camera start event only when the movement actually occurred
- cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE);
-
- // Calculate map bearing value
- double bearing = transform.getRawBearing() + rotationDegreesSinceLast;
-
- // Rotate the map
- PointF focalPoint = getRotateFocalPoint(detector);
- transform.setBearing(bearing, focalPoint.x, focalPoint.y);
-
- notifyOnRotateListeners(detector);
-
- return true;
- }
-
- @Override
- public void onRotateEnd(@NonNull RotateGestureDetector detector, float velocityX,
- float velocityY, float angularVelocity) {
- if (uiSettings.isIncreaseScaleThresholdWhenRotating()) {
- // resetting default scale threshold values
- gesturesManager.getStandardScaleGestureDetector().setSpanSinceStartThreshold(defaultSpanSinceStartThreshold);
- }
-
- notifyOnRotateEndListeners(detector);
-
- angularVelocity = angularVelocity * angularVelocityMultiplier;
- angularVelocity = MathUtils.clamp(angularVelocity, -MAXIMUM_ANGULAR_VELOCITY, MAXIMUM_ANGULAR_VELOCITY);
-
- float velocityXY = Math.abs(velocityX) + Math.abs(velocityY);
- float delta = Math.abs(detector.getDeltaSinceLast());
- double ratio = delta / velocityXY;
-
- if (!uiSettings.isRotateVelocityAnimationEnabled()
- || Math.abs(angularVelocity) < minimumAngularVelocity
- || (gesturesManager.getStandardScaleGestureDetector().isInProgress() && ratio < rotateVelocityRatioThreshold)) {
- // notifying listeners that camera is idle only if there is no follow-up animation
- dispatchCameraIdle();
- return;
- }
-
- long animationTime = (long) ((Math.log((Math.abs(angularVelocity)) + 1 / Math.pow(Math.E, 2)) + 2)
- * SCALE_VELOCITY_ANIMATION_DURATION_MULTIPLIER);
-
- PointF focalPoint = getRotateFocalPoint(detector);
- rotateAnimator = createRotateAnimator(angularVelocity, animationTime, focalPoint);
- scheduleAnimator(rotateAnimator);
- }
-
- @NonNull
- private PointF getRotateFocalPoint(@NonNull RotateGestureDetector detector) {
- if (constantFocalPoint != null) {
- // User provided focal point
- return constantFocalPoint;
- } else {
- // around gesture
- return detector.getFocalPoint();
- }
- }
-
- private Animator createRotateAnimator(float angularVelocity, long animationTime,
- @NonNull final PointF animationFocalPoint) {
- ValueAnimator animator = ValueAnimator.ofFloat(angularVelocity, 0f);
- animator.setDuration(animationTime);
- animator.setInterpolator(new DecelerateInterpolator());
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(@NonNull ValueAnimator animation) {
- transform.setBearing(
- transform.getRawBearing() + (float) animation.getAnimatedValue(),
- animationFocalPoint.x, animationFocalPoint.y,
- 0L
- );
- }
- });
-
- animator.addListener(new AnimatorListenerAdapter() {
-
- @Override
- public void onAnimationStart(Animator animation) {
- transform.cancelTransitions();
- cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- transform.cancelTransitions();
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- dispatchCameraIdle();
- }
- });
-
- return animator;
- }
- }
-
- private final class ShoveGestureListener extends ShoveGestureDetector.SimpleOnShoveGestureListener {
- @Override
- public boolean onShoveBegin(@NonNull ShoveGestureDetector detector) {
- if (!uiSettings.isTiltGesturesEnabled()) {
- return false;
- }
-
- cancelTransitionsIfRequired();
-
- // disabling move gesture during shove
- gesturesManager.getMoveGestureDetector().setEnabled(false);
-
- notifyOnShoveBeginListeners(detector);
-
- return true;
- }
-
- @Override
- public boolean onShove(@NonNull ShoveGestureDetector detector,
- float deltaPixelsSinceLast, float deltaPixelsSinceStart) {
- // dispatching camera start event only when the movement actually occurred
- cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE);
-
- // Get tilt value (scale and clamp)
- double pitch = transform.getTilt();
- pitch -= MapboxConstants.SHOVE_PIXEL_CHANGE_FACTOR * deltaPixelsSinceLast;
- pitch = MathUtils.clamp(pitch, MapboxConstants.MINIMUM_TILT, MapboxConstants.MAXIMUM_TILT);
-
- // Tilt the map
- transform.setTilt(pitch);
-
- notifyOnShoveListeners(detector);
-
- return true;
- }
-
- @Override
- public void onShoveEnd(@NonNull ShoveGestureDetector detector, float velocityX, float velocityY) {
- dispatchCameraIdle();
-
- // re-enabling move gesture
- gesturesManager.getMoveGestureDetector().setEnabled(true);
-
- notifyOnShoveEndListeners(detector);
- }
- }
-
- private final class TapGestureListener implements MultiFingerTapGestureDetector.OnMultiFingerTapGestureListener {
- @Override
- public boolean onMultiFingerTap(@NonNull MultiFingerTapGestureDetector detector, int pointersCount) {
- if (!uiSettings.isZoomGesturesEnabled() || pointersCount != 2) {
- return false;
- }
-
- transform.cancelTransitions();
- cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
-
- PointF zoomFocalPoint;
- // Single finger double tap
- if (constantFocalPoint != null) {
- // User provided focal point
- zoomFocalPoint = constantFocalPoint;
- } else {
- // Zoom in on gesture
- zoomFocalPoint = detector.getFocalPoint();
- }
-
- zoomOutAnimated(zoomFocalPoint, false);
-
- return true;
- }
- }
-
- private Animator createScaleAnimator(double currentZoom, double zoomAddition,
- @NonNull final PointF animationFocalPoint, long animationTime) {
- ValueAnimator animator = ValueAnimator.ofFloat((float) currentZoom, (float) (currentZoom + zoomAddition));
- animator.setDuration(animationTime);
- animator.setInterpolator(new DecelerateInterpolator());
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-
- @Override
- public void onAnimationUpdate(@NonNull ValueAnimator animation) {
- transform.setZoom((Float) animation.getAnimatedValue(), animationFocalPoint);
- }
- });
-
- animator.addListener(new AnimatorListenerAdapter() {
-
- @Override
- public void onAnimationStart(Animator animation) {
- transform.cancelTransitions();
- cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- transform.cancelTransitions();
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- dispatchCameraIdle();
- }
- });
- return animator;
- }
-
- /**
- * Zoom in by 1.
- *
- * @param zoomFocalPoint focal point of zoom animation
- * @param runImmediately if true, animation will be started right away, otherwise it will wait until
- * {@link MotionEvent#ACTION_UP} is registered.
- */
- void zoomInAnimated(@NonNull PointF zoomFocalPoint, boolean runImmediately) {
- zoomAnimated(true, zoomFocalPoint, runImmediately);
- }
-
- /**
- * Zoom out by 1.
- *
- * @param zoomFocalPoint focal point of zoom animation
- * @param runImmediately if true, animation will be started right away, otherwise it will wait until
- * {@link MotionEvent#ACTION_UP} is registered.
- */
- void zoomOutAnimated(@NonNull PointF zoomFocalPoint, boolean runImmediately) {
- zoomAnimated(false, zoomFocalPoint, runImmediately);
- }
-
- private void zoomAnimated(boolean zoomIn, @NonNull PointF zoomFocalPoint, boolean runImmediately) {
- //canceling here as well, because when using a button it will not be canceled automatically by onDown()
- cancelAnimator(scaleAnimator);
-
- double currentZoom = transform.getRawZoom();
- scaleAnimator = createScaleAnimator(
- currentZoom,
- zoomIn ? 1 : -1,
- zoomFocalPoint,
- MapboxConstants.ANIMATION_DURATION);
- if (runImmediately) {
- scaleAnimator.start();
- } else {
- scheduleAnimator(scaleAnimator);
- }
- }
-
- private void dispatchCameraIdle() {
- // we need to dispatch camera idle callback only if there is no other gestures in progress
- if (noGesturesInProgress()) {
- // invalidate the camera position, so that it's valid when fetched from the #onIdle event
- // and doesn't rely on the last frame being rendered
- transform.invalidateCameraPosition();
- cameraChangeDispatcher.onCameraIdle();
- }
- }
-
- private void cancelTransitionsIfRequired() {
- // we need to cancel core transitions only if there is no started gesture yet
- if (noGesturesInProgress()) {
- transform.cancelTransitions();
- }
- }
-
- private boolean noGesturesInProgress() {
- return (!uiSettings.isScrollGesturesEnabled() || !gesturesManager.getMoveGestureDetector().isInProgress())
- && (!uiSettings.isZoomGesturesEnabled() || !gesturesManager.getStandardScaleGestureDetector().isInProgress())
- && (!uiSettings.isRotateGesturesEnabled() || !gesturesManager.getRotateGestureDetector().isInProgress())
- && (!uiSettings.isTiltGesturesEnabled() || !gesturesManager.getShoveGestureDetector().isInProgress());
- }
-
- void notifyOnMapClickListeners(@NonNull PointF tapPoint) {
- for (MapboxMap.OnMapClickListener listener : onMapClickListenerList) {
- if (listener.onMapClick(projection.fromScreenLocation(tapPoint))) {
- return;
- }
- }
- }
-
- void notifyOnMapLongClickListeners(@NonNull PointF longClickPoint) {
- for (MapboxMap.OnMapLongClickListener listener : onMapLongClickListenerList) {
- if (listener.onMapLongClick(projection.fromScreenLocation(longClickPoint))) {
- return;
- }
- }
- }
-
- void notifyOnFlingListeners() {
- for (MapboxMap.OnFlingListener listener : onFlingListenerList) {
- listener.onFling();
- }
- }
-
- void notifyOnMoveBeginListeners(@NonNull MoveGestureDetector detector) {
- for (MapboxMap.OnMoveListener listener : onMoveListenerList) {
- listener.onMoveBegin(detector);
- }
- }
-
- void notifyOnMoveListeners(@NonNull MoveGestureDetector detector) {
- for (MapboxMap.OnMoveListener listener : onMoveListenerList) {
- listener.onMove(detector);
- }
- }
-
- void notifyOnMoveEndListeners(@NonNull MoveGestureDetector detector) {
- for (MapboxMap.OnMoveListener listener : onMoveListenerList) {
- listener.onMoveEnd(detector);
- }
- }
-
- void notifyOnRotateBeginListeners(@NonNull RotateGestureDetector detector) {
- for (MapboxMap.OnRotateListener listener : onRotateListenerList) {
- listener.onRotateBegin(detector);
- }
- }
-
- void notifyOnRotateListeners(@NonNull RotateGestureDetector detector) {
- for (MapboxMap.OnRotateListener listener : onRotateListenerList) {
- listener.onRotate(detector);
- }
- }
-
- void notifyOnRotateEndListeners(@NonNull RotateGestureDetector detector) {
- for (MapboxMap.OnRotateListener listener : onRotateListenerList) {
- listener.onRotateEnd(detector);
- }
- }
-
- void notifyOnScaleBeginListeners(@NonNull StandardScaleGestureDetector detector) {
- for (MapboxMap.OnScaleListener listener : onScaleListenerList) {
- listener.onScaleBegin(detector);
- }
- }
-
- void notifyOnScaleListeners(@NonNull StandardScaleGestureDetector detector) {
- for (MapboxMap.OnScaleListener listener : onScaleListenerList) {
- listener.onScale(detector);
- }
- }
-
- void notifyOnScaleEndListeners(@NonNull StandardScaleGestureDetector detector) {
- for (MapboxMap.OnScaleListener listener : onScaleListenerList) {
- listener.onScaleEnd(detector);
- }
- }
-
- void notifyOnShoveBeginListeners(@NonNull ShoveGestureDetector detector) {
- for (MapboxMap.OnShoveListener listener : onShoveListenerList) {
- listener.onShoveBegin(detector);
- }
- }
-
- void notifyOnShoveListeners(@NonNull ShoveGestureDetector detector) {
- for (MapboxMap.OnShoveListener listener : onShoveListenerList) {
- listener.onShove(detector);
- }
- }
-
- void notifyOnShoveEndListeners(@NonNull ShoveGestureDetector detector) {
- for (MapboxMap.OnShoveListener listener : onShoveListenerList) {
- listener.onShoveEnd(detector);
- }
- }
-
- 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 addOnMoveListener(MapboxMap.OnMoveListener listener) {
- onMoveListenerList.add(listener);
- }
-
- void removeOnMoveListener(MapboxMap.OnMoveListener listener) {
- onMoveListenerList.remove(listener);
- }
-
- void addOnRotateListener(MapboxMap.OnRotateListener listener) {
- onRotateListenerList.add(listener);
- }
-
- void removeOnRotateListener(MapboxMap.OnRotateListener listener) {
- onRotateListenerList.remove(listener);
- }
-
- void addOnScaleListener(MapboxMap.OnScaleListener listener) {
- onScaleListenerList.add(listener);
- }
-
- void removeOnScaleListener(MapboxMap.OnScaleListener listener) {
- onScaleListenerList.remove(listener);
- }
-
- void addShoveListener(MapboxMap.OnShoveListener listener) {
- onShoveListenerList.add(listener);
- }
-
- void removeShoveListener(MapboxMap.OnShoveListener listener) {
- onShoveListenerList.remove(listener);
- }
-
- AndroidGesturesManager getGesturesManager() {
- return gesturesManager;
- }
-
- void setGesturesManager(@NonNull Context context, @NonNull AndroidGesturesManager gesturesManager,
- boolean attachDefaultListeners, boolean setDefaultMutuallyExclusives) {
- initializeGesturesManager(gesturesManager, setDefaultMutuallyExclusives);
- initializeGestureListeners(context, attachDefaultListeners);
- }
-} \ No newline at end of file