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
This commit is contained in:
Ameer Armaly
2020-02-06 11:03:46 -08:00
parent 9657bcfbf5
commit 259daddeea
6 changed files with 121 additions and 12 deletions

View File

@@ -2876,6 +2876,7 @@ package android.accessibilityservice {
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 @@ package android.accessibilityservice {
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 @@ package android.accessibilityservice {
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

View File

@@ -18,6 +18,7 @@ package android.accessibilityservice;
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
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
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 @@ public final class AccessibilityGestureEvent implements Parcelable {
@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 @@ public final class AccessibilityGestureEvent implements Parcelable {
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 @@ public final class AccessibilityGestureEvent implements Parcelable {
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";

View File

@@ -411,6 +411,15 @@ public abstract class AccessibilityService extends Service {
/** 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.
*/

View File

@@ -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
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
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;
@@ -131,6 +134,9 @@ class GestureManifold implements GestureMatcher.StateChangeListener {
new MultiFingerMultiTap(mContext, 2, 1, GESTURE_2_FINGER_SINGLE_TAP, this));
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.
@@ -138,6 +144,9 @@ class GestureManifold implements GestureMatcher.StateChangeListener {
new MultiFingerMultiTap(mContext, 3, 1, GESTURE_3_FINGER_SINGLE_TAP, this));
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.
@@ -145,6 +154,9 @@ class GestureManifold implements GestureMatcher.StateChangeListener {
new MultiFingerMultiTap(mContext, 4, 1, GESTURE_4_FINGER_SINGLE_TAP, this));
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.

View File

@@ -42,10 +42,10 @@ class MultiFingerMultiTap extends GestureMatcher {
// 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 @@ class MultiFingerMultiTap extends GestureMatcher {
* @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 @@ class MultiFingerMultiTap extends GestureMatcher {
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 @@ class MultiFingerMultiTap extends GestureMatcher {
} 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 @@ class MultiFingerMultiTap extends GestureMatcher {
}
/**
* 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.

View File

@@ -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();
}
}