Add flag for services to request multi-finger gestures.
Bug: 136131815
Test: atest GestureManifoldTest AccessibilityGestureDetectorTest TouchExplorerTest
Change-Id: Ia6eac44f467963856ca7b8ba561794fa5e49b953
diff --git a/api/current.txt b/api/current.txt
index b2c8061..032da42 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2988,6 +2988,7 @@
field @Deprecated public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 8; // 0x8
field public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 32; // 0x20
field public static final int FLAG_REQUEST_FINGERPRINT_GESTURES = 512; // 0x200
+ field public static final int FLAG_REQUEST_MULTI_FINGER_GESTURES = 4096; // 0x1000
field public static final int FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK = 1024; // 0x400
field public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 4; // 0x4
field public static final int FLAG_RETRIEVE_INTERACTIVE_WINDOWS = 64; // 0x40
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 342b6a5..82c7635 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -351,6 +351,16 @@
*/
public static final int FLAG_SERVICE_HANDLES_DOUBLE_TAP = 0x0000800;
+ /**
+ * This flag requests that when when {@link #FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is enabled,
+ * multi-finger gestures are also enabled. As a consequence, two-finger bypass gestures will be
+ * disabled. If {@link #FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is disabled this flag has no
+ * effect.
+ *
+ * @see #FLAG_REQUEST_TOUCH_EXPLORATION_MODE
+ */
+ public static final int FLAG_REQUEST_MULTI_FINGER_GESTURES = 0x0001000;
+
/** {@hide} */
public static final int FLAG_FORCE_DIRECT_BOOT_AWARE = 0x00010000;
@@ -1233,6 +1243,8 @@
return "FLAG_REQUEST_TOUCH_EXPLORATION_MODE";
case FLAG_SERVICE_HANDLES_DOUBLE_TAP:
return "FLAG_SERVICE_HANDLES_DOUBLE_TAP";
+ case FLAG_REQUEST_MULTI_FINGER_GESTURES:
+ return "FLAG_REQUEST_MULTI_FINGER_GESTURES";
case FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY:
return "FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY";
case FLAG_REPORT_VIEW_IDS:
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 4b49d67..02b098b 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -105,6 +105,9 @@
public static final int STATE_FLAG_DISPATCH_DOUBLE_TAP = 0x00000008;
/** @hide */
+ public static final int STATE_FLAG_REQUEST_MULTI_FINGER_GESTURES = 0x00000010;
+
+ /** @hide */
public static final int DALTONIZER_DISABLED = -1;
/** @hide */
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 643a6ff..20901e0 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3716,6 +3716,8 @@
<flag name="flagRequestShortcutWarningDialogSpokenFeedback" value="0x00000400" />
<!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_SERVICE_HANDLES_DOUBLE_TAP}. -->
<flag name="flagServiceHandlesDoubleTap" value="0x00000800" />
+ <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_MULTI_FINGER_GESTURES}. -->
+ <flag name="flagRequestMultiFingerGestures" value="0x00001000" />
</attr>
<!-- Component name of an activity that allows the user to modify
the settings for this service. This setting cannot be changed at runtime. -->
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index a79ddce..a5877cc 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -136,6 +136,8 @@
private boolean mServiceHandlesDoubleTap;
+ private boolean mRequestMultiFingerGestures;
+
boolean mRequestFilterKeyEvents;
boolean mRetrieveInteractiveWindows;
@@ -302,6 +304,8 @@
& AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0;
mServiceHandlesDoubleTap = (info.flags
& AccessibilityServiceInfo.FLAG_SERVICE_HANDLES_DOUBLE_TAP) != 0;
+ mRequestMultiFingerGestures = (info.flags
+ & AccessibilityServiceInfo.FLAG_REQUEST_MULTI_FINGER_GESTURES) != 0;
mRequestFilterKeyEvents = (info.flags
& AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0;
mRetrieveInteractiveWindows = (info.flags
@@ -1697,4 +1701,8 @@
public boolean isServiceHandlesDoubleTapEnabled() {
return mServiceHandlesDoubleTap;
}
+
+ public boolean isMultiFingerGesturesEnabled() {
+ return mRequestMultiFingerGestures;
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index ee97678..efddd86 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -106,13 +106,21 @@
*/
static final int FLAG_SERVICE_HANDLES_DOUBLE_TAP = 0x00000080;
+/**
+ * Flag for enabling multi-finger gestures.
+ *
+ * @see #setUserAndEnabledFeatures(int, int)
+ */
+ static final int FLAG_REQUEST_MULTI_FINGER_GESTURES = 0x00000100;
+
static final int FEATURES_AFFECTING_MOTION_EVENTS =
FLAG_FEATURE_INJECT_MOTION_EVENTS
| FLAG_FEATURE_AUTOCLICK
| FLAG_FEATURE_TOUCH_EXPLORATION
| FLAG_FEATURE_SCREEN_MAGNIFIER
| FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER
- | FLAG_SERVICE_HANDLES_DOUBLE_TAP;
+ | FLAG_SERVICE_HANDLES_DOUBLE_TAP
+ | FLAG_REQUEST_MULTI_FINGER_GESTURES;
private final Context mContext;
@@ -405,6 +413,9 @@
if ((mEnabledFeatures & FLAG_SERVICE_HANDLES_DOUBLE_TAP) != 0) {
explorer.setServiceHandlesDoubleTap(true);
}
+ if ((mEnabledFeatures & FLAG_REQUEST_MULTI_FINGER_GESTURES) != 0) {
+ explorer.setMultiFingerGesturesEnabled(true);
+ }
addFirstEventHandler(displayId, explorer);
mTouchExplorer.put(displayId, explorer);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 560a8ef..61e1adf 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1613,6 +1613,9 @@
if (userState.isServiceHandlesDoubleTapEnabledLocked()) {
flags |= AccessibilityInputFilter.FLAG_SERVICE_HANDLES_DOUBLE_TAP;
}
+ if (userState.isMultiFingerGesturesEnabledLocked()) {
+ flags |= AccessibilityInputFilter.FLAG_REQUEST_MULTI_FINGER_GESTURES;
+ }
}
if (userState.isFilterKeyEventsEnabledLocked()) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_FILTER_KEY_EVENTS;
@@ -1887,18 +1890,19 @@
private void updateTouchExplorationLocked(AccessibilityUserState userState) {
boolean touchExplorationEnabled = mUiAutomationManager.isTouchExplorationEnabledLocked();
boolean serviceHandlesDoubleTapEnabled = false;
+ boolean requestMultiFingerGestures = false;
final int serviceCount = userState.mBoundServices.size();
for (int i = 0; i < serviceCount; i++) {
AccessibilityServiceConnection service = userState.mBoundServices.get(i);
if (canRequestAndRequestsTouchExplorationLocked(service, userState)) {
touchExplorationEnabled = true;
serviceHandlesDoubleTapEnabled = service.isServiceHandlesDoubleTapEnabled();
+ requestMultiFingerGestures = service.isMultiFingerGesturesEnabled();
break;
}
}
if (touchExplorationEnabled != userState.isTouchExplorationEnabledLocked()) {
userState.setTouchExplorationEnabledLocked(touchExplorationEnabled);
- userState.setServiceHandlesDoubleTapLocked(serviceHandlesDoubleTapEnabled);
final long identity = Binder.clearCallingIdentity();
try {
Settings.Secure.putIntForUser(mContext.getContentResolver(),
@@ -1908,6 +1912,8 @@
Binder.restoreCallingIdentity(identity);
}
}
+ userState.setServiceHandlesDoubleTapLocked(serviceHandlesDoubleTapEnabled);
+ userState.setMultiFingerGesturesLocked(requestMultiFingerGestures);
}
private boolean readAccessibilityShortcutKeySettingLocked(AccessibilityUserState userState) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index 49b1158..4e7da97 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -104,6 +104,7 @@
private boolean mIsTextHighContrastEnabled;
private boolean mIsTouchExplorationEnabled;
private boolean mServiceHandlesDoubleTap;
+ private boolean mRequestMultiFingerGestures;
private int mUserInteractiveUiTimeout;
private int mUserNonInteractiveUiTimeout;
private int mNonInteractiveUiTimeout = 0;
@@ -153,6 +154,7 @@
mAccessibilityButtonTargets.clear();
mIsTouchExplorationEnabled = false;
mServiceHandlesDoubleTap = false;
+ mRequestMultiFingerGestures = false;
mIsDisplayMagnificationEnabled = false;
mIsAutoclickEnabled = false;
mUserNonInteractiveUiTimeout = 0;
@@ -354,6 +356,7 @@
if (a11yEnabled && mIsTouchExplorationEnabled) {
clientState |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
clientState |= AccessibilityManager.STATE_FLAG_DISPATCH_DOUBLE_TAP;
+ clientState |= AccessibilityManager.STATE_FLAG_REQUEST_MULTI_FINGER_GESTURES;
}
if (mIsTextHighContrastEnabled) {
clientState |= AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED;
@@ -436,6 +439,8 @@
pw.append(", touchExplorationEnabled=").append(String.valueOf(mIsTouchExplorationEnabled));
pw.append(", serviceHandlesDoubleTap=")
.append(String.valueOf(mServiceHandlesDoubleTap));
+ pw.append(", requestMultiFingerGestures=")
+ .append(String.valueOf(mRequestMultiFingerGestures));
pw.append(", displayMagnificationEnabled=").append(String.valueOf(
mIsDisplayMagnificationEnabled));
pw.append(", autoclickEnabled=").append(String.valueOf(mIsAutoclickEnabled));
@@ -688,6 +693,14 @@
mServiceHandlesDoubleTap = enabled;
}
+ public boolean isMultiFingerGesturesEnabledLocked() {
+ return mRequestMultiFingerGestures;
+ }
+
+ public void setMultiFingerGesturesLocked(boolean enabled) {
+ mRequestMultiFingerGestures = enabled;
+ }
+
public int getUserInteractiveUiTimeoutLocked() {
return mUserInteractiveUiTimeout;
}
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 9afcf8d..b5660ae 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
@@ -65,8 +65,13 @@
private final Handler mHandler;
// Listener to be notified of gesture start and end.
private Listener mListener;
- // Whether enhanced touch exploration mode is enabled.
+ // Whether double tap and double tap and hold will be dispatched to the service or handled in
+ // the framework.
private boolean mServiceHandlesDoubleTap = false;
+ // Whether multi-finger gestures are enabled.
+ boolean mMultiFingerGesturesEnabled;
+ // A list of all the multi-finger gestures, for easy adding and removal.
+ private final List<GestureMatcher> mMultiFingerGestures = new ArrayList<>();
// Shared state information.
private TouchState mState;
@@ -257,11 +262,26 @@
}
}
- public boolean isServiceHandlesDoubleTapEnabled() {
- return mServiceHandlesDoubleTap;
+ public boolean isMultiFingerGesturesEnabled() {
+ return mMultiFingerGesturesEnabled;
+ }
+
+ public void setMultiFingerGesturesEnabled(boolean mode) {
+ if (mMultiFingerGesturesEnabled != mode) {
+ mMultiFingerGesturesEnabled = mode;
+ if (mode) {
+ mGestures.addAll(mMultiFingerGestures);
+ } else {
+ mGestures.removeAll(mMultiFingerGestures);
+ }
+ }
}
public void setServiceHandlesDoubleTap(boolean mode) {
mServiceHandlesDoubleTap = mode;
}
+
+ public boolean isServiceHandlesDoubleTapEnabled() {
+ return mServiceHandlesDoubleTap;
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index caf1424..2cc11c5 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -515,6 +515,9 @@
}
break;
case 2:
+ if (mGestureDetector.isMultiFingerGesturesEnabled()) {
+ return;
+ }
// Make sure we don't have any pending transitions to touch exploration
mSendHoverEnterAndMoveDelayed.cancel();
mSendHoverExitDelayed.cancel();
@@ -537,6 +540,9 @@
}
break;
default:
+ if (mGestureDetector.isMultiFingerGesturesEnabled()) {
+ return;
+ }
// More than two pointers are delegated to the view hierarchy.
mState.startDelegating();
event = MotionEvent.obtainNoHistory(event);
@@ -582,6 +588,9 @@
event, MotionEvent.ACTION_HOVER_MOVE, rawEvent, pointerIdBits, policyFlags);
break;
case 2:
+ if (mGestureDetector.isMultiFingerGesturesEnabled()) {
+ return;
+ }
if (mSendHoverEnterAndMoveDelayed.isPending()) {
// We have not started sending events so cancel
// scheduled sending events.
@@ -609,6 +618,9 @@
}
break;
default:
+ if (mGestureDetector.isMultiFingerGesturesEnabled()) {
+ return;
+ }
// Three or more fingers is something other than touch exploration.
if (mSendHoverEnterAndMoveDelayed.isPending()) {
// We have not started sending events so cancel
@@ -631,6 +643,10 @@
*/
private void handleMotionEventStateDragging(
MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mGestureDetector.isMultiFingerGesturesEnabled()) {
+ // Multi-finger gestures conflict with this functionality.
+ return;
+ }
int pointerIdBits = 0;
// Clear the dragging pointer id if it's no longer valid.
if (event.findPointerIndex(mDraggingPointerId) == -1) {
@@ -741,6 +757,10 @@
*/
private void handleMotionEventStateDelegating(
MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mGestureDetector.isMultiFingerGesturesEnabled()) {
+ // Multi-finger gestures conflict with this functionality.
+ return;
+ }
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
Slog.e(LOG_TAG, "Delegating state can only be reached if "
@@ -882,6 +902,14 @@
}
/**
+ * This function turns on and off multi-finger gestures. When enabled, multi-finger gestures
+ * will disable delegating and dragging functionality.
+ */
+ public void setMultiFingerGesturesEnabled(boolean enabled) {
+ mGestureDetector.setMultiFingerGesturesEnabled(enabled);
+ }
+
+ /**
* Class for delayed exiting from gesture detecting mode.
*/
private final class ExitGestureDetectionModeDelayed implements Runnable {