Loading services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java +73 −7 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) Loading @@ -287,6 +290,9 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH .replace(getClass().getName(), "")); } mPreviousState = mCurrentState; if (state == mPanningScalingState) { mPanningScalingState.prepareForState(); } mCurrentState = state; } Loading Loading @@ -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); Loading @@ -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) { Loading Loading @@ -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; } Loading @@ -424,6 +489,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH public void clear() { mInitialScaleFactor = -1; mScaling = false; mDetectingPassPersistedScale = false; } @Override Loading services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java +87 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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()); Loading Loading
services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java +73 −7 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) Loading @@ -287,6 +290,9 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH .replace(getClass().getName(), "")); } mPreviousState = mCurrentState; if (state == mPanningScalingState) { mPanningScalingState.prepareForState(); } mCurrentState = state; } Loading Loading @@ -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); Loading @@ -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) { Loading Loading @@ -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; } Loading @@ -424,6 +489,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH public void clear() { mInitialScaleFactor = -1; mScaling = false; mDetectingPassPersistedScale = false; } @Override Loading
services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java +87 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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()); Loading