diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java index 35abfd4c843e8..49db488bc740c 100644 --- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java @@ -676,6 +676,10 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { // 3tap and hold afterLongTapTimeoutTransitionToDraggingState(event); + } else if (isTapOutOfDistanceSlop()) { + + transitionToDelegatingStateAndClear(); + } else if (mDetectTripleTap // If magnified, delay an ACTION_DOWN for mMultiTapMaxDelay // to ensure reachability of @@ -921,6 +925,31 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { // TODO: multi-display support for magnification gesture handler mMagnificationController.setForceShowMagnifiableBounds(Display.DEFAULT_DISPLAY, state); } + + /** + * Detects if last action down is out of distance slop between with previous + * one, when triple tap is enabled. + * + * @return true if tap is out of distance slop + */ + boolean isTapOutOfDistanceSlop() { + if (!mDetectTripleTap) return false; + if (mPreLastDown == null || mLastDown == null) { + return false; + } + final boolean outOfDistanceSlop = + GestureUtils.distance(mPreLastDown, mLastDown) > mMultiTapMaxDistance; + if (tapCount() > 0) { + return outOfDistanceSlop; + } + // There's no tap in the queue here. We still need to check if this is the case that + // user tap screen quickly and out of distance slop. + if (outOfDistanceSlop + && !GestureUtils.isTimedOut(mPreLastDown, mLastDown, mMultiTapMaxDelay)) { + return true; + } + return false; + } } private void zoomOn(float centerX, float centerY) { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java index 5083110342f44..d91ce39ea92cf 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java @@ -308,6 +308,24 @@ public class MagnificationGestureHandlerTest { assertZoomsImmediatelyOnSwipeFrom(STATE_SHORTCUT_TRIGGERED); } + @Test + public void testMultiTap_outOfDistanceSlop_shouldInIdle() { + // All delay motion events should be sent, if multi-tap with out of distance slop. + // STATE_IDLE will check if tapCount() < 2. + allowEventDelegation(); + assertStaysIn(STATE_IDLE, () -> { + tap(); + tap(DEFAULT_X * 2, DEFAULT_Y * 2); + }); + assertStaysIn(STATE_IDLE, () -> { + tap(); + tap(DEFAULT_X * 2, DEFAULT_Y * 2); + tap(); + tap(DEFAULT_X * 2, DEFAULT_Y * 2); + tap(); + }); + } + private void assertZoomsImmediatelyOnSwipeFrom(int state) { goFromStateIdleTo(state); swipeAndHold(); @@ -531,6 +549,11 @@ public class MagnificationGestureHandlerTest { send(upEvent()); } + private void tap(float x, float y) { + send(downEvent(x, y)); + send(upEvent(x, y)); + } + private void swipe() { swipeAndHold(); send(upEvent()); @@ -572,18 +595,26 @@ public class MagnificationGestureHandlerTest { } private MotionEvent downEvent() { + return downEvent(DEFAULT_X, DEFAULT_Y); + } + + private MotionEvent downEvent(float x, float y) { mLastDownTime = mClock.now(); return fromTouchscreen(MotionEvent.obtain(mLastDownTime, mLastDownTime, - ACTION_DOWN, DEFAULT_X, DEFAULT_Y, 0)); + ACTION_DOWN, x, y, 0)); } private MotionEvent upEvent() { - return upEvent(mLastDownTime); + return upEvent(DEFAULT_X, DEFAULT_Y, mLastDownTime); } - private MotionEvent upEvent(long downTime) { + private MotionEvent upEvent(float x, float y) { + return upEvent(x, y, mLastDownTime); + } + + private MotionEvent upEvent(float x, float y, long downTime) { return fromTouchscreen(MotionEvent.obtain(downTime, mClock.now(), - MotionEvent.ACTION_UP, DEFAULT_X, DEFAULT_Y, 0)); + MotionEvent.ACTION_UP, x, y, 0)); } private MotionEvent pointerEvent(int action, float x, float y) {