Merge "Supports triple tap gesture to toggle window magnification"
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user