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.java286
1 files changed, 193 insertions, 93 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 5f5a10d0d0..de9b4fdbc2 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
@@ -12,6 +12,7 @@ import android.view.MotionEvent;
import android.view.animation.DecelerateInterpolator;
import com.mapbox.android.gestures.AndroidGesturesManager;
+import com.mapbox.android.gestures.Constants;
import com.mapbox.android.gestures.MoveGestureDetector;
import com.mapbox.android.gestures.MultiFingerTapGestureDetector;
import com.mapbox.android.gestures.RotateGestureDetector;
@@ -105,8 +106,40 @@ final class MapGestureDetector {
// Checking for context != null for testing purposes
if (context != null) {
- gesturesManager = new AndroidGesturesManager(context);
+ // Initialize gestures manager
+ AndroidGesturesManager androidGesturesManager = new AndroidGesturesManager(context);
+ initializeGesturesManager(androidGesturesManager, true);
+ // Initialize gesture listeners
+ initializeGestureListeners(context, true);
+ }
+ }
+
+ private void initializeGestureListeners(Context context, boolean attachDefaultListeners) {
+ if (attachDefaultListeners) {
+ StandardGestureListener standardGestureListener = new StandardGestureListener();
+ MoveGestureListener moveGestureListener = new MoveGestureListener();
+ ScaleGestureListener scaleGestureListener = new ScaleGestureListener(
+ 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_minimum_angular_velocity),
+ context.getResources().getDimension(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(AndroidGesturesManager androidGesturesManager,
+ boolean setDefaultMutuallyExclusives) {
+ if (setDefaultMutuallyExclusives) {
Set<Integer> shoveScaleSet = new HashSet<>();
shoveScaleSet.add(AndroidGesturesManager.GESTURE_TYPE_SHOVE);
shoveScaleSet.add(AndroidGesturesManager.GESTURE_TYPE_SCALE);
@@ -119,20 +152,10 @@ final class MapGestureDetector {
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());
+ androidGesturesManager.setMutuallyExclusiveGestures(shoveScaleSet, shoveRotateSet, ScaleLongPressSet);
}
+
+ gesturesManager = androidGesturesManager;
}
/**
@@ -218,15 +241,17 @@ final class MapGestureDetector {
}
void cancelAnimators() {
- if (scaleAnimator != null) {
- scaleAnimator.cancel();
- }
- if (rotateAnimator != null) {
- rotateAnimator.cancel();
- }
-
animationsTimeoutHandler.removeCallbacksAndMessages(null);
scheduledAnimators.clear();
+
+ cancelAnimator(scaleAnimator);
+ cancelAnimator(rotateAnimator);
+ }
+
+ private void cancelAnimator(Animator animator) {
+ if (animator != null && animator.isStarted()) {
+ animator.cancel();
+ }
}
/**
@@ -319,7 +344,7 @@ final class MapGestureDetector {
notifyOnMapClickListeners(tapPoint);
}
- sendTelemetryEvent(Events.SINGLE_TAP, new PointF(motionEvent.getX(), motionEvent.getY()));
+ sendTelemetryEvent(Telemetry.SINGLE_TAP, new PointF(motionEvent.getX(), motionEvent.getY()));
return true;
}
@@ -338,16 +363,19 @@ final class MapGestureDetector {
transform.cancelTransitions();
cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
+ PointF zoomFocalPoint;
// Single finger double tap
if (focalPoint != null) {
// User provided focal point
- transform.zoomIn(focalPoint);
+ zoomFocalPoint = focalPoint;
} else {
// Zoom in on gesture
- transform.zoomIn(new PointF(motionEvent.getX(), motionEvent.getY()));
+ zoomFocalPoint = new PointF(motionEvent.getX(), motionEvent.getY());
}
- sendTelemetryEvent(Events.DOUBLE_TAP, new PointF(motionEvent.getX(), motionEvent.getY()));
+ zoomInAnimated(zoomFocalPoint, false);
+
+ sendTelemetryEvent(Telemetry.DOUBLE_TAP, new PointF(motionEvent.getX(), motionEvent.getY()));
return true;
}
@@ -362,8 +390,14 @@ final class MapGestureDetector {
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
- if ((!uiSettings.isScrollGesturesEnabled())) {
- // don't allow a fling is scroll is disabled
+ if (!uiSettings.isScrollGesturesEnabled()) {
+ // don't allow a fling if scroll is disabled
+ return false;
+ }
+
+ notifyOnFlingListeners();
+
+ if (!uiSettings.isFlingVelocityAnimationEnabled()) {
return false;
}
@@ -391,8 +425,6 @@ final class MapGestureDetector {
// update transformation
transform.moveBy(offsetX, offsetY, animationTime);
- notifyOnFlingListeners();
-
return true;
}
}
@@ -407,7 +439,7 @@ final class MapGestureDetector {
transform.cancelTransitions();
cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
- sendTelemetryEvent(Events.PAN, detector.getFocalPoint());
+ sendTelemetryEvent(Telemetry.PAN, detector.getFocalPoint());
notifyOnMoveBeginListeners(detector);
@@ -461,16 +493,18 @@ final class MapGestureDetector {
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
- );
+ if (uiSettings.isIncreaseRotateThresholdWhenScaling()) {
+ // increase rotate angle threshold when scale is detected first
+ gesturesManager.getRotateGestureDetector().setAngleThreshold(
+ Constants.DEFAULT_ROTATE_ANGLE_THRESHOLD
+ + 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);
+ sendTelemetryEvent(Telemetry.PINCH, scaleFocalPoint);
notifyOnScaleBeginListeners(detector);
@@ -502,21 +536,27 @@ final class MapGestureDetector {
gesturesManager.getMoveGestureDetector().setEnabled(true);
}
- // resetting default angle threshold
- gesturesManager.getRotateGestureDetector().setAngleThreshold(
- gesturesManager.getRotateGestureDetector().getDefaultAngleThreshold()
- );
+ if (uiSettings.isIncreaseRotateThresholdWhenScaling()) {
+ // resetting default angle threshold
+ gesturesManager.getRotateGestureDetector().setAngleThreshold(
+ Constants.DEFAULT_ROTATE_ANGLE_THRESHOLD
+ );
+ }
+
+ notifyOnScaleEndListeners(detector);
+
+ if (!uiSettings.isScaleVelocityAnimationEnabled()) {
+ return;
+ }
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.abs(zoomAddition) * 1000 / 4);
- scaleAnimator = createScaleAnimator(currentZoom, zoomAddition, animationTime);
+ scaleAnimator = createScaleAnimator(currentZoom, zoomAddition, scaleFocalPoint, animationTime);
scheduleAnimator(scaleAnimator);
}
-
- notifyOnScaleEndListeners(detector);
}
private void setScaleFocalPoint(StandardScaleGestureDetector detector) {
@@ -540,39 +580,6 @@ final class MapGestureDetector {
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 DecelerateInterpolator());
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- 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) {
- transform.cancelTransitions();
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- cameraChangeDispatcher.onCameraIdle();
- }
- });
- return animator;
- }
-
private double getNewZoom(float scaleFactor, boolean quickZoom) {
double zoomBy = Math.log(scaleFactor) / Math.log(Math.PI / 2);
if (quickZoom) {
@@ -591,10 +598,13 @@ final class MapGestureDetector {
private PointF rotateFocalPoint;
private final float minimumScaleSpanWhenRotating;
private final float minimumAngularVelocity;
+ private final float defaultSpanSinceStartThreshold;
- RotateGestureListener(float minimumScaleSpanWhenRotating, float minimumAngularVelocity) {
+ public RotateGestureListener(float minimumScaleSpanWhenRotating, float minimumAngularVelocity,
+ float defaultSpanSinceStartThreshold) {
this.minimumScaleSpanWhenRotating = minimumScaleSpanWhenRotating;
this.minimumAngularVelocity = minimumAngularVelocity;
+ this.defaultSpanSinceStartThreshold = defaultSpanSinceStartThreshold;
}
@Override
@@ -606,15 +616,17 @@ final class MapGestureDetector {
transform.cancelTransitions();
cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
- // when rotation starts, interrupting scale and increasing the threshold
- // to make rotation without scaling easier
- gesturesManager.getStandardScaleGestureDetector().setSpanSinceStartThreshold(minimumScaleSpanWhenRotating);
- gesturesManager.getStandardScaleGestureDetector().interrupt();
+ 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();
+ }
// setting in #onRotateBegin() as well, because #onRotate() might not get called before #onRotateEnd()
setRotateFocalPoint(detector);
- sendTelemetryEvent(Events.ROTATION, rotateFocalPoint);
+ sendTelemetryEvent(Telemetry.ROTATION, rotateFocalPoint);
notifyOnRotateBeginListeners(detector);
@@ -644,9 +656,16 @@ final class MapGestureDetector {
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 (uiSettings.isIncreaseScaleThresholdWhenRotating()) {
+ // resetting default scale threshold values
+ gesturesManager.getStandardScaleGestureDetector().setSpanSinceStartThreshold(defaultSpanSinceStartThreshold);
+ }
+
+ notifyOnRotateEndListeners(detector);
+
+ if (!uiSettings.isRotateVelocityAnimationEnabled()) {
+ return;
+ }
if (Math.abs(angularVelocity) < minimumAngularVelocity) {
return;
@@ -665,8 +684,6 @@ final class MapGestureDetector {
rotateAnimator = createRotateAnimator(angularVelocity, animationTime);
scheduleAnimator(rotateAnimator);
-
- notifyOnRotateEndListeners(detector);
}
private void setRotateFocalPoint(RotateGestureDetector detector) {
@@ -727,7 +744,7 @@ final class MapGestureDetector {
transform.cancelTransitions();
cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
- sendTelemetryEvent(Events.PITCH, detector.getFocalPoint());
+ sendTelemetryEvent(Telemetry.PITCH, detector.getFocalPoint());
// disabling move gesture during shove
gesturesManager.getMoveGestureDetector().setEnabled(false);
@@ -776,23 +793,104 @@ final class MapGestureDetector {
transform.cancelTransitions();
cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
+ sendTelemetryEvent(Telemetry.TWO_FINGER_TAP, detector.getFocalPoint());
+
+ PointF zoomFocalPoint;
+ // Single finger double tap
if (focalPoint != null) {
- transform.zoomOut(focalPoint);
+ // User provided focal point
+ zoomFocalPoint = focalPoint;
} else {
- transform.zoomOut(detector.getFocalPoint());
+ // Zoom in on gesture
+ zoomFocalPoint = detector.getFocalPoint();
}
+ zoomOutAnimated(zoomFocalPoint, false);
+
return true;
}
}
+ private Animator createScaleAnimator(double currentZoom, double zoomAddition, 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(ValueAnimator animation) {
+ transform.setZoom((Float) animation.getAnimatedValue(), animationFocalPoint);
+ }
+ });
+
+ animator.addListener(new AnimatorListenerAdapter() {
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ transform.cancelTransitions();
+ cameraChangeDispatcher.onCameraMoveStarted(REASON_API_ANIMATION);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ transform.cancelTransitions();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ cameraChangeDispatcher.onCameraIdle();
+ }
+ });
+ 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(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(PointF zoomFocalPoint, boolean runImmediately) {
+ zoomAnimated(false, zoomFocalPoint, runImmediately);
+ }
+
+ private void zoomAnimated(boolean zoomIn, 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 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));
+ Telemetry.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, state));
}
}
@@ -1008,7 +1106,9 @@ final class MapGestureDetector {
return gesturesManager;
}
- void setGesturesManager(AndroidGesturesManager gesturesManager) {
- this.gesturesManager = gesturesManager;
+ void setGesturesManager(Context context, AndroidGesturesManager gesturesManager, boolean attachDefaultListeners,
+ boolean setDefaultMutuallyExclusives) {
+ initializeGesturesManager(gesturesManager, setDefaultMutuallyExclusives);
+ initializeGestureListeners(context, attachDefaultListeners);
}
} \ No newline at end of file