fix(MultiFingerMultiTap): Delete lots of duplicated code
Narrow the scope of the flags, making them as specific as possible to indicate the exact areas they affect. Avoid duplicating entire classes, so that the logical differences between the flag being on and off are immediately apparent.
Bug: 356322729
Test: atest -c FullScreenMagnificationGestureHandlerTest
Flag: com.android.server.accessibility.enable_magnification_multiple_finger_multiple_tap_gesture
Change-Id: I9d65bee086eb9ad96d5851a851f8d0853cd766b4
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 5c6f99a..aa57e0b 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -305,12 +305,8 @@
}
mDelegatingState = new DelegatingState();
- mDetectingState = Flags.enableMagnificationMultipleFingerMultipleTapGesture()
- ? new DetectingStateWithMultiFinger(context)
- : new DetectingState(context);
- mViewportDraggingState = Flags.enableMagnificationMultipleFingerMultipleTapGesture()
- ? new ViewportDraggingStateWithMultiFinger()
- : new ViewportDraggingState();
+ mDetectingState = new DetectingState(context);
+ mViewportDraggingState = new ViewportDraggingState();
mPanningScalingState = new PanningScalingState(context);
mSinglePanningState = new SinglePanningState(context);
mFullScreenMagnificationVibrationHelper = fullScreenMagnificationVibrationHelper;
@@ -701,62 +697,6 @@
}
}
- 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
@@ -777,7 +717,6 @@
protected boolean mLastMoveOutsideMagnifiedRegion;
- // LINT.IfChange(viewport_dragging_state)
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags)
throws GestureException {
@@ -788,7 +727,11 @@
}
break;
case ACTION_MOVE: {
- if (event.getPointerCount() != 1) {
+ if (Flags.enableMagnificationMultipleFingerMultipleTapGesture()) {
+ if (event.getPointerCount() > 2) {
+ throw new GestureException("Should have at most two pointers down.");
+ }
+ } else if (event.getPointerCount() != 1) {
throw new GestureException("Should have one pointer down.");
}
final float eventX = event.getX();
@@ -823,14 +766,20 @@
}
break;
- case ACTION_DOWN:
case ACTION_POINTER_UP: {
+ if (!Flags.enableMagnificationMultipleFingerMultipleTapGesture()) {
+ throw new GestureException(
+ "Unexpected event type: " + MotionEvent.actionToString(action));
+ }
+ }
+ break;
+
+ case ACTION_DOWN: {
throw new GestureException(
"Unexpected event type: " + MotionEvent.actionToString(action));
}
}
}
- // LINT.ThenChange(:viewport_dragging_state_with_multi_finger)
private boolean isAlwaysOnMagnificationEnabled() {
return mFullScreenMagnificationController.isAlwaysOnMagnificationEnabled();
@@ -916,270 +865,31 @@
}
}
- final class DetectingStateWithMultiFinger extends DetectingState {
- private static final int TWO_FINGER_GESTURE_MAX_TAPS = 2;
- // 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 (event.getPointerCount() == 2) {
- if (isMultiFingerMultiTapTriggered(
- TWO_FINGER_GESTURE_MAX_TAPS - 1, event)) {
- // 3tap and hold
- afterLongTapTimeoutTransitionToDraggingState(event);
- } else {
- if (mDetectTwoFingerTripleTap) {
- // If mDetectTwoFingerTripleTap, delay transition to the delegating
- // state for mMultiTapMaxDelay to ensure reachability of
- // multi finger multi tap
- afterMultiTapTimeoutTransitionToDelegatingState();
- }
-
- if (isActivated()) {
- // If activated, delay transition to the panning scaling
- // state for tap timeout to ensure reachability of
- // multi finger multi tap
- storePointerDownLocation(mSecondPointerDownLocation, event);
- mHandler.sendEmptyMessageDelayed(
- MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE,
- ViewConfiguration.getTapTimeout());
- }
- }
- } 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 (isMultiFingerMultiTapTriggered(
- TWO_FINGER_GESTURE_MAX_TAPS - 1, event)
- && event.getPointerCount() == 2) {
- transitionToViewportDraggingStateAndClear(event);
- } else if (isActivated() && event.getPointerCount() == 2) {
- if (mOverscrollHandler != null
- && overscrollState(event, mFirstPointerDownLocation)
- == OVERSCROLL_VERTICAL_EDGE) {
- transitionToDelegatingStateAndClear();
- } else {
- //Primary pointer is swiping, so transit to PanningScalingState
- transitToPanningScalingStateAndClear();
- }
- } else if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()
- && isActivated()
- && event.getPointerCount() == 1) {
- if (mOverscrollHandler != null
- && overscrollState(event, mFirstPointerDownLocation)
- == OVERSCROLL_VERTICAL_EDGE) {
- transitionToDelegatingStateAndClear();
- } else if (overscrollState(event, mFirstPointerDownLocation)
- != OVERSCROLL_NONE) {
- transitionToDelegatingStateAndClear();
- } else {
- transitToSinglePanningStateAndClear();
- }
- } else if (!mIsTwoFingerCountReached) {
- // 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_UP)
- transitionToDelegatingStateAndClear();
- }
- } else if (isActivated() && pointerDownValid(mSecondPointerDownLocation)
- && distanceClosestPointerToPoint(
- mSecondPointerDownLocation, /* move */ event) > mSwipeMinDistance) {
- // Second pointer is swiping, so transit to PanningScalingState
- // Delay an ACTION_MOVE for tap timeout to ensure it is not trigger from
- // multi finger multi tap
- storePointerDownLocation(mSecondPointerDownLocation, event);
- mHandler.sendEmptyMessageDelayed(
- MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE,
- ViewConfiguration.getTapTimeout());
- }
- }
- break;
- case ACTION_UP: {
-
- mHandler.removeMessages(MESSAGE_ON_TRIPLE_TAP_AND_HOLD);
- mHandler.removeMessages(MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE);
-
- if (!mFullScreenMagnificationController.magnificationRegionContains(
- mDisplayId, event.getX(), event.getY())) {
- transitionToDelegatingStateAndClear();
-
- } else if (isMultiFingerMultiTapTriggered(TWO_FINGER_GESTURE_MAX_TAPS, event)) {
- // Placing multiple fingers before a single finger, because achieving a
- // multi finger multi tap also means achieving a single finger triple tap
- onTripleTap(event);
-
- } else if (isMultiTapTriggered(3 /* taps */)) {
- onTripleTap(/* up */ 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;
- }
-
- if (mDetectTwoFingerTripleTap && mCompletedTapCount > TWO_FINGER_GESTURE_MAX_TAPS - 1) {
- final boolean enabled = !isActivated();
- mMagnificationLogger.logMagnificationTwoFingerTripleTap(enabled);
- }
- return mDetectTwoFingerTripleTap && mCompletedTapCount == targetTapCount;
- }
-
- void transitionToDelegatingStateAndClear() {
- mCompletedTapCount = 0;
- transitionTo(mDelegatingState);
- sendDelayedMotionEvents();
- removePendingDelayedMessages();
- mFirstPointerDownLocation.set(Float.NaN, Float.NaN);
- mSecondPointerDownLocation.set(Float.NaN, Float.NaN);
- }
-
- void transitionToViewportDraggingStateAndClear(MotionEvent down) {
-
- if (DEBUG_DETECTING) Slog.i(mLogTag, "onTripleTapAndHold()");
- final boolean shortcutTriggered = mShortcutTriggered;
-
- // Only log the 3tap and hold event
- if (!shortcutTriggered) {
- final boolean enabled = !isActivated();
- if (mCompletedTapCount == TWO_FINGER_GESTURE_MAX_TAPS - 1) {
- // Two finger triple tap and hold
- mMagnificationLogger.logMagnificationTwoFingerTripleTap(enabled);
- } else {
- // Triple tap and hold also belongs to triple tap event
- mMagnificationLogger.logMagnificationTripleTap(enabled);
- }
- }
- clear();
-
- mViewportDraggingState.prepareForZoomInTemporary(shortcutTriggered);
- zoomInTemporary(down.getX(), down.getY(), shortcutTriggered);
- transitionTo(mViewportDraggingState);
- }
- }
-
/**
* This class handles motion events when the event dispatch has not yet
* determined what the user is doing. It watches for various tap events.
*/
class DetectingState implements State, Handler.Callback {
- 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;
+ 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;
final int mLongTapMinDelay;
final int mSwipeMinDistance;
final int mMultiTapMaxDelay;
final int mMultiTapMaxDistance;
+ @Nullable final TwoFingerDoubleTapHandler mTwoFingerDoubleTapHandler;
- protected MotionEventInfo mDelayedEventQueue;
- protected MotionEvent mLastDown;
- protected MotionEvent mPreLastDown;
- protected MotionEvent mLastUp;
- protected MotionEvent mPreLastUp;
+ private MotionEventInfo mDelayedEventQueue;
+ private MotionEvent mLastDown;
+ private MotionEvent mPreLastDown;
+ private MotionEvent mLastUp;
+ private MotionEvent mPreLastUp;
- protected PointF mFirstPointerDownLocation = new PointF(Float.NaN, Float.NaN);
- protected PointF mSecondPointerDownLocation = new PointF(Float.NaN, Float.NaN);
- protected long mLastDetectingDownEventTime;
+ private PointF mFirstPointerDownLocation = new PointF(Float.NaN, Float.NaN);
+ private PointF mSecondPointerDownLocation = new PointF(Float.NaN, Float.NaN);
+ private long mLastDetectingDownEventTime;
@VisibleForTesting boolean mShortcutTriggered;
@@ -1191,6 +901,9 @@
MagnificationGestureMatcher.getMagnificationMultiTapTimeout(context);
mSwipeMinDistance = ViewConfiguration.get(context).getScaledTouchSlop();
mMultiTapMaxDistance = ViewConfiguration.get(context).getScaledDoubleTapSlop();
+ mTwoFingerDoubleTapHandler =
+ Flags.enableMagnificationMultipleFingerMultipleTapGesture()
+ ? new TwoFingerDoubleTapHandler() : null;
}
@Override
@@ -1218,7 +931,6 @@
return true;
}
- // LINT.IfChange(detecting_state)
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
cacheDelayedMotionEvent(event, rawEvent, policyFlags);
@@ -1244,6 +956,7 @@
transitionToDelegatingStateAndClear();
} else if (mDetectSingleFingerTripleTap
+ || (mTwoFingerDoubleTapHandler != null && mDetectTwoFingerTripleTap)
// If activated, delay an ACTION_DOWN for mMultiTapMaxDelay
// to ensure reachability of
// STATE_PANNING_SCALING(triggerable with ACTION_POINTER_DOWN)
@@ -1259,6 +972,12 @@
}
break;
case ACTION_POINTER_DOWN: {
+ if (mTwoFingerDoubleTapHandler != null) {
+ mTwoFingerDoubleTapHandler.onPointerDown(event);
+ break;
+ }
+
+ // LINT.IfChange(action_pointer_down)
if (isActivated() && event.getPointerCount() == 2) {
storePointerDownLocation(mSecondPointerDownLocation, event);
mHandler.sendEmptyMessageDelayed(MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE,
@@ -1266,13 +985,26 @@
} else {
transitionToDelegatingStateAndClear();
}
+ // LINT.ThenChange(:action_pointer_down_with_multi_finger)
}
break;
case ACTION_POINTER_UP: {
+ if (mTwoFingerDoubleTapHandler != null) {
+ mTwoFingerDoubleTapHandler.onPointerUp();
+ break;
+ }
+ // LINT.IfChange(action_pointer_up)
transitionToDelegatingStateAndClear();
+ // LINT.ThenChange(:action_pointer_up_with_multi_finger)
}
break;
case ACTION_MOVE: {
+ if (mTwoFingerDoubleTapHandler != null) {
+ mTwoFingerDoubleTapHandler.onMove(event);
+ break;
+ }
+
+ // LINT.IfChange(action_move)
if (isFingerDown()
&& distance(mLastDown, /* move */ event) > mSwipeMinDistance) {
// Swipe detected - transition immediately
@@ -1313,12 +1045,20 @@
//Second pointer is swiping, so transit to PanningScalingState
transitToPanningScalingStateAndClear();
}
+ // LINT.ThenChange(:action_move_with_multi_finger)
}
break;
case ACTION_UP: {
mHandler.removeMessages(MESSAGE_ON_TRIPLE_TAP_AND_HOLD);
+ if (mTwoFingerDoubleTapHandler != null) {
+ mHandler.removeMessages(MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE);
+ mTwoFingerDoubleTapHandler.onUp(event);
+ break;
+ }
+
+ // LINT.IfChange(action_up)
if (!mFullScreenMagnificationController.magnificationRegionContains(
mDisplayId, event.getX(), event.getY())) {
transitionToDelegatingStateAndClear();
@@ -1335,11 +1075,11 @@
transitionToDelegatingStateAndClear();
}
+ // LINT.ThenChange(:action_up_with_multi_finger)
}
break;
}
}
- // LINT.ThenChange(:detecting_state_with_multi_finger)
protected void storePointerDownLocation(PointF pointerDownLocation, MotionEvent event) {
final int index = event.getActionIndex();
@@ -1425,6 +1165,9 @@
@Override
public void clear() {
+ if (mTwoFingerDoubleTapHandler != null) {
+ mTwoFingerDoubleTapHandler.mCompletedTapCount = 0;
+ }
setShortcutTriggered(false);
removePendingDelayedMessages();
clearDelayedMotionEvents();
@@ -1501,9 +1244,13 @@
}
void transitionToDelegatingStateAndClear() {
+ if (mTwoFingerDoubleTapHandler != null) {
+ mTwoFingerDoubleTapHandler.mCompletedTapCount = 0;
+ }
transitionTo(mDelegatingState);
sendDelayedMotionEvents();
removePendingDelayedMessages();
+ mFirstPointerDownLocation.set(Float.NaN, Float.NaN);
mSecondPointerDownLocation.set(Float.NaN, Float.NaN);
}
@@ -1543,9 +1290,15 @@
// Only log the 3tap and hold event
if (!shortcutTriggered) {
- // Triple tap and hold also belongs to triple tap event
final boolean enabled = !isActivated();
- mMagnificationLogger.logMagnificationTripleTap(enabled);
+ if (mTwoFingerDoubleTapHandler != null
+ && mTwoFingerDoubleTapHandler.shouldLogTwoFingerDoubleTap()) {
+ // Two finger double tap and hold
+ mMagnificationLogger.logMagnificationTwoFingerTripleTap(enabled);
+ } else {
+ // Triple tap and hold also belongs to triple tap event
+ mMagnificationLogger.logMagnificationTripleTap(enabled);
+ }
}
clear();
@@ -1604,6 +1357,173 @@
}
return false;
}
+
+ final class TwoFingerDoubleTapHandler {
+ private static final int TWO_FINGER_GESTURE_MAX_TAPS = 2;
+ // A tap counts when two fingers are down and up once.
+ private int mCompletedTapCount;
+ // A flag set to true when two fingers have touched down.
+ // Used to indicate what next finger action should be.
+ private boolean mIsTwoFingerCountReached;
+
+ TwoFingerDoubleTapHandler() {
+ mCompletedTapCount = 0;
+ mIsTwoFingerCountReached = false;
+ }
+
+ private void onPointerDown(MotionEvent event) {
+ mIsTwoFingerCountReached = mDetectTwoFingerTripleTap
+ && event.getPointerCount() == 2;
+ mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
+
+ // LINT.IfChange(action_pointer_down_with_multi_finger)
+ if (event.getPointerCount() == 2) {
+ if (isMultiFingerMultiTapTriggered(
+ TWO_FINGER_GESTURE_MAX_TAPS - 1, event)) {
+ // 3tap and hold
+ afterLongTapTimeoutTransitionToDraggingState(event);
+ } else {
+ if (mDetectTwoFingerTripleTap) {
+ // If mDetectTwoFingerTripleTap, delay transition to the delegating
+ // state for mMultiTapMaxDelay to ensure reachability of
+ // multi finger multi tap
+ afterMultiTapTimeoutTransitionToDelegatingState();
+ }
+
+ if (isActivated()) {
+ // If activated, delay transition to the panning scaling
+ // state for tap timeout to ensure reachability of
+ // multi finger multi tap
+ storePointerDownLocation(mSecondPointerDownLocation, event);
+ mHandler.sendEmptyMessageDelayed(
+ MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE,
+ ViewConfiguration.getTapTimeout());
+ }
+ }
+ } else {
+ transitionToDelegatingStateAndClear();
+ }
+ // LINT.ThenChange(:action_pointer_down)
+ }
+
+ private void onMove(MotionEvent event) {
+ // LINT.IfChange(action_move_with_multi_finger)
+ 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 (isMultiFingerMultiTapTriggered(
+ TWO_FINGER_GESTURE_MAX_TAPS - 1, event)
+ && event.getPointerCount() == 2) {
+ transitionToViewportDraggingStateAndClear(event);
+ } else if (isActivated() && event.getPointerCount() == 2) {
+ if (mOverscrollHandler != null
+ && overscrollState(event, mFirstPointerDownLocation)
+ == OVERSCROLL_VERTICAL_EDGE) {
+ transitionToDelegatingStateAndClear();
+ } else {
+ //Primary pointer is swiping, so transit to PanningScalingState
+ transitToPanningScalingStateAndClear();
+ }
+ } else if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()
+ && isActivated()
+ && event.getPointerCount() == 1) {
+ if (mOverscrollHandler != null
+ && overscrollState(event, mFirstPointerDownLocation)
+ == OVERSCROLL_VERTICAL_EDGE) {
+ transitionToDelegatingStateAndClear();
+ } else if (overscrollState(event, mFirstPointerDownLocation)
+ != OVERSCROLL_NONE) {
+ transitionToDelegatingStateAndClear();
+ } else {
+ transitToSinglePanningStateAndClear();
+ }
+ } else if (!mIsTwoFingerCountReached) {
+ // 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_UP)
+ transitionToDelegatingStateAndClear();
+ }
+ } else if (isActivated() && pointerDownValid(mSecondPointerDownLocation)
+ && distanceClosestPointerToPoint(
+ mSecondPointerDownLocation, /* move */ event) > mSwipeMinDistance) {
+ // Second pointer is swiping, so transit to PanningScalingState
+ // Delay an ACTION_MOVE for tap timeout to ensure it is not trigger from
+ // multi finger multi tap
+ storePointerDownLocation(mSecondPointerDownLocation, event);
+ mHandler.sendEmptyMessageDelayed(
+ MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE,
+ ViewConfiguration.getTapTimeout());
+ }
+ // LINT.ThenChange(:action_move)
+ }
+
+ private void onPointerUp() {
+ // 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)
+ // LINT.IfChange(action_pointer_up_with_multi_finger)
+ if (!mIsTwoFingerCountReached) {
+ transitionToDelegatingStateAndClear();
+ }
+ // LINT.ThenChange(:action_pointer_up)
+ }
+
+ private void onUp(MotionEvent event) {
+ // LINT.IfChange(action_up_with_multi_finger)
+ if (!mFullScreenMagnificationController.magnificationRegionContains(
+ mDisplayId, event.getX(), event.getY())) {
+ transitionToDelegatingStateAndClear();
+
+ } else if (isMultiFingerMultiTapTriggered(
+ TWO_FINGER_GESTURE_MAX_TAPS, event)) {
+ // Placing multiple fingers before a single finger, because achieving a
+ // multi finger multi tap also means achieving a single finger
+ // triple tap
+ onTripleTap(event);
+
+ } else if (isMultiTapTriggered(3 /* taps */)) {
+ onTripleTap(/* up */ 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();
+ }
+ // LINT.ThenChange(:action_up)
+ }
+
+ private boolean isMultiFingerMultiTapTriggered(int targetTapCount, MotionEvent event) {
+ if (event.getActionMasked() == ACTION_UP && mIsTwoFingerCountReached) {
+ mCompletedTapCount++;
+ mIsTwoFingerCountReached = false;
+ }
+
+ if (mDetectTwoFingerTripleTap
+ && mCompletedTapCount > TWO_FINGER_GESTURE_MAX_TAPS - 1) {
+ final boolean enabled = !isActivated();
+ mMagnificationLogger.logMagnificationTwoFingerTripleTap(enabled);
+ }
+ return mDetectTwoFingerTripleTap && mCompletedTapCount == targetTapCount;
+ }
+
+ private boolean shouldLogTwoFingerDoubleTap() {
+ return mCompletedTapCount
+ == TwoFingerDoubleTapHandler.TWO_FINGER_GESTURE_MAX_TAPS - 1;
+ }
+ }
}
private void zoomInTemporary(float centerX, float centerY, boolean shortcutTriggered) {