summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Roy Chou <juchengchou@google.com> 2023-02-16 09:15:59 +0000
committer Roy Chou <juchengchou@google.com> 2023-02-23 06:50:32 +0000
commit3cc8cedc9dc7ac38fb288cabbf47a43bc9ae589c (patch)
tree14c3afc9acf77fbdbfca3bf55e1781b66acd0110
parente02709f4a7d84113a661e37f894851e46749366b (diff)
feat(#AlwaysOnMagnifier): Add haptic and buffer zone when panning scale to persisted scale
In FullScreenMagnificationGestureHandler, support vibration feedback and create buffer zone when panning scale to persisted scale. It's to help user stay on preferred zoom level when panning scale. Besides, with always on feature enabled, the magnifier would set scale to 100% and keep activated after transitions. The changes in this CL help user return to persisted scale quickly. Bug: 269476411 Test: manually - attach video in b/269476411 atest FullScreenMagnificationGestureHandlerTest atest AccessibilityMagnificationTest Change-Id: I4a607e5ad402d0d69b9236e4ccf5979850dfc49c (cherry picked from commit 43807cdfcc7bf15eea0083c642f82408ef9d1785)
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java80
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java87
2 files changed, 160 insertions, 7 deletions
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index 9c84c048003d..f85ef43f99fe 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -44,6 +44,8 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
import android.provider.Settings;
import android.util.Log;
import android.util.MathUtils;
@@ -279,7 +281,8 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
return mTempPointerProperties;
}
- private void transitionTo(State state) {
+ @VisibleForTesting
+ void transitionTo(State state) {
if (DEBUG_STATE_TRANSITIONS) {
Slog.i(mLogTag,
(State.nameOf(mCurrentState) + " -> " + State.nameOf(state)
@@ -287,6 +290,9 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
.replace(getClass().getName(), ""));
}
mPreviousState = mCurrentState;
+ if (state == mPanningScalingState) {
+ mPanningScalingState.prepareForState();
+ }
mCurrentState = state;
}
@@ -317,18 +323,34 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
final class PanningScalingState extends SimpleOnGestureListener
implements OnScaleGestureListener, State {
+ private final Context mContext;
private final ScaleGestureDetector mScaleGestureDetector;
private final GestureDetector mScrollGestureDetector;
final float mScalingThreshold;
float mInitialScaleFactor = -1;
- boolean mScaling;
+ @VisibleForTesting boolean mScaling;
+
+ /**
+ * Whether it needs to detect the target scale passes
+ * {@link FullScreenMagnificationController#getPersistedScale} during panning scale.
+ */
+ @VisibleForTesting boolean mDetectingPassPersistedScale;
+
+ // The threshold for relative difference from given scale to persisted scale. If the
+ // difference >= threshold, we can start detecting if the scale passes the persisted
+ // scale during panning.
+ @VisibleForTesting static final float CHECK_DETECTING_PASS_PERSISTED_SCALE_THRESHOLD = 0.2f;
+ // The threshold for relative difference from given scale to persisted scale. If the
+ // difference < threshold, we can decide that the scale passes the persisted scale.
+ @VisibleForTesting static final float PASSING_PERSISTED_SCALE_THRESHOLD = 0.01f;
PanningScalingState(Context context) {
final TypedValue scaleValue = new TypedValue();
context.getResources().getValue(
R.dimen.config_screen_magnification_scaling_threshold,
scaleValue, false);
+ mContext = context;
mScalingThreshold = scaleValue.getFloat();
mScaleGestureDetector = new ScaleGestureDetector(context, this, Handler.getMain());
mScaleGestureDetector.setQuickScaleEnabled(false);
@@ -351,12 +373,59 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
}
}
+
+ void prepareForState() {
+ checkShouldDetectPassPersistedScale();
+ }
+
+ private void checkShouldDetectPassPersistedScale() {
+ if (mDetectingPassPersistedScale) {
+ return;
+ }
+
+ final float currentScale =
+ mFullScreenMagnificationController.getScale(mDisplayId);
+ final float persistedScale =
+ mFullScreenMagnificationController.getPersistedScale(mDisplayId);
+
+ mDetectingPassPersistedScale =
+ (abs(currentScale - persistedScale) / persistedScale)
+ >= CHECK_DETECTING_PASS_PERSISTED_SCALE_THRESHOLD;
+ }
+
public void persistScaleAndTransitionTo(State state) {
mFullScreenMagnificationController.persistScale(mDisplayId);
clear();
transitionTo(state);
}
+ @VisibleForTesting
+ void setScaleAndClearIfNeeded(float scale, float pivotX, float pivotY) {
+ if (mDetectingPassPersistedScale) {
+ final float persistedScale =
+ mFullScreenMagnificationController.getPersistedScale(mDisplayId);
+ // If the scale passes the persisted scale during panning, perform a vibration
+ // feedback to user. Also, call {@link clear} to create a buffer zone so that
+ // user needs to panning more than {@link mScalingThreshold} to change scale again.
+ if (abs(scale - persistedScale) / persistedScale
+ < PASSING_PERSISTED_SCALE_THRESHOLD) {
+ scale = persistedScale;
+ final Vibrator vibrator = mContext.getSystemService(Vibrator.class);
+ if (vibrator != null) {
+ vibrator.vibrate(
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK));
+ }
+ clear();
+ }
+ }
+
+ if (DEBUG_PANNING_SCALING) Slog.i(mLogTag, "Scaled content to: " + scale + "x");
+ mFullScreenMagnificationController.setScale(mDisplayId, scale, pivotX, pivotY, false,
+ AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+
+ checkShouldDetectPassPersistedScale();
+ }
+
@Override
public boolean onScroll(MotionEvent first, MotionEvent second,
float distanceX, float distanceY) {
@@ -402,11 +471,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
scale = targetScale;
}
- final float pivotX = detector.getFocusX();
- final float pivotY = detector.getFocusY();
- if (DEBUG_PANNING_SCALING) Slog.i(mLogTag, "Scaled content to: " + scale + "x");
- mFullScreenMagnificationController.setScale(mDisplayId, scale, pivotX, pivotY, false,
- AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+ setScaleAndClearIfNeeded(scale, detector.getFocusX(), detector.getFocusY());
return /* handled: */ true;
}
@@ -424,6 +489,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
public void clear() {
mInitialScaleFactor = -1;
mScaling = false;
+ mDetectingPassPersistedScale = false;
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 51d3bae7d32c..306ce4dbf707 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -44,6 +44,8 @@ import android.annotation.NonNull;
import android.graphics.PointF;
import android.os.Handler;
import android.os.Message;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
import android.testing.TestableContext;
import android.util.DebugUtils;
import android.view.InputDevice;
@@ -507,6 +509,91 @@ public class FullScreenMagnificationGestureHandlerTest {
verify(mWindowMagnificationPromptController).showNotificationIfNeeded();
}
+ @Test
+ public void testTransitToPanningState_scaleDifferenceOverThreshold_startDetecting() {
+ final float scale = 2.0f;
+ final float threshold = FullScreenMagnificationGestureHandler.PanningScalingState
+ .CHECK_DETECTING_PASS_PERSISTED_SCALE_THRESHOLD;
+ final float persistedScale = (1.0f + threshold) * scale + 1.0f;
+ mFullScreenMagnificationController.setScale(DISPLAY_0, persistedScale, DEFAULT_X,
+ DEFAULT_Y, /* animate= */ false,
+ AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+ mFullScreenMagnificationController.persistScale(DISPLAY_0);
+ mFullScreenMagnificationController.setScale(DISPLAY_0, scale, DEFAULT_X,
+ DEFAULT_Y, /* animate= */ false,
+ AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+
+ mMgh.transitionTo(mMgh.mPanningScalingState);
+
+ assertTrue(mMgh.mPanningScalingState.mDetectingPassPersistedScale);
+
+ mMgh.clearAndTransitionToStateDetecting();
+ mFullScreenMagnificationController.reset(DISPLAY_0, /* animate= */ false);
+ }
+
+ @Test
+ public void testTransitToPanningState_scaleDifferenceLessThanThreshold_doNotDetect() {
+ final float scale = 2.0f;
+ final float threshold = FullScreenMagnificationGestureHandler.PanningScalingState
+ .CHECK_DETECTING_PASS_PERSISTED_SCALE_THRESHOLD;
+ final float persistedScale = (1.0f + threshold) * scale - 0.1f;
+ mFullScreenMagnificationController.setScale(DISPLAY_0, persistedScale, DEFAULT_X,
+ DEFAULT_Y, /* animate= */ false,
+ AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+ mFullScreenMagnificationController.persistScale(DISPLAY_0);
+ mFullScreenMagnificationController.setScale(DISPLAY_0, scale, DEFAULT_X,
+ DEFAULT_Y, /* animate= */ false,
+ AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+
+ mMgh.transitionTo(mMgh.mPanningScalingState);
+
+ assertFalse(mMgh.mPanningScalingState.mDetectingPassPersistedScale);
+
+ mMgh.clearAndTransitionToStateDetecting();
+ mFullScreenMagnificationController.reset(DISPLAY_0, /* animate= */ false);
+ }
+
+ @Test
+ public void testPanningScaleToPersistedScale_detecting_vibrateAndClear() {
+ Vibrator vibrator = mock(Vibrator.class);
+ mContext.addMockSystemService(Vibrator.class, vibrator);
+
+ mMgh.mPanningScalingState.mDetectingPassPersistedScale = true;
+
+ final float persistedScale =
+ mFullScreenMagnificationController.getPersistedScale(DISPLAY_0);
+
+ mMgh.transitionTo(mMgh.mPanningScalingState);
+ mMgh.mPanningScalingState.setScaleAndClearIfNeeded(persistedScale, DEFAULT_X, DEFAULT_Y);
+
+ verify(vibrator).vibrate(any(VibrationEffect.class));
+ assertFalse(mMgh.mPanningScalingState.mScaling);
+
+ mMgh.clearAndTransitionToStateDetecting();
+ mFullScreenMagnificationController.reset(DISPLAY_0, /* animate= */ false);
+ }
+
+ @Test
+ public void testPanningScaleOverThreshold_notDetecting_startDetecting() {
+ final float persistedScale =
+ mFullScreenMagnificationController.getPersistedScale(DISPLAY_0);
+
+ mFullScreenMagnificationController.setScale(DISPLAY_0, persistedScale, DEFAULT_X,
+ DEFAULT_Y, /* animate= */ false,
+ AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+ mMgh.transitionTo(mMgh.mPanningScalingState);
+
+ final float threshold = FullScreenMagnificationGestureHandler.PanningScalingState
+ .CHECK_DETECTING_PASS_PERSISTED_SCALE_THRESHOLD;
+ final float scale = (1.0f + threshold) * persistedScale + 1.0f;
+ mMgh.mPanningScalingState.setScaleAndClearIfNeeded(scale, DEFAULT_X, DEFAULT_Y);
+
+ assertTrue(mMgh.mPanningScalingState.mDetectingPassPersistedScale);
+
+ mMgh.clearAndTransitionToStateDetecting();
+ mFullScreenMagnificationController.reset(DISPLAY_0, /* animate= */ false);
+ }
+
private void assertActionsInOrder(List<MotionEvent> actualEvents,
List<Integer> expectedActions) {
assertTrue(actualEvents.size() == expectedActions.size());