Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8453814e authored by mincheli's avatar mincheli Committed by Minche Li
Browse files

Supports triple tap gesture to toggle window magnification

Users can use tripple tap gesture to toggle window magnification just
like accessibility button shortcut.

Bug: 157861708
Test: atest WindowMagnificationGestureHandlerTest
Change-Id: I4a7ca54ee98c59a59cba528eea5c81b6f619cc7a
parent 97b3c324
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -533,9 +533,10 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
                & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0;
        MagnificationGestureHandler magnificationGestureHandler;
        if (mAms.getMagnificationMode(displayId)
                == Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW && triggerable) {
                == Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
            magnificationGestureHandler = new WindowMagnificationGestureHandler(displayContext,
                    mAms.getWindowMagnificationMgr(), mAms::onMagnificationScaleChanged, displayId);
                    mAms.getWindowMagnificationMgr(), mAms::onMagnificationScaleChanged,
                    detectControlGestures, triggerable, displayId);
        } else {
            magnificationGestureHandler = new FullScreenMagnificationGestureHandler(displayContext,
                    mAms.getMagnificationController(), mAms::onMagnificationScaleChanged,
+6 −0
Original line number Diff line number Diff line
@@ -37,6 +37,8 @@ class MagnificationGestureMatcher {
    public static final int GESTURE_SWIPE = GESTURE_BASE + 2;
    public static final int GESTURE_SINGLE_TAP = GESTURE_BASE + 3;
    public static final int GESTURE_SINGLE_TAP_AND_HOLD = GESTURE_BASE + 4;
    public static final int GESTURE_TRIPLE_TAP = GESTURE_BASE + 5;
    public static final int GESTURE_TRIPLE_TAP_AND_HOLD = GESTURE_BASE + 6;

    @IntDef(prefix = {"GESTURE_MAGNIFICATION_"}, value = {
            GESTURE_TWO_FINGER_DOWN,
@@ -61,6 +63,10 @@ class MagnificationGestureMatcher {
                return "GESTURE_SINGLE_TAP";
            case GESTURE_SINGLE_TAP_AND_HOLD:
                return "GESTURE_SINGLE_TAP_AND_HOLD";
            case GESTURE_TRIPLE_TAP:
                return "GESTURE_TRIPLE_TAP";
            case GESTURE_TRIPLE_TAP_AND_HOLD:
                return "GESTURE_TRIPLE_TAP_AND_HOLD";
        }
        return "none";
    }
+56 −17
Original line number Diff line number Diff line
@@ -44,9 +44,11 @@ import java.util.Queue;
 * This class handles window magnification in response to touch events and shortcut.
 *
 * The behavior is as follows:

 *
 * <ol>
 *   <li> Window magnification can be "toggled" by tapping shortcut. It is triggered via
 *   <li> 1. Toggle Window magnification by triple-tap gesture shortcut. It is triggered via
 *   {@link #onTripleTap(MotionEvent)}.
 *   <li> 2. Toggle Window magnification by tapping shortcut. It is triggered via
 *   {@link #notifyShortcutTriggered()}.
 *   <li> When the window magnifier is visible, pinching with any number of additional fingers
 *   would adjust the magnification scale .<strong>Note</strong> that this operation is valid only
@@ -72,7 +74,6 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
    private static final float MAX_SCALE = WindowMagnificationManager.MAX_SCALE;

    private final WindowMagnificationManager mWindowMagnificationMgr;

    @VisibleForTesting
    final DelegatingState mDelegatingState;
    @VisibleForTesting
@@ -85,6 +86,8 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
    @VisibleForTesting
    State mPreviousState;

    final boolean mDetectShortcutTrigger;

    private MotionEventDispatcherDelegate mMotionEventDispatcherDelegate;
    private final int mDisplayId;

@@ -97,7 +100,8 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
     */
    public WindowMagnificationGestureHandler(Context context,
            WindowMagnificationManager windowMagnificationMgr,
            MagnificationGestureHandler.ScaleChangedListener listener, int displayId) {
            MagnificationGestureHandler.ScaleChangedListener listener, boolean detectTripleTap,
            boolean detectShortcutTrigger, int displayId) {
        super(listener);
        if (DEBUG_ALL) {
            Slog.i(LOG_TAG,
@@ -105,12 +109,13 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
        }

        mWindowMagnificationMgr = windowMagnificationMgr;
        mDetectShortcutTrigger = detectShortcutTrigger;
        mDisplayId = displayId;
        mMotionEventDispatcherDelegate = new MotionEventDispatcherDelegate(context,
                (event, rawEvent, policyFlags) -> super.onMotionEvent(
                        event, rawEvent, policyFlags));
        mDelegatingState = new DelegatingState(mMotionEventDispatcherDelegate);
        mDetectingState = new DetectingState(context);
        mDetectingState = new DetectingState(context, detectTripleTap);
        mObservePanningScalingState = new PanningScalingGestureState(
                new PanningScalingHandler(context, MAX_SCALE, MIN_SCALE, true,
                        new PanningScalingHandler.MagnificationDelegate() {
@@ -176,11 +181,10 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
        if (DEBUG_ALL) {
            Slog.i(LOG_TAG, "notifyShortcutTriggered():");
        }
        if (mWindowMagnificationMgr.isWindowMagnifierEnabled(mDisplayId)) {
            disableWindowMagnifier();
        } else {
            enableWindowMagnifier(Float.NaN, Float.NaN);
        if (!mDetectShortcutTrigger) {
            return;
        }
        toggleMagnification(Float.NaN, Float.NaN);
    }

    @Override
@@ -206,6 +210,21 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
        mWindowMagnificationMgr.disableWindowMagnifier(mDisplayId, false);
    }

    private void toggleMagnification(float centerX, float centerY) {
        if (mWindowMagnificationMgr.isWindowMagnifierEnabled(mDisplayId)) {
            disableWindowMagnifier();
        } else {
            enableWindowMagnifier(centerX, centerY);
        }
    }

    private void onTripleTap(MotionEvent up) {
        if (DEBUG_DETECTING) {
            Slog.i(LOG_TAG, "onTripleTap()");
        }
        toggleMagnification(up.getX(), up.getY());
    }

    void resetToDetectState() {
        transitionTo(mDetectingState);
    }
@@ -363,11 +382,28 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl

        private final MagnificationGesturesObserver mGesturesObserver;

        DetectingState(Context context) {
            mGesturesObserver = new MagnificationGesturesObserver(this, new SimpleSwipe(context),
                    new MultiTap(context, 1, MagnificationGestureMatcher.GESTURE_SINGLE_TAP, null),
                    new MultiTapAndHold(context, 1,
                            MagnificationGestureMatcher.GESTURE_SINGLE_TAP_AND_HOLD, null),
        /**
         * {@code true} if this detector should detect and respond to triple-tap
         * gestures for engaging and disengaging magnification,
         * {@code false} if it should ignore such gestures
         */
        private final boolean mDetectTripleTap;

        DetectingState(Context context, boolean detectTripleTap) {
            mDetectTripleTap = detectTripleTap;
            final MultiTap multiTap = new MultiTap(context, mDetectTripleTap ? 3 : 1,
                    mDetectTripleTap
                            ? MagnificationGestureMatcher.GESTURE_TRIPLE_TAP
                            : MagnificationGestureMatcher.GESTURE_SINGLE_TAP, null);
            final MultiTapAndHold multiTapAndHold = new MultiTapAndHold(context,
                    mDetectTripleTap ? 3 : 1,
                    mDetectTripleTap
                            ? MagnificationGestureMatcher.GESTURE_TRIPLE_TAP_AND_HOLD
                            : MagnificationGestureMatcher.GESTURE_SINGLE_TAP_AND_HOLD, null);
            mGesturesObserver = new MagnificationGesturesObserver(this,
                    new SimpleSwipe(context),
                    multiTap,
                    multiTapAndHold,
                    new TwoFingersDown(context));
        }

@@ -395,7 +431,8 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl

        @Override
        public boolean shouldStopDetection(MotionEvent motionEvent) {
            return !mWindowMagnificationMgr.isWindowMagnifierEnabled(mDisplayId);
            return !mWindowMagnificationMgr.isWindowMagnifierEnabled(mDisplayId)
                    && !mDetectTripleTap;
        }

        @Override
@@ -412,6 +449,8 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
            if (gestureId == MagnificationGestureMatcher.GESTURE_TWO_FINGER_DOWN
                    && mWindowMagnificationMgr.pointersInWindow(mDisplayId, motionEvent) > 0) {
                transitionTo(mObservePanningScalingState);
            } else if (gestureId == MagnificationGestureMatcher.GESTURE_TRIPLE_TAP) {
                onTripleTap(motionEvent);
            } else {
                mMotionEventDispatcherDelegate.sendDelayedMotionEvents(delayedEventQueue,
                        lastDownEventTime);
+50 −17
Original line number Diff line number Diff line
@@ -56,10 +56,11 @@ public class WindowMagnificationGestureHandlerTest {
    public static final int STATE_IDLE = 1;
    public static final int STATE_SHOW_MAGNIFIER = 2;
    public static final int STATE_TWO_FINGERS_DOWN = 3;
    public static final int STATE_SHOW_MAGNIFIER_TRIPLE_TAP = 4;
    //TODO: Test it after can injecting Handler to GestureMatcher is available.

    public static final int FIRST_STATE = STATE_IDLE;
    public static final int LAST_STATE = STATE_TWO_FINGERS_DOWN;
    public static final int LAST_STATE = STATE_SHOW_MAGNIFIER_TRIPLE_TAP;

    // Co-prime x and y, to potentially catch x-y-swapped errors
    public static final float DEFAULT_X = 301;
@@ -79,7 +80,8 @@ public class WindowMagnificationGestureHandlerTest {
        mWindowMagnificationManager = new WindowMagnificationManager(mContext, 0);
        mMockConnection = new MockWindowMagnificationConnection();
        mWindowMagnificationGestureHandler = new WindowMagnificationGestureHandler(
                mContext, mWindowMagnificationManager, mock(ScaleChangedListener.class), DISPLAY_0);
                mContext, mWindowMagnificationManager, mock(ScaleChangedListener.class),
                /** detectTripleTap= */true,   /** detectShortcutTrigger= */true, DISPLAY_0);
        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
        mMockConnection.getConnectionCallback().onWindowMagnifierBoundsChanged(DISPLAY_0,
                DEFAULT_WINDOW_FRAME);
@@ -108,7 +110,9 @@ public class WindowMagnificationGestureHandlerTest {
     *     <br> IDLE -> SHOW_MAGNIFIER [label="a11y\nbtn"]
     *     <br> SHOW_MAGNIFIER -> TWO_FINGER_DOWN [label="2hold"]
     *     <br> TWO_FINGER_DOWN -> SHOW_MAGNIFIER [label="release"]
     *     <br> SHOW_MAGNIFIER -> IDLE [label="a11y\nbtn"]*
     *     <br> SHOW_MAGNIFIER -> IDLE [label="a11y\nbtn"]
     *     <br> IDLE -> SHOW_MAGNIFIER_TRIPLE_TAP [label="3tap"]
     *     <br> SHOW_MAGNIFIER_TRIPLE_TAP -> IDLE [label="3tap"]
     * </p>
     * This navigates between states using "canonical" paths, specified in
     * {@link #goFromStateIdleTo} (for traversing away from {@link #STATE_IDLE}) and
@@ -167,20 +171,24 @@ public class WindowMagnificationGestureHandlerTest {
                check(!isWindowMagnifierEnabled(DISPLAY_0), state);
                check(mWindowMagnificationGestureHandler.mCurrentState
                        == mWindowMagnificationGestureHandler.mDetectingState, state);
            } break;
            case STATE_SHOW_MAGNIFIER: {
            }
            break;
            case STATE_SHOW_MAGNIFIER:
            case STATE_SHOW_MAGNIFIER_TRIPLE_TAP: {
                check(isWindowMagnifierEnabled(DISPLAY_0), state);
                check(mWindowMagnificationGestureHandler.mCurrentState
                        == mWindowMagnificationGestureHandler.mDetectingState, state);
            } break;
            }
                break;
            case STATE_TWO_FINGERS_DOWN: {
                check(isWindowMagnifierEnabled(DISPLAY_0), state);
                check(mWindowMagnificationGestureHandler.mCurrentState
                                == mWindowMagnificationGestureHandler.mObservePanningScalingState,
                        state);
            } break;

            default: throw new IllegalArgumentException("Illegal state: " + state);
            }
            break;
            default:
                throw new IllegalArgumentException("Illegal state: " + state);
        }
    }

@@ -192,17 +200,27 @@ public class WindowMagnificationGestureHandlerTest {
            switch (state) {
                case STATE_IDLE: {
                    // no op
                } break;
                }
                break;
                case STATE_SHOW_MAGNIFIER: {
                    triggerShortcut();
                } break;
                }
                break;
                case STATE_TWO_FINGERS_DOWN: {
                    goFromStateIdleTo(STATE_SHOW_MAGNIFIER);
                    send(downEvent());
                    //Second finger is outside the window.
                    send(pointerEvent(ACTION_POINTER_DOWN, DEFAULT_WINDOW_FRAME.right + 10,
                            DEFAULT_WINDOW_FRAME.bottom + 10));
                } break;
                }
                break;
                case STATE_SHOW_MAGNIFIER_TRIPLE_TAP: {
                    // Perform triple tap gesture
                    tap();
                    tap();
                    tap();
                }
                break;
                default:
                    throw new IllegalArgumentException("Illegal state: " + state);
            }
@@ -218,15 +236,25 @@ public class WindowMagnificationGestureHandlerTest {
        switch (state) {
            case STATE_IDLE: {
                // no op
            } break;
            }
            break;
            case STATE_SHOW_MAGNIFIER: {
                mWindowMagnificationManager.disableWindowMagnifier(DISPLAY_0, false);
            } break;
            }
            break;
            case STATE_TWO_FINGERS_DOWN: {
                send(upEvent());
                returnToNormalFrom(STATE_SHOW_MAGNIFIER);
            } break;
            default: throw new IllegalArgumentException("Illegal state: " + state);
            }
            break;
            case STATE_SHOW_MAGNIFIER_TRIPLE_TAP: {
                tap();
                tap();
                tap();
            }
            break;
            default:
                throw new IllegalArgumentException("Illegal state: " + state);
        }
    }

@@ -270,6 +298,11 @@ public class WindowMagnificationGestureHandlerTest {
        return TouchEventGenerator.upEvent(DISPLAY_0, x, y);
    }

    private void tap() {
        send(downEvent());
        send(upEvent());
    }

    private MotionEvent pointerEvent(int action, float x, float y) {
        final MotionEvent.PointerCoords defPointerCoords = new MotionEvent.PointerCoords();
        defPointerCoords.x = DEFAULT_X;