diff options
| author | 2023-02-16 09:15:59 +0000 | |
|---|---|---|
| committer | 2023-02-23 06:50:32 +0000 | |
| commit | 3cc8cedc9dc7ac38fb288cabbf47a43bc9ae589c (patch) | |
| tree | 14c3afc9acf77fbdbfca3bf55e1781b66acd0110 | |
| parent | e02709f4a7d84113a661e37f894851e46749366b (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)
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()); |