summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java311
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java93
2 files changed, 374 insertions, 30 deletions
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index 45ca7260bbe7..d31b1efafcc4 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -69,6 +69,7 @@ import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.accessibility.AccessibilityTraceManager;
+import com.android.server.accessibility.Flags;
import com.android.server.accessibility.gestures.GestureUtils;
/**
@@ -261,8 +262,12 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
}
mDelegatingState = new DelegatingState();
- mDetectingState = new DetectingState(context);
- mViewportDraggingState = new ViewportDraggingState();
+ mDetectingState = Flags.enableMagnificationMultipleFingerMultipleTapGesture()
+ ? new DetectingStateWithMultiFinger(context)
+ : new DetectingState(context);
+ mViewportDraggingState = Flags.enableMagnificationMultipleFingerMultipleTapGesture()
+ ? new ViewportDraggingStateWithMultiFinger()
+ : new ViewportDraggingState();
mPanningScalingState = new PanningScalingState(context);
mSinglePanningState = new SinglePanningState(context);
mFullScreenMagnificationVibrationHelper = fullScreenMagnificationVibrationHelper;
@@ -634,6 +639,62 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
}
}
+ final class ViewportDraggingStateWithMultiFinger extends ViewportDraggingState {
+ // LINT.IfChange(viewport_dragging_state_with_multi_finger)
+ @Override
+ public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags)
+ throws GestureException {
+ final int action = event.getActionMasked();
+ switch (action) {
+ case ACTION_POINTER_DOWN: {
+ clearAndTransitToPanningScalingState();
+ }
+ break;
+ case ACTION_MOVE: {
+ if (event.getPointerCount() > 2) {
+ throw new GestureException("Should have one pointer down.");
+ }
+ final float eventX = event.getX();
+ final float eventY = event.getY();
+ if (mFullScreenMagnificationController.magnificationRegionContains(
+ mDisplayId, eventX, eventY)) {
+ mFullScreenMagnificationController.setCenter(mDisplayId, eventX, eventY,
+ /* animate */ mLastMoveOutsideMagnifiedRegion,
+ AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+ mLastMoveOutsideMagnifiedRegion = false;
+ } else {
+ mLastMoveOutsideMagnifiedRegion = true;
+ }
+ }
+ break;
+
+ case ACTION_UP:
+ case ACTION_CANCEL: {
+ // If mScaleToRecoverAfterDraggingEnd >= 1.0, the dragging state is triggered
+ // by zoom in temporary, and the magnifier needs to recover to original scale
+ // after exiting dragging state.
+ // Otherwise, the magnifier should be disabled.
+ if (mScaleToRecoverAfterDraggingEnd >= 1.0f) {
+ zoomToScale(mScaleToRecoverAfterDraggingEnd, event.getX(),
+ event.getY());
+ } else {
+ zoomOff();
+ }
+ clear();
+ mScaleToRecoverAfterDraggingEnd = Float.NaN;
+ transitionTo(mDetectingState);
+ }
+ break;
+
+ case ACTION_DOWN: {
+ throw new GestureException(
+ "Unexpected event type: " + MotionEvent.actionToString(action));
+ }
+ }
+ }
+ // LINT.ThenChange(:viewport_dragging_state)
+ }
+
/**
* This class handles motion events when the event dispatcher has
* determined that the user is performing a single-finger drag of the
@@ -643,17 +704,18 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
* of the finger, and any part of the screen is reachable without lifting the finger.
* This makes it the preferable mode for tasks like reading text spanning full screen width.
*/
- final class ViewportDraggingState implements State {
+ class ViewportDraggingState implements State {
/**
* The cached scale for recovering after dragging ends.
* If the scale >= 1.0, the magnifier needs to recover to scale.
* Otherwise, the magnifier should be disabled.
*/
- @VisibleForTesting float mScaleToRecoverAfterDraggingEnd = Float.NaN;
+ @VisibleForTesting protected float mScaleToRecoverAfterDraggingEnd = Float.NaN;
- private boolean mLastMoveOutsideMagnifiedRegion;
+ protected boolean mLastMoveOutsideMagnifiedRegion;
+ // LINT.IfChange(viewport_dragging_state)
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags)
throws GestureException {
@@ -706,6 +768,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
}
}
}
+ // LINT.ThenChange(:viewport_dragging_state_with_multi_finger)
private boolean isAlwaysOnMagnificationEnabled() {
return mFullScreenMagnificationController.isAlwaysOnMagnificationEnabled();
@@ -732,7 +795,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
? mFullScreenMagnificationController.getScale(mDisplayId) : Float.NaN;
}
- private void clearAndTransitToPanningScalingState() {
+ protected void clearAndTransitToPanningScalingState() {
final float scaleToRecovery = mScaleToRecoverAfterDraggingEnd;
clear();
mScaleToRecoverAfterDraggingEnd = scaleToRecovery;
@@ -791,36 +854,220 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
}
}
+ final class DetectingStateWithMultiFinger extends DetectingState {
+ // A flag set to true when two fingers have touched down.
+ // Used to indicate what next finger action should be.
+ private boolean mIsTwoFingerCountReached = false;
+ // A tap counts when two fingers are down and up once.
+ private int mCompletedTapCount = 0;
+ DetectingStateWithMultiFinger(Context context) {
+ super(context);
+ }
+
+ // LINT.IfChange(detecting_state_with_multi_finger)
+ @Override
+ public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ cacheDelayedMotionEvent(event, rawEvent, policyFlags);
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN: {
+ mLastDetectingDownEventTime = event.getDownTime();
+ mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
+
+ mFirstPointerDownLocation.set(event.getX(), event.getY());
+
+ if (!mFullScreenMagnificationController.magnificationRegionContains(
+ mDisplayId, event.getX(), event.getY())) {
+
+ transitionToDelegatingStateAndClear();
+
+ } else if (isMultiTapTriggered(2 /* taps */)) {
+
+ // 3tap and hold
+ afterLongTapTimeoutTransitionToDraggingState(event);
+
+ } else if (isTapOutOfDistanceSlop()) {
+
+ transitionToDelegatingStateAndClear();
+
+ } else if (mDetectSingleFingerTripleTap
+ || mDetectTwoFingerTripleTap
+ // If activated, delay an ACTION_DOWN for mMultiTapMaxDelay
+ // to ensure reachability of
+ // STATE_PANNING_SCALING(triggerable with ACTION_POINTER_DOWN)
+ || isActivated()) {
+
+ afterMultiTapTimeoutTransitionToDelegatingState();
+
+ } else {
+
+ // Delegate pending events without delay
+ transitionToDelegatingStateAndClear();
+ }
+ }
+ break;
+ case ACTION_POINTER_DOWN: {
+ mIsTwoFingerCountReached = mDetectTwoFingerTripleTap
+ && event.getPointerCount() == 2;
+ mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
+
+ if (isActivated() && event.getPointerCount() == 2) {
+ storePointerDownLocation(mSecondPointerDownLocation, event);
+ mHandler.sendEmptyMessageDelayed(MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE,
+ ViewConfiguration.getTapTimeout());
+ } else if (mIsTwoFingerCountReached) {
+ // Placing two-finger triple-taps behind isActivated to avoid
+ // blocking panning scaling state
+ if (isMultiFingerMultiTapTriggered(/* targetTapCount= */ 2, event)) {
+ // 3tap and hold
+ afterLongTapTimeoutTransitionToDraggingState(event);
+ } else {
+ afterMultiTapTimeoutTransitionToDelegatingState();
+ }
+ } else {
+ transitionToDelegatingStateAndClear();
+ }
+ }
+ break;
+ case ACTION_POINTER_UP: {
+ // If it is a two-finger gesture, do not transition to the delegating state
+ // to ensure the reachability of
+ // the two-finger triple tap (triggerable with ACTION_MOVE and ACTION_UP)
+ if (!mIsTwoFingerCountReached) {
+ transitionToDelegatingStateAndClear();
+ }
+ }
+ break;
+ case ACTION_MOVE: {
+ if (isFingerDown()
+ && distance(mLastDown, /* move */ event) > mSwipeMinDistance) {
+ // Swipe detected - transition immediately
+
+ // For convenience, viewport dragging takes precedence
+ // over insta-delegating on 3tap&swipe
+ // (which is a rare combo to be used aside from magnification)
+ if (isMultiTapTriggered(2 /* taps */) && event.getPointerCount() == 1) {
+ transitionToViewportDraggingStateAndClear(event);
+ } else if (isActivated() && event.getPointerCount() == 2) {
+ if (mIsSinglePanningEnabled
+ && overscrollState(event, mFirstPointerDownLocation)
+ == OVERSCROLL_VERTICAL_EDGE) {
+ transitionToDelegatingStateAndClear();
+ }
+ //Primary pointer is swiping, so transit to PanningScalingState
+ transitToPanningScalingStateAndClear();
+ } else if (isMultiFingerMultiTapTriggered(/* targetTapCount= */ 2, event)
+ && event.getPointerCount() == 2) {
+ // Placing two-finger triple-taps behind isActivated to avoid
+ // blocking panning scaling state
+ transitionToViewportDraggingStateAndClear(event);
+ } else if (mIsSinglePanningEnabled
+ && isActivated()
+ && event.getPointerCount() == 1) {
+ if (overscrollState(event, mFirstPointerDownLocation)
+ == OVERSCROLL_VERTICAL_EDGE) {
+ transitionToDelegatingStateAndClear();
+ }
+ transitToSinglePanningStateAndClear();
+ } else {
+ transitionToDelegatingStateAndClear();
+ }
+ } else if (isActivated() && pointerDownValid(mSecondPointerDownLocation)
+ && distanceClosestPointerToPoint(
+ mSecondPointerDownLocation, /* move */ event) > mSwipeMinDistance) {
+ //Second pointer is swiping, so transit to PanningScalingState
+ transitToPanningScalingStateAndClear();
+ }
+ }
+ break;
+ case ACTION_UP: {
+
+ mHandler.removeMessages(MESSAGE_ON_TRIPLE_TAP_AND_HOLD);
+
+ if (!mFullScreenMagnificationController.magnificationRegionContains(
+ mDisplayId, event.getX(), event.getY())) {
+ transitionToDelegatingStateAndClear();
+
+ } else if (isMultiTapTriggered(3 /* taps */)) {
+ onTripleTap(/* up */ event);
+
+ } else if (isMultiFingerMultiTapTriggered(/* targetTapCount= */ 3, event)) {
+ onTripleTap(event);
+
+ } else if (
+ // Possible to be false on: 3tap&drag -> scale -> PTR_UP -> UP
+ isFingerDown()
+ //TODO long tap should never happen here
+ && ((timeBetween(mLastDown, mLastUp) >= mLongTapMinDelay)
+ || (distance(mLastDown, mLastUp) >= mSwipeMinDistance))
+ // If it is a two-finger but not reach 3 tap, do not transition to the
+ // delegating state to ensure the reachability of the triple tap
+ && mCompletedTapCount == 0) {
+ transitionToDelegatingStateAndClear();
+
+ }
+ }
+ break;
+ }
+ }
+ // LINT.ThenChange(:detecting_state)
+
+ @Override
+ public void clear() {
+ mCompletedTapCount = 0;
+ setShortcutTriggered(false);
+ removePendingDelayedMessages();
+ clearDelayedMotionEvents();
+ mFirstPointerDownLocation.set(Float.NaN, Float.NaN);
+ mSecondPointerDownLocation.set(Float.NaN, Float.NaN);
+ }
+
+ private boolean isMultiFingerMultiTapTriggered(int targetTapCount, MotionEvent event) {
+ if (event.getActionMasked() == ACTION_UP && mIsTwoFingerCountReached) {
+ mCompletedTapCount++;
+ mIsTwoFingerCountReached = false;
+ }
+ return mDetectTwoFingerTripleTap && mCompletedTapCount == targetTapCount;
+ }
+
+ void transitionToDelegatingStateAndClear() {
+ mCompletedTapCount = 0;
+ transitionTo(mDelegatingState);
+ sendDelayedMotionEvents();
+ removePendingDelayedMessages();
+ mFirstPointerDownLocation.set(Float.NaN, Float.NaN);
+ mSecondPointerDownLocation.set(Float.NaN, Float.NaN);
+ }
+ }
+
/**
* This class handles motion events when the event dispatch has not yet
* determined what the user is doing. It watches for various tap events.
*/
- final class DetectingState implements State, Handler.Callback {
+ class DetectingState implements State, Handler.Callback {
- private static final int MESSAGE_ON_TRIPLE_TAP_AND_HOLD = 1;
- private static final int MESSAGE_TRANSITION_TO_DELEGATING_STATE = 2;
- private static final int MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE = 3;
+ protected static final int MESSAGE_ON_TRIPLE_TAP_AND_HOLD = 1;
+ protected static final int MESSAGE_TRANSITION_TO_DELEGATING_STATE = 2;
+ protected static final int MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE = 3;
final int mLongTapMinDelay;
final int mSwipeMinDistance;
final int mMultiTapMaxDelay;
final int mMultiTapMaxDistance;
- private MotionEventInfo mDelayedEventQueue;
- MotionEvent mLastDown;
- private MotionEvent mPreLastDown;
- private MotionEvent mLastUp;
- private MotionEvent mPreLastUp;
- private PointF mSecondPointerDownLocation = new PointF(Float.NaN, Float.NaN);
+ protected MotionEventInfo mDelayedEventQueue;
+ protected MotionEvent mLastDown;
+ protected MotionEvent mPreLastDown;
+ protected MotionEvent mLastUp;
+ protected MotionEvent mPreLastUp;
- private long mLastDetectingDownEventTime;
+ protected PointF mFirstPointerDownLocation = new PointF(Float.NaN, Float.NaN);
+ protected PointF mSecondPointerDownLocation = new PointF(Float.NaN, Float.NaN);
+ protected long mLastDetectingDownEventTime;
@VisibleForTesting boolean mShortcutTriggered;
@VisibleForTesting Handler mHandler = new Handler(Looper.getMainLooper(), this);
- private PointF mFirstPointerDownLocation = new PointF(Float.NaN, Float.NaN);
-
DetectingState(Context context) {
mLongTapMinDelay = ViewConfiguration.getLongPressTimeout();
mMultiTapMaxDelay = ViewConfiguration.getDoubleTapTimeout()
@@ -855,6 +1102,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
return true;
}
+ // LINT.IfChange(detecting_state)
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
cacheDelayedMotionEvent(event, rawEvent, policyFlags);
@@ -969,23 +1217,24 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
break;
}
}
+ // LINT.ThenChange(:detecting_state_with_multi_finger)
- private void storePointerDownLocation(PointF pointerDownLocation, MotionEvent event) {
+ protected void storePointerDownLocation(PointF pointerDownLocation, MotionEvent event) {
final int index = event.getActionIndex();
pointerDownLocation.set(event.getX(index), event.getY(index));
}
- private boolean pointerDownValid(PointF pointerDownLocation) {
+ protected boolean pointerDownValid(PointF pointerDownLocation) {
return !(Float.isNaN(pointerDownLocation.x) && Float.isNaN(
pointerDownLocation.y));
}
- private void transitToPanningScalingStateAndClear() {
+ protected void transitToPanningScalingStateAndClear() {
transitionTo(mPanningScalingState);
clear();
}
- private void transitToSinglePanningStateAndClear() {
+ protected void transitToSinglePanningStateAndClear() {
transitionTo(mSinglePanningState);
clear();
}
@@ -1016,7 +1265,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
return mLastDown != null;
}
- private long timeBetween(@Nullable MotionEvent a, @Nullable MotionEvent b) {
+ protected long timeBetween(@Nullable MotionEvent a, @Nullable MotionEvent b) {
if (a == null && b == null) return 0;
return abs(timeOf(a) - timeOf(b));
}
@@ -1061,13 +1310,13 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
mSecondPointerDownLocation.set(Float.NaN, Float.NaN);
}
- private void removePendingDelayedMessages() {
+ protected void removePendingDelayedMessages() {
mHandler.removeMessages(MESSAGE_ON_TRIPLE_TAP_AND_HOLD);
mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
mHandler.removeMessages(MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE);
}
- private void cacheDelayedMotionEvent(MotionEvent event, MotionEvent rawEvent,
+ protected void cacheDelayedMotionEvent(MotionEvent event, MotionEvent rawEvent,
int policyFlags) {
if (event.getActionMasked() == ACTION_DOWN) {
mPreLastDown = mLastDown;
@@ -1090,7 +1339,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
}
}
- private void sendDelayedMotionEvents() {
+ protected void sendDelayedMotionEvents() {
if (mDelayedEventQueue == null) {
return;
}
@@ -1112,7 +1361,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
} while (mDelayedEventQueue != null);
}
- private void clearDelayedMotionEvents() {
+ protected void clearDelayedMotionEvents() {
while (mDelayedEventQueue != null) {
MotionEventInfo info = mDelayedEventQueue;
mDelayedEventQueue = info.mNext;
@@ -1136,7 +1385,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
* 1. direct three tap gesture
* 2. one tap while shortcut triggered (it counts as two taps).
*/
- private void onTripleTap(MotionEvent up) {
+ protected void onTripleTap(MotionEvent up) {
if (DEBUG_DETECTING) {
Slog.i(mLogTag, "onTripleTap(); delayed: "
+ MotionEventInfo.toString(mDelayedEventQueue));
@@ -1156,7 +1405,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
clear();
}
- private boolean isActivated() {
+ protected boolean isActivated() {
return mFullScreenMagnificationController.isActivated(mDisplayId);
}
@@ -1167,6 +1416,8 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
// Only log the 3tap and hold event
if (!shortcutTriggered) {
+ // TODO:(b/309534286): Add metrics for two-finger triple-tap and fix
+ // the log two-finger bug before enabling the flag
// Triple tap and hold also belongs to triple tap event
final boolean enabled = !isActivated();
mMagnificationLogger.logMagnificationTripleTap(enabled);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index a2e7cf3f1bb2..fd2cf6d4bb5f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -55,6 +55,10 @@ import android.os.Message;
import android.os.UserHandle;
import android.os.VibrationEffect;
import android.os.Vibrator;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import android.testing.TestableContext;
import android.util.DebugUtils;
@@ -69,6 +73,7 @@ import com.android.internal.util.ConcurrentUtils;
import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.EventStreamTransformation;
+import com.android.server.accessibility.Flags;
import com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationInfoChangedCallback;
import com.android.server.testutils.OffsettableClock;
import com.android.server.testutils.TestHandler;
@@ -134,6 +139,9 @@ import java.util.function.IntConsumer;
@RunWith(AndroidJUnit4.class)
public class FullScreenMagnificationGestureHandlerTest {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
public static final int STATE_IDLE = 1;
public static final int STATE_ACTIVATED = 2;
public static final int STATE_SHORTCUT_TRIGGERED = 3;
@@ -425,6 +433,7 @@ public class FullScreenMagnificationGestureHandlerTest {
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
public void testDisablingTripleTap_removesInputLag() {
mMgh = newInstance(/* detectSingleFingerTripleTap */ false,
/* detectTwoFingerTripleTap */ true, /* detectShortcut */ true);
@@ -436,6 +445,18 @@ public class FullScreenMagnificationGestureHandlerTest {
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
+ public void testDisablingSingleFingerTripleTapAndTwoFingerTripleTap_removesInputLag() {
+ mMgh = newInstance(/* detectSingleFingerTripleTap */ false,
+ /* detectTwoFingerTripleTap */ false, /* detectShortcut */ true);
+ goFromStateIdleTo(STATE_IDLE);
+ allowEventDelegation();
+ tap();
+ // no fast forward
+ verify(mMgh.getNext(), times(2)).onMotionEvent(any(), any(), anyInt());
+ }
+
+ @Test
public void testLongTapAfterShortcutTriggered_neverLogMagnificationTripleTap() {
goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED);
@@ -510,6 +531,54 @@ public class FullScreenMagnificationGestureHandlerTest {
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
+ public void testTwoFingerTripleTap_StateIsIdle_shouldInActivated() {
+ goFromStateIdleTo(STATE_IDLE);
+
+ twoFingerTap();
+ twoFingerTap();
+ twoFingerTap();
+
+ assertIn(STATE_ACTIVATED);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
+ public void testTwoFingerTripleTap_StateIsActivated_shouldInIdle() {
+ goFromStateIdleTo(STATE_ACTIVATED);
+
+ twoFingerTap();
+ twoFingerTap();
+ twoFingerTap();
+
+ assertIn(STATE_IDLE);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
+ public void testTwoFingerTripleTapAndHold_StateIsIdle_shouldZoomsImmediately() {
+ goFromStateIdleTo(STATE_IDLE);
+
+ twoFingerTap();
+ twoFingerTap();
+ twoFingerTapAndHold();
+
+ assertIn(STATE_NON_ACTIVATED_ZOOMED_TMP);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
+ public void testTwoFingerTripleSwipeAndHold_StateIsIdle_shouldZoomsImmediately() {
+ goFromStateIdleTo(STATE_IDLE);
+
+ twoFingerTap();
+ twoFingerTap();
+ twoFingerSwipeAndHold();
+
+ assertIn(STATE_NON_ACTIVATED_ZOOMED_TMP);
+ }
+
+ @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.
@@ -1258,6 +1327,30 @@ public class FullScreenMagnificationGestureHandlerTest {
send(upEvent());
}
+ private void twoFingerTap() {
+ send(downEvent());
+ send(pointerEvent(ACTION_POINTER_DOWN, DEFAULT_X * 2, DEFAULT_Y));
+ send(pointerEvent(ACTION_POINTER_UP, DEFAULT_X * 2, DEFAULT_Y));
+ send(upEvent());
+ }
+
+ private void twoFingerTapAndHold() {
+ send(downEvent());
+ send(pointerEvent(ACTION_POINTER_DOWN, DEFAULT_X * 2, DEFAULT_Y));
+ fastForward(2000);
+ }
+
+ private void twoFingerSwipeAndHold() {
+ PointF pointer1 = DEFAULT_POINT;
+ PointF pointer2 = new PointF(DEFAULT_X * 1.5f, DEFAULT_Y);
+
+ send(downEvent());
+ send(pointerEvent(ACTION_POINTER_DOWN, new PointF[] {pointer1, pointer2}, 1));
+ final float sWipeMinDistance = ViewConfiguration.get(mContext).getScaledTouchSlop();
+ pointer1.offset(sWipeMinDistance + 1, 0);
+ send(pointerEvent(ACTION_MOVE, new PointF[] {pointer1, pointer2}, 0));
+ }
+
private void triggerShortcut() {
mMgh.notifyShortcutTriggered();
}