Merge "Supports triple tap gesture to toggle window magnification"

This commit is contained in:
Minche Li
2020-06-29 17:05:01 +00:00
committed by Android (Google) Code Review
4 changed files with 115 additions and 36 deletions

View File

@@ -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,

View File

@@ -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";
}

View File

@@ -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);
}
@@ -348,8 +367,8 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
/**
* This class handles motion events in a duration to determine if the user is going to
* manipulate the window magnifier or want to interact with current UI. The rule of leaving
* this state is as follows:
* manipulate the window magnifier or want to interact with current UI. The rule of leaving
* this state is as follows:
* <ol>
* <li> If {@link MagnificationGestureMatcher#GESTURE_TWO_FINGER_DOWN} is detected,
* {@link State} will be transited to {@link PanningScalingGestureState}.</li>
@@ -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);

View File

@@ -56,16 +56,17 @@ 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;
public static final float DEFAULT_Y = 299;
//Assume first pointer position (DEFAULT_X,DEFAULT_Y) is in the window.
public static Rect DEFAULT_WINDOW_FRAME = new Rect(0, 0, 500, 500);
public static Rect DEFAULT_WINDOW_FRAME = new Rect(0, 0, 500, 500);
private static final int DISPLAY_0 = 0;
private Context mContext;
@@ -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;