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

Commit eb856d8f authored by Minche Li's avatar Minche Li Committed by Android (Google) Code Review
Browse files

Merge "Supports triple tap gesture to toggle window magnification"

parents 480258a7 8453814e
Loading
Loading
Loading
Loading
+3 −2
Original line number Original line Diff line number Diff line
@@ -533,9 +533,10 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
                & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0;
                & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0;
        MagnificationGestureHandler magnificationGestureHandler;
        MagnificationGestureHandler magnificationGestureHandler;
        if (mAms.getMagnificationMode(displayId)
        if (mAms.getMagnificationMode(displayId)
                == Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW && triggerable) {
                == Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
            magnificationGestureHandler = new WindowMagnificationGestureHandler(displayContext,
            magnificationGestureHandler = new WindowMagnificationGestureHandler(displayContext,
                    mAms.getWindowMagnificationMgr(), mAms::onMagnificationScaleChanged, displayId);
                    mAms.getWindowMagnificationMgr(), mAms::onMagnificationScaleChanged,
                    detectControlGestures, triggerable, displayId);
        } else {
        } else {
            magnificationGestureHandler = new FullScreenMagnificationGestureHandler(displayContext,
            magnificationGestureHandler = new FullScreenMagnificationGestureHandler(displayContext,
                    mAms.getMagnificationController(), mAms::onMagnificationScaleChanged,
                    mAms.getMagnificationController(), mAms::onMagnificationScaleChanged,
+6 −0
Original line number Original line 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_SWIPE = GESTURE_BASE + 2;
    public static final int GESTURE_SINGLE_TAP = GESTURE_BASE + 3;
    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_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 = {
    @IntDef(prefix = {"GESTURE_MAGNIFICATION_"}, value = {
            GESTURE_TWO_FINGER_DOWN,
            GESTURE_TWO_FINGER_DOWN,
@@ -61,6 +63,10 @@ class MagnificationGestureMatcher {
                return "GESTURE_SINGLE_TAP";
                return "GESTURE_SINGLE_TAP";
            case GESTURE_SINGLE_TAP_AND_HOLD:
            case GESTURE_SINGLE_TAP_AND_HOLD:
                return "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";
        return "none";
    }
    }
+56 −17
Original line number Original line 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.
 * This class handles window magnification in response to touch events and shortcut.
 *
 *
 * The behavior is as follows:
 * The behavior is as follows:

 *
 * <ol>
 * <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()}.
 *   {@link #notifyShortcutTriggered()}.
 *   <li> When the window magnifier is visible, pinching with any number of additional fingers
 *   <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
 *   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 static final float MAX_SCALE = WindowMagnificationManager.MAX_SCALE;


    private final WindowMagnificationManager mWindowMagnificationMgr;
    private final WindowMagnificationManager mWindowMagnificationMgr;

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


    final boolean mDetectShortcutTrigger;

    private MotionEventDispatcherDelegate mMotionEventDispatcherDelegate;
    private MotionEventDispatcherDelegate mMotionEventDispatcherDelegate;
    private final int mDisplayId;
    private final int mDisplayId;


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


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


    @Override
    @Override
@@ -206,6 +210,21 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
        mWindowMagnificationMgr.disableWindowMagnifier(mDisplayId, false);
        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() {
    void resetToDetectState() {
        transitionTo(mDetectingState);
        transitionTo(mDetectingState);
    }
    }
@@ -363,11 +382,28 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl


        private final MagnificationGesturesObserver mGesturesObserver;
        private final MagnificationGesturesObserver mGesturesObserver;


        DetectingState(Context context) {
        /**
            mGesturesObserver = new MagnificationGesturesObserver(this, new SimpleSwipe(context),
         * {@code true} if this detector should detect and respond to triple-tap
                    new MultiTap(context, 1, MagnificationGestureMatcher.GESTURE_SINGLE_TAP, null),
         * gestures for engaging and disengaging magnification,
                    new MultiTapAndHold(context, 1,
         * {@code false} if it should ignore such gestures
                            MagnificationGestureMatcher.GESTURE_SINGLE_TAP_AND_HOLD, null),
         */
        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));
                    new TwoFingersDown(context));
        }
        }


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


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


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


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

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


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


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

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