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.java1096
1 files changed, 558 insertions, 538 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 8047e19809..5f5a10d0d0 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
@@ -5,26 +5,31 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.PointF;
+import android.os.Handler;
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;
-import com.almeros.android.multitouch.gesturedetectors.ShoveGestureDetector;
-import com.almeros.android.multitouch.gesturedetectors.TwoFingerGestureDetector;
+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.android.telemetry.Event;
import com.mapbox.android.telemetry.MapEventFactory;
import com.mapbox.android.telemetry.MapState;
+import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.geometry.LatLng;
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.maps.MapboxMap.OnCameraMoveStartedListener.REASON_API_ANIMATION;
@@ -32,24 +37,15 @@ import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener.RE
/**
* Manages gestures events on a MapView.
- * <p>
- * Relies on gesture detection code in almeros.android.multitouch.gesturedetectors.
- * </p>
*/
final class MapGestureDetector {
private final Transform transform;
private final Projection projection;
private final UiSettings uiSettings;
- private final TrackingSettings trackingSettings;
private final AnnotationManager annotationManager;
private final CameraChangeDispatcher cameraChangeDispatcher;
- private GestureDetectorCompat gestureDetector;
- private ScaleGestureDetector scaleGestureDetector;
- private RotateGestureDetector rotateGestureDetector;
- private ShoveGestureDetector shoveGestureDetector;
-
// deprecated map touch API
private MapboxMap.OnMapClickListener onMapClickListener;
private MapboxMap.OnMapLongClickListener onMapLongClickListener;
@@ -69,43 +65,73 @@ final class MapGestureDetector {
private final CopyOnWriteArrayList<MapboxMap.OnScrollListener> onScrollListenerList
= new CopyOnWriteArrayList<>();
- private PointF focalPoint;
+ private final CopyOnWriteArrayList<MapboxMap.OnMoveListener> onMoveListenerList
+ = new CopyOnWriteArrayList<>();
- private boolean twoTap;
- private boolean quickZoom;
- private boolean tiltGestureOccurred;
- private boolean scrollGestureOccurred;
+ 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.
+ */
+ private PointF focalPoint;
- private boolean scaleGestureOccurred;
- private boolean recentScaleGestureOccurred;
- private long scaleBeginTime;
+ private AndroidGesturesManager gesturesManager;
+ private boolean executeDoubleTap;
- private boolean scaleAnimating;
- private boolean rotateAnimating;
+ private Animator scaleAnimator;
+ private Animator rotateAnimator;
+ private final List<Animator> scheduledAnimators = new ArrayList<>();
- private VelocityTracker velocityTracker;
- private boolean wasZoomingIn;
- private boolean wasClockwiseRotating;
- private boolean rotateGestureOccurred;
+ /**
+ * Cancels scheduled velocity animations if user doesn't lift fingers within
+ * {@link MapboxConstants#SCHEDULED_ANIMATION_TIMEOUT}
+ */
+ private Handler animationsTimeoutHandler = new Handler();
MapGestureDetector(Context context, Transform transform, Projection projection, UiSettings uiSettings,
- TrackingSettings trackingSettings, AnnotationManager annotationManager,
- CameraChangeDispatcher cameraChangeDispatcher) {
+ AnnotationManager annotationManager, CameraChangeDispatcher cameraChangeDispatcher) {
this.annotationManager = annotationManager;
this.transform = transform;
this.projection = projection;
this.uiSettings = uiSettings;
- this.trackingSettings = trackingSettings;
this.cameraChangeDispatcher = cameraChangeDispatcher;
- // Touch gesture detectors
+ // Checking for context != null for testing purposes
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());
+ gesturesManager = new AndroidGesturesManager(context);
+
+ 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);
+
+ gesturesManager.setMutuallyExclusiveGestures(shoveScaleSet, shoveRotateSet, ScaleLongPressSet);
+
+ gesturesManager.setStandardGestureListener(new StandardGestureListener());
+ gesturesManager.setMoveGestureListener(new MoveGestureListener());
+ gesturesManager.setStandardScaleGestureListener(new ScaleGestureListener(
+ context.getResources().getDimension(R.dimen.mapbox_minimum_scale_velocity)
+ ));
+ gesturesManager.setRotateGestureListener(new RotateGestureListener(
+ context.getResources().getDimension(R.dimen.mapbox_minimum_scale_span_when_rotating),
+ context.getResources().getDimension(R.dimen.mapbox_minimum_angular_velocity)
+ ));
+ gesturesManager.setShoveGestureListener(new ShoveGestureListener());
+ gesturesManager.setMultiFingerTapGestureListener(new TapGestureListener());
}
}
@@ -132,8 +158,9 @@ final class MapGestureDetector {
/**
* Get the current active gesture focal point.
* <p>
- * This could be either the user provided focal point in {@link UiSettings#setFocalPoint(PointF)} or the focal point
- * defined as a result of {@link TrackingSettings#setMyLocationEnabled(boolean)}.
+ * This could be either the user provided focal point in
+ * {@link UiSettings#setFocalPoint(PointF)}or <code>null</code>.
+ * If it's <code>null</code>, gestures will use focal pointed returned by the detector.
* </p>
*
* @return the current active gesture focal point.
@@ -149,118 +176,79 @@ final class MapGestureDetector {
* Forwards event to the related gesture detectors.
* </p>
*
- * @param event the MotionEvent
+ * @param motionEvent the MotionEvent
* @return True if touch event is handled
*/
- boolean onTouchEvent(MotionEvent event) {
- // framework can return null motion events in edge cases #9432
- if (event == null) {
+ boolean onTouchEvent(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 ((event.getButtonState() != 0) && (event.getButtonState() != MotionEvent.BUTTON_PRIMARY)) {
+ if ((motionEvent.getButtonState() != 0) && (motionEvent.getButtonState() != MotionEvent.BUTTON_PRIMARY)) {
return false;
}
- // Check two finger gestures first
- scaleGestureDetector.onTouchEvent(event);
- rotateGestureDetector.onTouchEvent(event);
- shoveGestureDetector.onTouchEvent(event);
+ boolean result = gesturesManager.onTouchEvent(motionEvent);
- // Handle two finger tap
- switch (event.getActionMasked()) {
+ switch (motionEvent.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
- recentScaleGestureOccurred = false;
+ cancelAnimators();
transform.setGestureInProgress(true);
break;
+ case MotionEvent.ACTION_UP:
+ transform.setGestureInProgress(false);
- case MotionEvent.ACTION_POINTER_DOWN:
- // Second pointer down
- twoTap = event.getPointerCount() == 2
- && uiSettings.isZoomGesturesEnabled();
- if (twoTap) {
- // Confirmed 2nd Finger Down
- if (isZoomValid(transform)) {
- MapEventFactory mapEventFactory = new MapEventFactory();
- LatLng latLng = projection.fromScreenLocation(new PointF(event.getX(), event.getY()));
- MapState twoFingerTap = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom());
- twoFingerTap.setGesture(Events.TWO_FINGER_TAP);
- Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, twoFingerTap));
- }
+ // Start all awaiting velocity animations
+ animationsTimeoutHandler.removeCallbacksAndMessages(null);
+ for (Animator animator : scheduledAnimators) {
+ animator.start();
}
+ scheduledAnimators.clear();
break;
- case MotionEvent.ACTION_POINTER_UP:
- // Second pointer up
+ case MotionEvent.ACTION_CANCEL:
+ scheduledAnimators.clear();
+ transform.setGestureInProgress(false);
break;
+ }
- case MotionEvent.ACTION_UP:
- // First pointer up
- long tapInterval = event.getEventTime() - event.getDownTime();
- boolean isTap = tapInterval <= ViewConfiguration.getTapTimeout();
- boolean inProgress = rotateGestureDetector.isInProgress()
- || scaleGestureDetector.isInProgress()
- || shoveGestureDetector.isInProgress();
-
- if (twoTap && isTap && !inProgress) {
- if (focalPoint != null) {
- transform.zoom(false, focalPoint);
- } else {
- PointF focalPoint = TwoFingerGestureDetector.determineFocalPoint(event);
- transform.zoom(false, focalPoint);
- }
- twoTap = false;
- return true;
- }
-
- // Scroll / Pan Has Stopped
- if (scrollGestureOccurred) {
- if (isZoomValid(transform)) {
- MapEventFactory mapEventFactory = new MapEventFactory();
- LatLng latLng = projection.fromScreenLocation(new PointF(event.getX(), event.getY()));
- MapState dragend = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom());
- Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_DRAGEND, dragend));
- }
- scrollGestureOccurred = false;
+ return result;
+ }
- if (!scaleAnimating && !rotateAnimating) {
- cameraChangeDispatcher.onCameraIdle();
- }
- }
+ void cancelAnimators() {
+ if (scaleAnimator != null) {
+ scaleAnimator.cancel();
+ }
+ if (rotateAnimator != null) {
+ rotateAnimator.cancel();
+ }
- twoTap = false;
- transform.setGestureInProgress(false);
- if (velocityTracker != null) {
- velocityTracker.recycle();
- }
- velocityTracker = null;
- break;
+ animationsTimeoutHandler.removeCallbacksAndMessages(null);
+ scheduledAnimators.clear();
+ }
- 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;
+ /**
+ * Posted on main thread with {@link #animationsTimeoutHandler}. Cancels all scheduled animators if needed.
+ */
+ private Runnable cancelAnimatorsRunnable = new Runnable() {
+ @Override
+ public void run() {
+ cancelAnimators();
}
+ };
- return gestureDetector.onTouchEvent(event);
+ /**
+ * Schedules a velocity animator to be executed when user lift 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);
}
/**
@@ -269,7 +257,7 @@ final class MapGestureDetector {
* Examples of such events are mouse scroll events, mouse moves, joystick & trackpad.
* </p>
*
- * @param event The MotionEvent occured
+ * @param event The MotionEvent occurred
* @return True is the event is handled
*/
boolean onGenericMotionEvent(MotionEvent event) {
@@ -291,7 +279,7 @@ final class MapGestureDetector {
float scrollDist = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
// Scale the map by the appropriate power of two factor
- transform.zoomBy(scrollDist, event.getX(), event.getY());
+ transform.zoomBy(scrollDist, new PointF(event.getX(), event.getY()));
return true;
@@ -305,61 +293,14 @@ final class MapGestureDetector {
return false;
}
- /**
- * Responsible for handling one finger gestures.
- */
- private class GestureListener extends android.view.GestureDetector.SimpleOnGestureListener {
-
+ private final class StandardGestureListener extends StandardGestureDetector.SimpleStandardOnGestureListener {
@Override
- public boolean onDown(MotionEvent event) {
- return true;
- }
-
- @Override
- public boolean onDoubleTapEvent(MotionEvent e) {
- if (!uiSettings.isZoomGesturesEnabled() || !uiSettings.isDoubleTapGesturesEnabled()) {
- return false;
- }
-
- switch (e.getAction()) {
- case MotionEvent.ACTION_DOWN:
- break;
- case MotionEvent.ACTION_MOVE:
- break;
- case MotionEvent.ACTION_UP:
- if (quickZoom) {
- cameraChangeDispatcher.onCameraIdle();
- quickZoom = false;
- break;
- }
-
- // notify camera change listener
- cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
-
- // Single finger double tap
- if (focalPoint != null) {
- // User provided focal point
- transform.zoom(true, focalPoint);
- } else {
- // Zoom in on gesture
- transform.zoom(true, new PointF(e.getX(), e.getY()));
- }
- if (isZoomValid(transform)) {
- MapEventFactory mapEventFactory = new MapEventFactory();
- LatLng latLng = projection.fromScreenLocation(new PointF(e.getX(), e.getY()));
- MapState doubleTap = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom());
- doubleTap.setGesture(Events.DOUBLE_TAP);
- Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, doubleTap));
- }
- break;
- }
-
+ public boolean onDown(MotionEvent motionEvent) {
return true;
}
@Override
public boolean onSingleTapUp(MotionEvent motionEvent) {
- // Cancel any animation
transform.cancelTransitions();
return true;
}
@@ -378,31 +319,51 @@ final class MapGestureDetector {
notifyOnMapClickListeners(tapPoint);
}
- if (isZoomValid(transform)) {
- MapEventFactory mapEventFactory = new MapEventFactory();
- LatLng latLng = projection.fromScreenLocation(new PointF(motionEvent.getX(), motionEvent.getY()));
- MapState singleTap = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom());
- singleTap.setGesture(Events.SINGLE_TAP);
- Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, singleTap));
- }
+ sendTelemetryEvent(Events.SINGLE_TAP, new PointF(motionEvent.getX(), motionEvent.getY()));
return true;
}
@Override
- public void onLongPress(MotionEvent motionEvent) {
- PointF longClickPoint = new PointF(motionEvent.getX(), motionEvent.getY());
+ public boolean onDoubleTapEvent(MotionEvent motionEvent) {
+ int action = motionEvent.getActionMasked();
+ if (action == MotionEvent.ACTION_DOWN) {
+ executeDoubleTap = true;
+ }
+ if (motionEvent.getActionMasked() == MotionEvent.ACTION_UP) {
+ if (!uiSettings.isZoomGesturesEnabled() || !uiSettings.isDoubleTapGesturesEnabled() || !executeDoubleTap) {
+ return false;
+ }
+
+ transform.cancelTransitions();
+ cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
- if (!quickZoom) {
- notifyOnMapLongClickListeners(longClickPoint);
+ // Single finger double tap
+ if (focalPoint != null) {
+ // User provided focal point
+ transform.zoomIn(focalPoint);
+ } else {
+ // Zoom in on gesture
+ transform.zoomIn(new PointF(motionEvent.getX(), motionEvent.getY()));
+ }
+
+ sendTelemetryEvent(Events.DOUBLE_TAP, new PointF(motionEvent.getX(), motionEvent.getY()));
+
+ 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 ((!trackingSettings.isScrollGestureCurrentlyEnabled()) || recentScaleGestureOccurred) {
+ if ((!uiSettings.isScrollGesturesEnabled())) {
// don't allow a fling is scroll is disabled
- // and ignore when a scale gesture has occurred
return false;
}
@@ -415,11 +376,7 @@ final class MapGestureDetector {
return false;
}
- trackingSettings.resetTrackingModesIfRequired(true, false, false);
-
- // cancel any animation
transform.cancelTransitions();
-
cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
// tilt results in a bigger translation, limiting input for #5281
@@ -435,230 +392,149 @@ final class MapGestureDetector {
transform.moveBy(offsetX, offsetY, animationTime);
notifyOnFlingListeners();
+
return true;
}
+ }
- // Called for drags
+ private final class MoveGestureListener extends MoveGestureDetector.SimpleOnMoveGestureListener {
@Override
- public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
- if (!trackingSettings.isScrollGestureCurrentlyEnabled()) {
+ public boolean onMoveBegin(MoveGestureDetector detector) {
+ if (!uiSettings.isScrollGesturesEnabled()) {
return false;
}
- if (tiltGestureOccurred) {
- return false;
- }
+ transform.cancelTransitions();
+ cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
- if (!scrollGestureOccurred) {
- scrollGestureOccurred = true;
+ sendTelemetryEvent(Events.PAN, detector.getFocalPoint());
- // Cancel any animation
- if (!scaleGestureOccurred) {
- transform.cancelTransitions();
- cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
- }
+ notifyOnMoveBeginListeners(detector);
- if (isZoomValid(transform)) {
- MapEventFactory mapEventFactory = new MapEventFactory();
- LatLng latLng = projection.fromScreenLocation(new PointF(e1.getX(), e1.getY()));
- MapState pan = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom());
- pan.setGesture(Events.PAN);
- Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, pan));
- }
- }
+ return true;
+ }
- // reset tracking if needed
- trackingSettings.resetTrackingModesIfRequired(true, false, false);
+ @Override
+ public boolean onMove(MoveGestureDetector detector, float distanceX, float distanceY) {
+ // dispatching start even once more if another detector ended, and this one didn't
+ cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE);
// Scroll the map
transform.moveBy(-distanceX, -distanceY, 0 /*no duration*/);
notifyOnScrollListeners();
+ notifyOnMoveListeners(detector);
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));
+ @Override
+ public void onMoveEnd(MoveGestureDetector detector, float velocityX, float velocityY) {
+ cameraChangeDispatcher.onCameraIdle();
+ notifyOnMoveEndListeners(detector);
}
}
- void notifyOnMapLongClickListeners(PointF longClickPoint) {
- // deprecated API
- if (onMapLongClickListener != null) {
- onMapLongClickListener.onMapLongClick(projection.fromScreenLocation(longClickPoint));
- }
+ private final class ScaleGestureListener extends StandardScaleGestureDetector.SimpleStandardOnScaleGestureListener {
- // new API
- for (MapboxMap.OnMapLongClickListener listener : onMapLongClickListenerList) {
- listener.onMapLongClick(projection.fromScreenLocation(longClickPoint));
- }
- }
+ private final float minimumVelocity;
- void notifyOnFlingListeners() {
- // deprecated API
- if (onFlingListener != null) {
- onFlingListener.onFling();
- }
+ private PointF scaleFocalPoint;
+ private boolean quickZoom;
- // new API
- for (MapboxMap.OnFlingListener listener : onFlingListenerList) {
- listener.onFling();
+ ScaleGestureListener(float minimumVelocity) {
+ this.minimumVelocity = minimumVelocity;
}
- }
- 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 {
-
- 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
- public boolean onScaleBegin(ScaleGestureDetector detector) {
+ public boolean onScaleBegin(StandardScaleGestureDetector detector) {
if (!uiSettings.isZoomGesturesEnabled()) {
return false;
}
- recentScaleGestureOccurred = true;
- scalePointBegin = new PointF(detector.getFocusX(), detector.getFocusY());
- scaleBeginTime = detector.getEventTime();
- if (isZoomValid(transform)) {
- MapEventFactory mapEventFactory = new MapEventFactory();
- LatLng latLng = projection.fromScreenLocation(new PointF(detector.getFocusX(), detector.getFocusY()));
- MapState pinch = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom());
- pinch.setGesture(Events.PINCH);
- Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, pinch));
+ transform.cancelTransitions();
+ cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
+
+ quickZoom = detector.getPointersCount() == 1;
+ if (quickZoom) {
+ // when quickzoom, dismiss double tap and disable move gesture
+ executeDoubleTap = false;
+ gesturesManager.getMoveGestureDetector().setEnabled(false);
}
+
+ // increase rotate angle threshold when scale is detected first
+ gesturesManager.getRotateGestureDetector().setAngleThreshold(
+ gesturesManager.getRotateGestureDetector().getDefaultAngleThreshold()
+ + MapboxConstants.ROTATION_THRESHOLD_INCREASE_WHEN_SCALING
+ );
+
+ // setting focalPoint in #onScaleBegin() as well, because #onScale() might not get called before #onScaleEnd()
+ setScaleFocalPoint(detector);
+
+ sendTelemetryEvent(Events.PINCH, scaleFocalPoint);
+
+ notifyOnScaleBeginListeners(detector);
+
return true;
}
- // Called each time a finger moves
- // Called for pinch zooms and quickzooms/quickscales
@Override
- public boolean onScale(ScaleGestureDetector detector) {
- if (!uiSettings.isZoomGesturesEnabled()) {
- return super.onScale(detector);
- }
+ public boolean onScale(StandardScaleGestureDetector detector) {
+ // dispatching start even once more if another detector ended, and this one didn't
+ cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE);
- 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 - scaleBeginTime;
- if (!scaleGestureOccurred && (interval <= ViewConfiguration.getTapTimeout())) {
- 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;
- }
+ setScaleFocalPoint(detector);
- if (!scaleGestureOccurred) {
- return false;
- }
+ float scaleFactor = detector.getScaleFactor();
+ double zoomBy = getNewZoom(scaleFactor, quickZoom);
+ transform.zoomBy(zoomBy, scaleFocalPoint);
- // Gesture is a quickzoom if there aren't two fingers
- if (!quickZoom && !twoTap) {
- cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
- }
- quickZoom = !twoTap;
+ notifyOnScaleListeners(detector);
- // 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(Math.PI / 2), focalPoint.x, focalPoint.y);
- } else if (quickZoom) {
- cameraChangeDispatcher.onCameraMove();
- // clamp scale factors we feed to core #7514
- 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);
- transform.zoomBy(negative ? -zoomBy : zoomBy, uiSettings.getWidth() / 2, uiSettings.getHeight() / 2);
- recentScaleGestureOccurred = true;
- } else {
- // around gesture
- 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;
- }
+ public void onScaleEnd(StandardScaleGestureDetector detector, float velocityX, float velocityY) {
+ cameraChangeDispatcher.onCameraIdle();
- if (rotateGestureOccurred || quickZoom) {
- reset();
- return;
+ if (quickZoom) {
+ //if quickzoom, re-enabling move gesture detector
+ gesturesManager.getMoveGestureDetector().setEnabled(true);
}
- double velocityXY = Math.abs(velocityTracker.getYVelocity()) + Math.abs(velocityTracker.getXVelocity());
- if (velocityXY > MapboxConstants.VELOCITY_THRESHOLD_IGNORE_FLING / 2) {
- scaleAnimating = true;
- double zoomAddition = calculateScale(velocityXY);
+ // resetting default angle threshold
+ gesturesManager.getRotateGestureDetector().setAngleThreshold(
+ gesturesManager.getRotateGestureDetector().getDefaultAngleThreshold()
+ );
+
+ float velocityXY = Math.abs(velocityX) + Math.abs(velocityY);
+ if (velocityXY > minimumVelocity) {
+ double zoomAddition = calculateScale(velocityXY, detector.isScalingOut());
double currentZoom = transform.getRawZoom();
- long animationTime = (long) (Math.log(velocityXY) * ANIMATION_TIME_MULTIPLIER);
- createScaleAnimator(currentZoom, zoomAddition, animationTime).start();
- } else if (!scaleAnimating) {
- reset();
+ long animationTime = (long) (Math.abs(zoomAddition) * 1000 / 4);
+ scaleAnimator = createScaleAnimator(currentZoom, zoomAddition, animationTime);
+ scheduleAnimator(scaleAnimator);
}
+
+ notifyOnScaleEndListeners(detector);
}
- private void reset() {
- scaleAnimating = false;
- scaleGestureOccurred = false;
- scaleBeginTime = 0;
- scaleFactor = 1.0f;
- cameraChangeDispatcher.onCameraIdle();
+ private void setScaleFocalPoint(StandardScaleGestureDetector detector) {
+ if (focalPoint != null) {
+ // around user provided focal point
+ scaleFocalPoint = focalPoint;
+ } else if (quickZoom) {
+ // around center
+ scaleFocalPoint = new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2);
+ } else {
+ // around gesture
+ scaleFocalPoint = detector.getFocalPoint();
+ }
}
- private double calculateScale(double velocityXY) {
- double zoomAddition = (float) (Math.log(velocityXY) / ZOOM_DISTANCE_DIVIDER);
- if (!wasZoomingIn) {
+ private double calculateScale(double velocityXY, boolean isScalingOut) {
+ double zoomAddition = (float) Math.log(velocityXY / 1000 + 1);
+ if (isScalingOut) {
zoomAddition = -zoomAddition;
}
return zoomAddition;
@@ -667,272 +543,387 @@ final class MapGestureDetector {
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.setInterpolator(new DecelerateInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
- transform.setZoom((Float) animation.getAnimatedValue(), scalePointBegin, 0, true);
+ transform.setZoom((Float) animation.getAnimatedValue(), scaleFocalPoint, 0);
}
});
+
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
+ transform.cancelTransitions();
cameraChangeDispatcher.onCameraMoveStarted(REASON_API_ANIMATION);
}
@Override
public void onAnimationCancel(Animator animation) {
- reset();
+ transform.cancelTransitions();
}
@Override
public void onAnimationEnd(Animator animation) {
- reset();
+ cameraChangeDispatcher.onCameraIdle();
}
});
return animator;
}
- }
- /**
- * Responsible for handling rotation gestures.
- */
- private class RotateGestureListener extends RotateGestureDetector.SimpleOnRotateGestureListener {
+ private double getNewZoom(float scaleFactor, boolean quickZoom) {
+ double zoomBy = Math.log(scaleFactor) / Math.log(Math.PI / 2);
+ if (quickZoom) {
+ // clamp scale factors we feed to core #7514
+ boolean negative = zoomBy < 0;
+ zoomBy = MathUtils.clamp(Math.abs(zoomBy),
+ MapboxConstants.MINIMUM_SCALE_FACTOR_CLAMP,
+ MapboxConstants.MAXIMUM_SCALE_FACTOR_CLAMP);
+ return negative ? -zoomBy : zoomBy;
+ }
+ return zoomBy;
+ }
+ }
- 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 final class RotateGestureListener extends RotateGestureDetector.SimpleOnRotateGestureListener {
+ private PointF rotateFocalPoint;
+ private final float minimumScaleSpanWhenRotating;
+ private final float minimumAngularVelocity;
- private long beginTime = 0;
- private boolean started = false;
+ RotateGestureListener(float minimumScaleSpanWhenRotating, float minimumAngularVelocity) {
+ this.minimumScaleSpanWhenRotating = minimumScaleSpanWhenRotating;
+ this.minimumAngularVelocity = minimumAngularVelocity;
+ }
- // Called when two fingers first touch the screen
@Override
public boolean onRotateBegin(RotateGestureDetector detector) {
- if (!trackingSettings.isRotateGestureCurrentlyEnabled()) {
+ if (!uiSettings.isRotateGesturesEnabled()) {
return false;
}
- // notify camera change listener
+ transform.cancelTransitions();
cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
- beginTime = detector.getEventTime();
- return true;
- }
+ // when rotation starts, interrupting scale and increasing the threshold
+ // to make rotation without scaling easier
+ gesturesManager.getStandardScaleGestureDetector().setSpanSinceStartThreshold(minimumScaleSpanWhenRotating);
+ gesturesManager.getStandardScaleGestureDetector().interrupt();
- // Called each time one of the two fingers moves
- // Called for rotation
- @Override
- public boolean onRotate(RotateGestureDetector detector) {
- if (!trackingSettings.isRotateGestureCurrentlyEnabled() || tiltGestureOccurred) {
- return false;
- }
+ // setting in #onRotateBegin() as well, because #onRotate() might not get called before #onRotateEnd()
+ setRotateFocalPoint(detector);
- // If rotate is large enough ignore a tap
- // Also is zoom already started, don't rotate
- float angle = detector.getRotationDegreesDelta();
- if (Math.abs(angle) >= ROTATE_INVOKE_ANGLE) {
- if (isZoomValid(transform)) {
- MapEventFactory mapEventFactory = new MapEventFactory();
- LatLng latLng = projection.fromScreenLocation(new PointF(detector.getFocusX(), detector.getFocusY()));
- MapState rotation = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom());
- rotation.setGesture(Events.ROTATION);
- Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, rotation));
- }
- started = true;
- }
+ sendTelemetryEvent(Events.ROTATION, rotateFocalPoint);
- if (!started) {
- return false;
- }
+ notifyOnRotateBeginListeners(detector);
- wasClockwiseRotating = detector.getRotationDegreesDelta() > 0;
- if (scaleBeginTime != 0) {
- rotateGestureOccurred = true;
- }
+ return 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);
+ @Override
+ public boolean onRotate(RotateGestureDetector detector, float rotationDegreesSinceLast,
+ float rotationDegreesSinceFirst) {
+ // dispatching start even once more if another detector ended, and this one didn't
+ cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE);
+
+ setRotateFocalPoint(detector);
// Calculate map bearing value
- double bearing = transform.getRawBearing() + angle;
+ double bearing = transform.getRawBearing() + rotationDegreesSinceLast;
// Rotate the map
- if (focalPoint != null) {
- // User provided focal point
- transform.setBearing(bearing, focalPoint.x, focalPoint.y);
- } else {
- // around gesture
- transform.setBearing(bearing, detector.getFocusX(), detector.getFocusY());
- }
+ transform.setBearing(bearing, rotateFocalPoint.x, rotateFocalPoint.y);
+
+ notifyOnRotateListeners(detector);
+
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();
+ public void onRotateEnd(RotateGestureDetector detector, float velocityX, float velocityY, float angularVelocity) {
+ cameraChangeDispatcher.onCameraIdle();
+
+ // resetting default scale threshold values
+ gesturesManager.getStandardScaleGestureDetector().setSpanSinceStartThreshold(
+ gesturesManager.getStandardScaleGestureDetector().getDefaultSpanSinceStartThreshold());
+
+ if (Math.abs(angularVelocity) < minimumAngularVelocity) {
return;
}
- double angularVelocity = calculateVelocityVector(detector);
- if (Math.abs(angularVelocity) > 0.001 && rotateGestureOccurred && !rotateAnimating) {
- animateRotateVelocity();
- } else if (!rotateAnimating) {
- reset();
- }
- }
+ boolean negative = angularVelocity < 0;
+ angularVelocity = (float) Math.pow(angularVelocity, 2);
+ angularVelocity = MathUtils.clamp(
+ angularVelocity, MapboxConstants.MINIMUM_ANGULAR_VELOCITY, MapboxConstants.MAXIMUM_ANGULAR_VELOCITY);
- private void reset() {
- beginTime = 0;
- started = false;
- rotateAnimating = false;
- rotateGestureOccurred = false;
+ long animationTime = (long) (Math.log(angularVelocity + 1) * 500);
- if (!twoTap) {
- cameraChangeDispatcher.onCameraIdle();
+ if (negative) {
+ angularVelocity = -angularVelocity;
}
- }
- private void animateRotateVelocity() {
- rotateAnimating = true;
- double currentRotation = transform.getRawBearing();
- double rotateAdditionDegrees = calculateVelocityInDegrees();
- createAnimator(currentRotation, rotateAdditionDegrees).start();
- }
+ rotateAnimator = createRotateAnimator(angularVelocity, animationTime);
+ scheduleAnimator(rotateAnimator);
- private double calculateVelocityVector(RotateGestureDetector detector) {
- return ((detector.getFocusX() * velocityTracker.getYVelocity())
- + (detector.getFocusY() * velocityTracker.getXVelocity()))
- / (Math.pow(detector.getFocusX(), 2) + Math.pow(detector.getFocusY(), 2));
+ notifyOnRotateEndListeners(detector);
}
- 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;
+ private void setRotateFocalPoint(RotateGestureDetector detector) {
+ if (focalPoint != null) {
+ // User provided focal point
+ rotateFocalPoint = focalPoint;
+ } else {
+ // around gesture
+ rotateFocalPoint = detector.getFocalPoint();
}
-
- 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));
+ private Animator createRotateAnimator(float angularVelocity, long animationTime) {
+ ValueAnimator animator = ValueAnimator.ofFloat(angularVelocity, 0f);
+ animator.setDuration(animationTime);
+ animator.setInterpolator(new DecelerateInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
- transform.setBearing((Float) animation.getAnimatedValue());
+ transform.setBearing(
+ transform.getRawBearing() + (float) animation.getAnimatedValue(),
+ rotateFocalPoint.x, rotateFocalPoint.y,
+ 0L
+ );
}
});
+
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
+ transform.cancelTransitions();
cameraChangeDispatcher.onCameraMoveStarted(REASON_API_ANIMATION);
}
@Override
public void onAnimationCancel(Animator animation) {
- reset();
+ cameraChangeDispatcher.onCameraIdle();
}
@Override
public void onAnimationEnd(Animator animation) {
- reset();
+ cameraChangeDispatcher.onCameraIdle();
}
});
+
return animator;
}
}
- /**
- * Responsible for handling 2 finger shove gestures.
- */
- private class ShoveGestureListener implements ShoveGestureDetector.OnShoveGestureListener {
-
- private long beginTime = 0;
- private float totalDelta = 0.0f;
-
+ private final class ShoveGestureListener extends ShoveGestureDetector.SimpleOnShoveGestureListener {
@Override
public boolean onShoveBegin(ShoveGestureDetector detector) {
if (!uiSettings.isTiltGesturesEnabled()) {
return false;
}
- // notify camera change listener
+ transform.cancelTransitions();
cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
+
+ sendTelemetryEvent(Events.PITCH, detector.getFocalPoint());
+
+ // disabling move gesture during shove
+ gesturesManager.getMoveGestureDetector().setEnabled(false);
+
+ notifyOnShoveBeginListeners(detector);
+
return true;
}
@Override
- public void onShoveEnd(ShoveGestureDetector detector) {
- beginTime = 0;
- totalDelta = 0.0f;
- tiltGestureOccurred = false;
+ public boolean onShove(ShoveGestureDetector detector, float deltaPixelsSinceLast, float deltaPixelsSinceStart) {
+ // dispatching start even once more if another detector ended, and this one didn't
+ 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 boolean onShove(ShoveGestureDetector detector) {
- if (!uiSettings.isTiltGesturesEnabled()) {
- return false;
- }
+ public void onShoveEnd(ShoveGestureDetector detector, float velocityX, float velocityY) {
+ cameraChangeDispatcher.onCameraIdle();
- // Ignore short touches in case it is a tap
- // Also ignore small tilt
- long time = detector.getEventTime();
- long interval = time - beginTime;
- if (!tiltGestureOccurred && (interval <= ViewConfiguration.getTapTimeout())) {
- return false;
- }
+ // re-enabling move gesture
+ gesturesManager.getMoveGestureDetector().setEnabled(true);
- // 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();
- if (isZoomValid(transform)) {
- MapEventFactory mapEventFactory = new MapEventFactory();
- LatLng latLng = projection.fromScreenLocation(new PointF(detector.getFocusX(), detector.getFocusY()));
- MapState pitch = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom());
- pitch.setGesture(Events.PITCH);
- Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, pitch));
- }
- }
+ notifyOnShoveEndListeners(detector);
+ }
+ }
- if (!tiltGestureOccurred) {
+ private final class TapGestureListener implements MultiFingerTapGestureDetector.OnMultiFingerTapGestureListener {
+ @Override
+ public boolean onMultiFingerTap(MultiFingerTapGestureDetector detector, int pointersCount) {
+ if (!uiSettings.isZoomGesturesEnabled() || pointersCount != 2) {
return false;
}
- // Get tilt value (scale and clamp)
- double pitch = transform.getTilt();
- pitch -= 0.1 * detector.getShovePixelsDelta();
- pitch = Math.max(MapboxConstants.MINIMUM_TILT, Math.min(MapboxConstants.MAXIMUM_TILT, pitch));
+ transform.cancelTransitions();
+ cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
+
+ if (focalPoint != null) {
+ transform.zoomOut(focalPoint);
+ } else {
+ transform.zoomOut(detector.getFocalPoint());
+ }
- // Tilt the map
- transform.setTilt(pitch);
return true;
}
}
+ private void sendTelemetryEvent(String eventType, PointF focalPoint) {
+ if (isZoomValid(transform)) {
+ MapEventFactory mapEventFactory = new MapEventFactory();
+ LatLng latLng = projection.fromScreenLocation(focalPoint);
+ MapState state = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom());
+ state.setGesture(eventType);
+ Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, state));
+ }
+ }
+
+ private boolean isZoomValid(Transform transform) {
+ if (transform == null) {
+ return false;
+ }
+ double mapZoom = transform.getZoom();
+ return mapZoom >= MapboxConstants.MINIMUM_ZOOM && mapZoom <= MapboxConstants.MAXIMUM_ZOOM;
+ }
+
+ 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();
+ }
+ }
+
+ void notifyOnMoveBeginListeners(MoveGestureDetector detector) {
+ for (MapboxMap.OnMoveListener listener : onMoveListenerList) {
+ listener.onMoveBegin(detector);
+ }
+ }
+
+ void notifyOnMoveListeners(MoveGestureDetector detector) {
+ for (MapboxMap.OnMoveListener listener : onMoveListenerList) {
+ listener.onMove(detector);
+ }
+ }
+
+ void notifyOnMoveEndListeners(MoveGestureDetector detector) {
+ for (MapboxMap.OnMoveListener listener : onMoveListenerList) {
+ listener.onMoveEnd(detector);
+ }
+ }
+
+ void notifyOnRotateBeginListeners(RotateGestureDetector detector) {
+ for (MapboxMap.OnRotateListener listener : onRotateListenerList) {
+ listener.onRotateBegin(detector);
+ }
+ }
+
+ void notifyOnRotateListeners(RotateGestureDetector detector) {
+ for (MapboxMap.OnRotateListener listener : onRotateListenerList) {
+ listener.onRotate(detector);
+ }
+ }
+
+ void notifyOnRotateEndListeners(RotateGestureDetector detector) {
+ for (MapboxMap.OnRotateListener listener : onRotateListenerList) {
+ listener.onRotateEnd(detector);
+ }
+ }
+
+ void notifyOnScaleBeginListeners(StandardScaleGestureDetector detector) {
+ for (MapboxMap.OnScaleListener listener : onScaleListenerList) {
+ listener.onScaleBegin(detector);
+ }
+ }
+
+ void notifyOnScaleListeners(StandardScaleGestureDetector detector) {
+ for (MapboxMap.OnScaleListener listener : onScaleListenerList) {
+ listener.onScale(detector);
+ }
+ }
+
+ void notifyOnScaleEndListeners(StandardScaleGestureDetector detector) {
+ for (MapboxMap.OnScaleListener listener : onScaleListenerList) {
+ listener.onScaleEnd(detector);
+ }
+ }
+
+ void notifyOnShoveBeginListeners(ShoveGestureDetector detector) {
+ for (MapboxMap.OnShoveListener listener : onShoveListenerList) {
+ listener.onShoveBegin(detector);
+ }
+ }
+
+ void notifyOnShoveListeners(ShoveGestureDetector detector) {
+ for (MapboxMap.OnShoveListener listener : onShoveListenerList) {
+ listener.onShove(detector);
+ }
+ }
+
+ void notifyOnShoveEndListeners(ShoveGestureDetector detector) {
+ for (MapboxMap.OnShoveListener listener : onShoveListenerList) {
+ listener.onShoveEnd(detector);
+ }
+ }
+
void setOnMapClickListener(MapboxMap.OnMapClickListener onMapClickListener) {
this.onMapClickListener = onMapClickListener;
}
@@ -981,14 +972,43 @@ final class MapGestureDetector {
onScrollListenerList.remove(onScrollListener);
}
- private boolean isZoomValid(Transform transform) {
- if (transform == null) {
- return false;
- }
- double mapZoom = transform.getZoom();
- if (mapZoom < MapboxConstants.MINIMUM_ZOOM || mapZoom > MapboxConstants.MAXIMUM_ZOOM) {
- return false;
- }
- return true;
+ 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(AndroidGesturesManager gesturesManager) {
+ this.gesturesManager = gesturesManager;
}
-}
+} \ No newline at end of file