From 3e73e4d49fd4239d8dc12584294e6ea3d200facf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Paczos?= Date: Tue, 16 Jul 2019 12:46:27 +0200 Subject: [android] improve scale + rotation behavior --- .../mapboxsdk/constants/MapboxConstants.java | 1 + .../mapbox/mapboxsdk/maps/MapGestureDetector.java | 103 +++++++++++++-------- .../java/com/mapbox/mapboxsdk/maps/UiSettings.java | 29 ++++++ .../src/main/res/values/dimens.xml | 5 +- .../com/mapbox/mapboxsdk/maps/UiSettingsTest.java | 16 ++-- 5 files changed, 103 insertions(+), 51 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java index e9fc9e2e02..d5c5c4d999 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java @@ -200,5 +200,6 @@ public class MapboxConstants { public static final String STATE_ROTATE_ANIMATION_ENABLED = "mapbox_rotateAnimationEnabled"; public static final String STATE_FLING_ANIMATION_ENABLED = "mapbox_flingAnimationEnabled"; public static final String STATE_INCREASE_ROTATE_THRESHOLD = "mapbox_increaseRotateThreshold"; + public static final String STATE_DISABLE_ROTATE_WHEN_SCALING = "mapbox_disableRotateWhenScaling"; public static final String STATE_INCREASE_SCALE_THRESHOLD = "mapbox_increaseScaleThreshold"; } 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 77d59e1690..6b61f01b8a 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 @@ -13,7 +13,6 @@ 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; @@ -115,12 +114,12 @@ final class MapGestureDetector { com.mapbox.android.gestures.R.dimen.mapbox_defaultScaleSpanSinceStartThreshold)); MoveGestureListener moveGestureListener = new MoveGestureListener(); ScaleGestureListener scaleGestureListener = new ScaleGestureListener( - context.getResources().getDimension(R.dimen.mapbox_minimum_scale_velocity)); + 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_minimum_angular_velocity), - context.getResources().getDimension( - com.mapbox.android.gestures.R.dimen.mapbox_defaultScaleSpanSinceStartThreshold)); + context.getResources().getDimension(R.dimen.mapbox_minimum_angular_velocity)); ShoveGestureListener shoveGestureListener = new ShoveGestureListener(); TapGestureListener tapGestureListener = new TapGestureListener(); @@ -152,6 +151,7 @@ final class MapGestureDetector { } gesturesManager = androidGesturesManager; + gesturesManager.getRotateGestureDetector().setAngleThreshold(3f); } /** @@ -480,10 +480,14 @@ final class MapGestureDetector { private final class ScaleGestureListener extends StandardScaleGestureDetector.SimpleStandardOnScaleGestureListener { + private final float minimumGestureSpeed; + private final float minimumAngledGestureSpeed; private final float minimumVelocity; private boolean quickZoom; - ScaleGestureListener(float minimumVelocity) { + ScaleGestureListener(float minimumGestureSpeed, float minimumAngledGestureSpeed, float minimumVelocity) { + this.minimumGestureSpeed = minimumGestureSpeed; + this.minimumAngledGestureSpeed = minimumAngledGestureSpeed; this.minimumVelocity = minimumVelocity; } @@ -501,18 +505,38 @@ final class MapGestureDetector { } // 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; + } } cancelTransitionsIfRequired(); - 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 - ); - } - notifyOnScaleBeginListeners(detector); return true; @@ -538,13 +562,9 @@ final class MapGestureDetector { if (quickZoom) { // re-enabled the move detector if the quickzoom happened gesturesManager.getMoveGestureDetector().setEnabled(true); - } - - if (uiSettings.isIncreaseRotateThresholdWhenScaling()) { - // resetting default angle threshold - gesturesManager.getRotateGestureDetector().setAngleThreshold( - Constants.DEFAULT_ROTATE_ANGLE_THRESHOLD - ); + } else { + // re-enable rotation in case it's been disabled + gesturesManager.getRotateGestureDetector().setEnabled(true); } notifyOnScaleEndListeners(detector); @@ -605,15 +625,10 @@ final class MapGestureDetector { } private final class RotateGestureListener extends RotateGestureDetector.SimpleOnRotateGestureListener { - private final float minimumScaleSpanWhenRotating; private final float minimumAngularVelocity; - private final float defaultSpanSinceStartThreshold; - RotateGestureListener(float minimumScaleSpanWhenRotating, float minimumAngularVelocity, - float defaultSpanSinceStartThreshold) { - this.minimumScaleSpanWhenRotating = minimumScaleSpanWhenRotating; + RotateGestureListener(float minimumAngularVelocity) { this.minimumAngularVelocity = minimumAngularVelocity; - this.defaultSpanSinceStartThreshold = defaultSpanSinceStartThreshold; } @Override @@ -622,15 +637,26 @@ final class MapGestureDetector { return false; } - cancelTransitionsIfRequired(); - - 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(); + 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; } + cancelTransitionsIfRequired(); + notifyOnRotateBeginListeners(detector); return true; @@ -657,11 +683,6 @@ final class MapGestureDetector { @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); if (!uiSettings.isRotateVelocityAnimationEnabled() || Math.abs(angularVelocity) < minimumAngularVelocity) { @@ -681,6 +702,8 @@ final class MapGestureDetector { angularVelocity = -angularVelocity; } + // todo clear scale velocity if angular velocity exceeds a threshold + PointF focalPoint = getRotateFocalPoint(detector); rotateAnimator = createRotateAnimator(angularVelocity, animationTime, focalPoint); scheduleAnimator(rotateAnimator); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java index 49abb70c91..4c4ced8804 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java @@ -67,6 +67,7 @@ public final class UiSettings { private boolean flingVelocityAnimationEnabled = true; private boolean increaseRotateThresholdWhenScaling = true; + private boolean disableRotateWhenScaling = true; private boolean increaseScaleThresholdWhenRotating = true; private float zoomRate = 1.0f; @@ -132,6 +133,7 @@ public final class UiSettings { outState.putBoolean(MapboxConstants.STATE_ROTATE_ANIMATION_ENABLED, isRotateVelocityAnimationEnabled()); outState.putBoolean(MapboxConstants.STATE_FLING_ANIMATION_ENABLED, isFlingVelocityAnimationEnabled()); outState.putBoolean(MapboxConstants.STATE_INCREASE_ROTATE_THRESHOLD, isIncreaseRotateThresholdWhenScaling()); + outState.putBoolean(MapboxConstants.STATE_DISABLE_ROTATE_WHEN_SCALING, isDisableRotateWhenScaling()); outState.putBoolean(MapboxConstants.STATE_INCREASE_SCALE_THRESHOLD, isIncreaseScaleThresholdWhenRotating()); outState.putBoolean(MapboxConstants.STATE_QUICK_ZOOM_ENABLED, isQuickZoomGesturesEnabled()); outState.putFloat(MapboxConstants.STATE_ZOOM_RATE, getZoomRate()); @@ -148,6 +150,7 @@ public final class UiSettings { setFlingVelocityAnimationEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_FLING_ANIMATION_ENABLED)); setIncreaseRotateThresholdWhenScaling( savedInstanceState.getBoolean(MapboxConstants.STATE_INCREASE_ROTATE_THRESHOLD)); + setDisableRotateWhenScaling(savedInstanceState.getBoolean(MapboxConstants.STATE_DISABLE_ROTATE_WHEN_SCALING)); setIncreaseScaleThresholdWhenRotating( savedInstanceState.getBoolean(MapboxConstants.STATE_INCREASE_SCALE_THRESHOLD)); setQuickZoomGesturesEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_QUICK_ZOOM_ENABLED)); @@ -919,7 +922,9 @@ public final class UiSettings { * Returns whether rotation threshold should be increase whenever scale is detected. * * @return If true, rotation threshold will be increased. + * @deprecated unused, see {@link #isDisableRotateWhenScaling()} instead */ + @Deprecated public boolean isIncreaseRotateThresholdWhenScaling() { return increaseRotateThresholdWhenScaling; } @@ -928,16 +933,38 @@ public final class UiSettings { * Set whether rotation threshold should be increase whenever scale is detected. * * @param increaseRotateThresholdWhenScaling If true, rotation threshold will be increased. + * @deprecated unused, see {@link #setDisableRotateWhenScaling(boolean)} instead */ + @Deprecated public void setIncreaseRotateThresholdWhenScaling(boolean increaseRotateThresholdWhenScaling) { this.increaseRotateThresholdWhenScaling = increaseRotateThresholdWhenScaling; } + /** + * Returns whether rotation gesture detector is disabled when scale is detected first. + * + * @return If true, rotation gesture detector will be disabled when scale is detected first. + */ + public boolean isDisableRotateWhenScaling() { + return disableRotateWhenScaling; + } + + /** + * Set whether rotation gesture detector should be disabled when scale is detected first. + * + * @param disableRotateWhenScaling If true, rotation gesture detector will be disabled when scale is detected first. + */ + public void setDisableRotateWhenScaling(boolean disableRotateWhenScaling) { + this.disableRotateWhenScaling = disableRotateWhenScaling; + } + /** * Returns whether scale threshold should be increase whenever rotation is detected. * * @return If true, scale threshold will be increased. + * @deprecated unused */ + @Deprecated public boolean isIncreaseScaleThresholdWhenRotating() { return increaseScaleThresholdWhenRotating; } @@ -946,7 +973,9 @@ public final class UiSettings { * set whether scale threshold should be increase whenever rotation is detected. * * @param increaseScaleThresholdWhenRotating If true, scale threshold will be increased. + * @deprecated unused */ + @Deprecated public void setIncreaseScaleThresholdWhenRotating(boolean increaseScaleThresholdWhenRotating) { this.increaseScaleThresholdWhenRotating = increaseScaleThresholdWhenRotating; } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml index 161a9634d2..018750400c 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml @@ -3,6 +3,8 @@ 8dp 8dp 4dp + 0.6dp + 0.9dp 8dp 92dp 18dp @@ -10,9 +12,6 @@ 225dp - - 100dp - 0.025dp diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java index 80ac2213ee..cfcf0a0486 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java @@ -341,17 +341,17 @@ public class UiSettingsTest { } @Test - public void testIncreaseRotateThresholdWhenScalingEnabled() { - uiSettings.setIncreaseRotateThresholdWhenScaling(true); - assertEquals("Rotate threshold increase should be enabled", true, - uiSettings.isIncreaseRotateThresholdWhenScaling()); + public void testDisableRotateWhenScalingEnabled() { + uiSettings.setDisableRotateWhenScaling(true); + assertEquals("Rotate disabling should be enabled", true, + uiSettings.isDisableRotateWhenScaling()); } @Test - public void testIncreaseRotateThresholdWhenScalingDisabled() { - uiSettings.setIncreaseRotateThresholdWhenScaling(false); - assertEquals("Rotate threshold increase should be disabled", false, - uiSettings.isIncreaseRotateThresholdWhenScaling()); + public void testDisableRotateWhenScalingDisabled() { + uiSettings.setDisableRotateWhenScaling(false); + assertEquals("Rotate disabling should be disabled", false, + uiSettings.isDisableRotateWhenScaling()); } @Test -- cgit v1.2.1