Add multi-finger double tap and hold gestures.
This CL includes two, three and four finger double-tap and hold.
Bug: 136131815
Test: atest GestureManifoldTest TouchExplorerTest AccessibilityGestureDetectorTest
Change-Id: I4c0a95a4ac2d13e0a7e2c7920df619063ec1cfc0
diff --git a/api/current.txt b/api/current.txt
index 4887c66..6fe737c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2876,6 +2876,7 @@
method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo);
method public boolean takeScreenshot(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.accessibilityservice.AccessibilityService.ScreenshotResult>);
field public static final int GESTURE_2_FINGER_DOUBLE_TAP = 20; // 0x14
+ field public static final int GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD = 40; // 0x28
field public static final int GESTURE_2_FINGER_SINGLE_TAP = 19; // 0x13
field public static final int GESTURE_2_FINGER_SWIPE_DOWN = 26; // 0x1a
field public static final int GESTURE_2_FINGER_SWIPE_LEFT = 27; // 0x1b
@@ -2883,6 +2884,7 @@
field public static final int GESTURE_2_FINGER_SWIPE_UP = 25; // 0x19
field public static final int GESTURE_2_FINGER_TRIPLE_TAP = 21; // 0x15
field public static final int GESTURE_3_FINGER_DOUBLE_TAP = 23; // 0x17
+ field public static final int GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD = 41; // 0x29
field public static final int GESTURE_3_FINGER_SINGLE_TAP = 22; // 0x16
field public static final int GESTURE_3_FINGER_SWIPE_DOWN = 30; // 0x1e
field public static final int GESTURE_3_FINGER_SWIPE_LEFT = 31; // 0x1f
@@ -2890,6 +2892,7 @@
field public static final int GESTURE_3_FINGER_SWIPE_UP = 29; // 0x1d
field public static final int GESTURE_3_FINGER_TRIPLE_TAP = 24; // 0x18
field public static final int GESTURE_4_FINGER_DOUBLE_TAP = 38; // 0x26
+ field public static final int GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD = 42; // 0x2a
field public static final int GESTURE_4_FINGER_SINGLE_TAP = 37; // 0x25
field public static final int GESTURE_4_FINGER_SWIPE_DOWN = 34; // 0x22
field public static final int GESTURE_4_FINGER_SWIPE_LEFT = 35; // 0x23
diff --git a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
index ace13513..25729ab 100644
--- a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
+++ b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
@@ -18,6 +18,7 @@
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_DOWN;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_LEFT;
@@ -25,6 +26,7 @@
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_UP;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_DOWN;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_LEFT;
@@ -32,6 +34,7 @@
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_UP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SINGLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_DOWN;
import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_LEFT;
@@ -83,9 +86,11 @@
@IntDef(prefix = { "GESTURE_" }, value = {
GESTURE_2_FINGER_SINGLE_TAP,
GESTURE_2_FINGER_DOUBLE_TAP,
+ GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD,
GESTURE_2_FINGER_TRIPLE_TAP,
GESTURE_3_FINGER_SINGLE_TAP,
GESTURE_3_FINGER_DOUBLE_TAP,
+ GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD,
GESTURE_3_FINGER_TRIPLE_TAP,
GESTURE_DOUBLE_TAP,
GESTURE_DOUBLE_TAP_AND_HOLD,
@@ -114,6 +119,7 @@
GESTURE_3_FINGER_SWIPE_RIGHT,
GESTURE_3_FINGER_SWIPE_UP,
GESTURE_4_FINGER_DOUBLE_TAP,
+ GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD,
GESTURE_4_FINGER_SINGLE_TAP,
GESTURE_4_FINGER_SWIPE_DOWN,
GESTURE_4_FINGER_SWIPE_LEFT,
@@ -175,12 +181,18 @@
switch (eventType) {
case GESTURE_2_FINGER_SINGLE_TAP: return "GESTURE_2_FINGER_SINGLE_TAP";
case GESTURE_2_FINGER_DOUBLE_TAP: return "GESTURE_2_FINGER_DOUBLE_TAP";
+ case GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD:
+ return "GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD";
case GESTURE_2_FINGER_TRIPLE_TAP: return "GESTURE_2_FINGER_TRIPLE_TAP";
case GESTURE_3_FINGER_SINGLE_TAP: return "GESTURE_3_FINGER_SINGLE_TAP";
case GESTURE_3_FINGER_DOUBLE_TAP: return "GESTURE_3_FINGER_DOUBLE_TAP";
+ case GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD:
+ return "GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD";
case GESTURE_3_FINGER_TRIPLE_TAP: return "GESTURE_3_FINGER_TRIPLE_TAP";
case GESTURE_4_FINGER_SINGLE_TAP: return "GESTURE_4_FINGER_SINGLE_TAP";
case GESTURE_4_FINGER_DOUBLE_TAP: return "GESTURE_4_FINGER_DOUBLE_TAP";
+ case GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD:
+ return "GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD";
case GESTURE_4_FINGER_TRIPLE_TAP: return "GESTURE_4_FINGER_TRIPLE_TAP";
case GESTURE_DOUBLE_TAP: return "GESTURE_DOUBLE_TAP";
case GESTURE_DOUBLE_TAP_AND_HOLD: return "GESTURE_DOUBLE_TAP_AND_HOLD";
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index b65f68e..0ed6b1f 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -411,6 +411,15 @@
/** The user has performed a four-finger triple tap gesture on the touch screen. */
public static final int GESTURE_4_FINGER_TRIPLE_TAP = 39;
+ /** The user has performed a two-finger double tap and hold gesture on the touch screen. */
+ public static final int GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD = 40;
+
+ /** The user has performed a three-finger double tap and hold gesture on the touch screen. */
+ public static final int GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD = 41;
+
+ /** The user has performed a two-finger double tap and hold gesture on the touch screen. */
+ public static final int GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD = 42;
+
/**
* The {@link Intent} that must be declared as handled by the service.
*/
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
index b74be7e..a3b5a3e 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
@@ -17,6 +17,7 @@
package com.android.server.accessibility.gestures;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_DOWN;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_LEFT;
@@ -24,6 +25,7 @@
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_UP;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_DOWN;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_LEFT;
@@ -31,6 +33,7 @@
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_UP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SINGLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_DOWN;
import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_LEFT;
@@ -132,6 +135,9 @@
mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 2, 2, GESTURE_2_FINGER_DOUBLE_TAP, this));
mMultiFingerGestures.add(
+ new MultiFingerMultiTapAndHold(
+ mContext, 2, 2, GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD, this));
+ mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 2, 3, GESTURE_2_FINGER_TRIPLE_TAP, this));
// Three-finger taps.
mMultiFingerGestures.add(
@@ -139,6 +145,9 @@
mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 3, 2, GESTURE_3_FINGER_DOUBLE_TAP, this));
mMultiFingerGestures.add(
+ new MultiFingerMultiTapAndHold(
+ mContext, 3, 2, GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD, this));
+ mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 3, 3, GESTURE_3_FINGER_TRIPLE_TAP, this));
// Four-finger taps.
mMultiFingerGestures.add(
@@ -146,6 +155,9 @@
mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 4, 2, GESTURE_4_FINGER_DOUBLE_TAP, this));
mMultiFingerGestures.add(
+ new MultiFingerMultiTapAndHold(
+ mContext, 4, 2, GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD, this));
+ mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 4, 3, GESTURE_4_FINGER_TRIPLE_TAP, this));
// Two-finger swipes.
mMultiFingerGestures.add(
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java
index 20def61..e5340f1 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java
@@ -42,10 +42,10 @@
// The acceptable distance the pointer can move and still count as a tap.
private int mTouchSlop;
// A tap counts when target number of fingers are down and up once.
- private int mCompletedTapCount;
+ protected int mCompletedTapCount;
// A flag set to true when target number of fingers have touched down at once before.
// Used to indicate what next finger action should be. Down when false and lift when true.
- private boolean mIsTargetFingerCountReached = false;
+ protected boolean mIsTargetFingerCountReached = false;
// Store initial down points for slop checking and update when next down if is inside slop.
private PointF[] mBases;
// The points in bases that already have slop checked when onDown or onPointerDown.
@@ -56,7 +56,11 @@
* @throws IllegalArgumentException if <code>fingers<code/> is less than 2
* or <code>taps<code/> is not positive.
*/
- MultiFingerMultiTap(Context context, int fingers, int taps, int gestureId,
+ MultiFingerMultiTap(
+ Context context,
+ int fingers,
+ int taps,
+ int gestureId,
GestureMatcher.StateChangeListener listener) {
super(gestureId, new Handler(context.getMainLooper()), listener);
Preconditions.checkArgument(fingers >= 2);
@@ -117,8 +121,7 @@
cancelAfterDoubleTapTimeout(event, rawEvent, policyFlags);
final PointF nearest = findNearestPoint(rawEvent, mTouchSlop, false);
- if ((getState() == STATE_GESTURE_STARTED || getState() == STATE_CLEAR)
- && null != nearest) {
+ if ((getState() == STATE_GESTURE_STARTED || getState() == STATE_CLEAR) && null != nearest) {
// Increase current tap count when the user have all fingers lifted
// within the tap timeout since the target number of fingers are down.
if (mIsTargetFingerCountReached) {
@@ -169,8 +172,7 @@
} else {
nearest = findNearestPoint(rawEvent, mDoubleTapSlop, true);
}
- if ((getState() == STATE_GESTURE_STARTED || getState() == STATE_CLEAR)
- && nearest != null) {
+ if ((getState() == STATE_GESTURE_STARTED || getState() == STATE_CLEAR) && nearest != null) {
// The user have all fingers down within the tap timeout since first finger down,
// setting the timeout for fingers to be lifted.
if (currentFingerCount == mTargetFingerCount) {
@@ -227,11 +229,11 @@
}
/**
- * Find the nearest location to the given event in the bases.
- * If no one found, it could be not inside {@code slop}, filtered or empty bases.
- * When {@code filterMatched} is true, if the location of given event matches one of the points
- * in {@link #mExcludedPointsForDownSlopChecked} it would be ignored. Otherwise, the location
- * will be added to {@link #mExcludedPointsForDownSlopChecked}.
+ * Find the nearest location to the given event in the bases. If no one found, it could be not
+ * inside {@code slop}, filtered or empty bases. When {@code filterMatched} is true, if the
+ * location of given event matches one of the points in {@link
+ * #mExcludedPointsForDownSlopChecked} it would be ignored. Otherwise, the location will be
+ * added to {@link #mExcludedPointsForDownSlopChecked}.
*
* @param event to find nearest point in bases.
* @param slop to check to the given location of the event.
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java
new file mode 100644
index 0000000..7824fd9
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility.gestures;
+
+import android.content.Context;
+import android.view.MotionEvent;
+
+/**
+ * This class matches gestures of the form multi-finger multi-tap and hold. The number of fingers
+ * and taps for each instance is specified in the constructor.
+ */
+class MultiFingerMultiTapAndHold extends MultiFingerMultiTap {
+
+ MultiFingerMultiTapAndHold(
+ Context context,
+ int fingers,
+ int taps,
+ int gestureId,
+ GestureMatcher.StateChangeListener listener) {
+ super(context, fingers, taps, gestureId, listener);
+ }
+
+ @Override
+ protected void onPointerDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ super.onPointerDown(event, rawEvent, policyFlags);
+ if (mIsTargetFingerCountReached && mCompletedTapCount + 1 == mTargetTapCount) {
+ completeAfterLongPressTimeout(event, rawEvent, policyFlags);
+ }
+ }
+
+ @Override
+ protected void onUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mCompletedTapCount + 1 == mTargetFingerCount) {
+ // Calling super.onUp would complete the multi-tap version of this.
+ cancelGesture(event, rawEvent, policyFlags);
+ } else {
+ super.onUp(event, rawEvent, policyFlags);
+ cancelAfterDoubleTapTimeout(event, rawEvent, policyFlags);
+ }
+ }
+
+ @Override
+ public String getGestureName() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(mTargetFingerCount).append("-Finger ");
+ if (mTargetTapCount == 1) {
+ builder.append("Single");
+ } else if (mTargetTapCount == 2) {
+ builder.append("Double");
+ } else if (mTargetTapCount == 3) {
+ builder.append("Triple");
+ } else if (mTargetTapCount > 3) {
+ builder.append(mTargetTapCount);
+ }
+ return builder.append(" Tap and hold").toString();
+ }
+}