Merge "Add API to record touch interactions."
diff --git a/api/current.txt b/api/current.txt
index 498d470..ce82b5e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2851,6 +2851,7 @@
method public int describeContents();
method public int getDisplayId();
method public int getGestureId();
+ method @NonNull public java.util.List<android.view.MotionEvent> getMotionEvents();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.accessibilityservice.AccessibilityGestureEvent> CREATOR;
}
@@ -2932,6 +2933,7 @@
field public static final int GESTURE_SWIPE_UP_AND_DOWN = 7; // 0x7
field public static final int GESTURE_SWIPE_UP_AND_LEFT = 13; // 0xd
field public static final int GESTURE_SWIPE_UP_AND_RIGHT = 14; // 0xe
+ field public static final int GESTURE_UNKNOWN = 0; // 0x0
field public static final int GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS = 14; // 0xe
field public static final int GLOBAL_ACTION_ACCESSIBILITY_BUTTON = 11; // 0xb
field public static final int GLOBAL_ACTION_ACCESSIBILITY_BUTTON_CHOOSER = 12; // 0xc
@@ -3047,6 +3049,7 @@
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
+ field public static final int FLAG_SEND_MOTION_EVENTS = 16384; // 0x4000
field public static final int FLAG_SERVICE_HANDLES_DOUBLE_TAP = 2048; // 0x800
field public int eventTypes;
field public int feedbackType;
diff --git a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
index e3139eb..d4713cb 100644
--- a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
+++ b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
@@ -46,6 +46,7 @@
import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_TRIPLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP_AND_HOLD;
+import static android.accessibilityservice.AccessibilityService.GESTURE_PASSTHROUGH;
import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN;
import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN_AND_LEFT;
import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN_AND_RIGHT;
@@ -62,15 +63,21 @@
import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_UP_AND_DOWN;
import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_UP_AND_LEFT;
import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_UP_AND_RIGHT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_TOUCH_EXPLORATION;
+import static android.accessibilityservice.AccessibilityService.GESTURE_UNKNOWN;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.TestApi;
+import android.content.pm.ParceledListSlice;
import android.os.Parcel;
import android.os.Parcelable;
+import android.view.MotionEvent;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
/**
* This class describes the gesture event including gesture id and which display it happens
@@ -87,6 +94,8 @@
/** @hide */
@IntDef(prefix = { "GESTURE_" }, value = {
+ GESTURE_UNKNOWN,
+ GESTURE_TOUCH_EXPLORATION,
GESTURE_2_FINGER_SINGLE_TAP,
GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD,
GESTURE_2_FINGER_DOUBLE_TAP,
@@ -139,17 +148,27 @@
@GestureId
private final int mGestureId;
private final int mDisplayId;
+ private List<MotionEvent> mMotionEvents = new ArrayList<>();
+
+ /** @hide */
+ public AccessibilityGestureEvent(
+ int gestureId, int displayId, @NonNull List<MotionEvent> motionEvents) {
+ mGestureId = gestureId;
+ mDisplayId = displayId;
+ mMotionEvents.addAll(motionEvents);
+ }
/** @hide */
@TestApi
public AccessibilityGestureEvent(int gestureId, int displayId) {
- mGestureId = gestureId;
- mDisplayId = displayId;
+ this(gestureId, displayId, new ArrayList<MotionEvent>());
}
private AccessibilityGestureEvent(@NonNull Parcel parcel) {
mGestureId = parcel.readInt();
mDisplayId = parcel.readInt();
+ ParceledListSlice<MotionEvent> slice = parcel.readParcelable(getClass().getClassLoader());
+ mMotionEvents = slice.getList();
}
/**
@@ -172,6 +191,15 @@
return mGestureId;
}
+ /**
+ * Returns the motion events that lead to this gesture.
+ *
+ */
+ @NonNull
+ public List<MotionEvent> getMotionEvents() {
+ return mMotionEvents;
+ }
+
@NonNull
@Override
public String toString() {
@@ -179,12 +207,26 @@
stringBuilder.append("gestureId: ").append(eventTypeToString(mGestureId));
stringBuilder.append(", ");
stringBuilder.append("displayId: ").append(mDisplayId);
+ stringBuilder.append(", ");
+ stringBuilder.append("Motion Events: [");
+ for (int i = 0; i < mMotionEvents.size(); ++i) {
+ String action = MotionEvent.actionToString(mMotionEvents.get(i).getActionMasked());
+ stringBuilder.append(action);
+ if (i < (mMotionEvents.size() - 1)) {
+ stringBuilder.append(", ");
+ } else {
+ stringBuilder.append("]");
+ }
+ }
stringBuilder.append(']');
return stringBuilder.toString();
}
private static String eventTypeToString(int eventType) {
switch (eventType) {
+ case GESTURE_UNKNOWN: return "GESTURE_UNKNOWN";
+ case GESTURE_PASSTHROUGH: return "GESTURE_PASSTHROUGH";
+ case GESTURE_TOUCH_EXPLORATION: return "GESTURE_TOUCH_EXPLORATION";
case GESTURE_2_FINGER_SINGLE_TAP: return "GESTURE_2_FINGER_SINGLE_TAP";
case GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD:
return "GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD";
@@ -252,6 +294,7 @@
public void writeToParcel(@NonNull Parcel parcel, int flags) {
parcel.writeInt(mGestureId);
parcel.writeInt(mDisplayId);
+ parcel.writeParcelable(new ParceledListSlice<MotionEvent>(mMotionEvents), 0);
}
/**
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 7c6d448..0ad9e44 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -235,6 +235,29 @@
public abstract class AccessibilityService extends Service {
/**
+ * The user has performed a touch-exploration gesture on the touch screen without ever
+ * triggering gesture detection. This gesture is only dispatched when {@link
+ * AccessibilityServiceInfo#FLAG_SEND_MOTION_EVENTS} is set.
+ *
+ * @hide
+ */
+ public static final int GESTURE_TOUCH_EXPLORATION = -2;
+
+ /**
+ * The user has performed a passthrough gesture on the touch screen without ever triggering
+ * gesture detection. This gesture is only dispatched when {@link
+ * AccessibilityServiceInfo#FLAG_SEND_MOTION_EVENTS} is set.
+ * @hide
+ */
+ public static final int GESTURE_PASSTHROUGH = -1;
+
+ /**
+ * The user has performed an unrecognized gesture on the touch screen. This gesture is only
+ * dispatched when {@link AccessibilityServiceInfo#FLAG_SEND_MOTION_EVENTS} is set.
+ */
+ public static final int GESTURE_UNKNOWN = 0;
+
+ /**
* The user has performed a swipe up gesture on the touch screen.
*/
public static final int GESTURE_SWIPE_UP = 1;
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 769d006..f953da4 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -376,6 +376,20 @@
*/
public static final int FLAG_REQUEST_2_FINGER_PASSTHROUGH = 0x0002000;
+ /**
+ * This flag requests that when when {@link #FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is enabled, a
+ * service will receive the motion events for each successfully-detected gesture. The service
+ * will also receive an AccessibilityGestureEvent of type GESTURE_INVALID for each cancelled
+ * gesture along with its motion events. A service will receive a gesture of type
+ * GESTURE_PASSTHROUGH and accompanying motion events for every passthrough gesture that does
+ * not start gesture detection. This information can be used to collect instances of improper
+ * gesture detection behavior and relay that information to framework developers. 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_SEND_MOTION_EVENTS = 0x0004000;
+
/** {@hide} */
public static final int FLAG_FORCE_DIRECT_BOOT_AWARE = 0x00010000;
@@ -1276,6 +1290,8 @@
return "FLAG_REQUEST_MULTI_FINGER_GESTURES";
case FLAG_REQUEST_2_FINGER_PASSTHROUGH:
return "FLAG_REQUEST_2_FINGER_PASSTHROUGH";
+ case FLAG_SEND_MOTION_EVENTS:
+ return "FLAG_SEND_MOTION_EVENTS";
case FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY:
return "FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY";
case FLAG_REPORT_VIEW_IDS:
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 64de32c..b74f96d 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3748,6 +3748,7 @@
<flag name="flagServiceHandlesDoubleTap" value="0x00000800" />
<!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_MULTI_FINGER_GESTURES}. -->
<flag name="flagRequestMultiFingerGestures" value="0x00001000" />
+ <flag name="flagSendMotionEvents" value="0x0004000" />
</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/non-updatable-api/current.txt b/non-updatable-api/current.txt
index babd54d..225a2e7 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -2851,6 +2851,7 @@
method public int describeContents();
method public int getDisplayId();
method public int getGestureId();
+ method @NonNull public java.util.List<android.view.MotionEvent> getMotionEvents();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.accessibilityservice.AccessibilityGestureEvent> CREATOR;
}
@@ -2932,6 +2933,7 @@
field public static final int GESTURE_SWIPE_UP_AND_DOWN = 7; // 0x7
field public static final int GESTURE_SWIPE_UP_AND_LEFT = 13; // 0xd
field public static final int GESTURE_SWIPE_UP_AND_RIGHT = 14; // 0xe
+ field public static final int GESTURE_UNKNOWN = 0; // 0x0
field public static final int GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS = 14; // 0xe
field public static final int GLOBAL_ACTION_ACCESSIBILITY_BUTTON = 11; // 0xb
field public static final int GLOBAL_ACTION_ACCESSIBILITY_BUTTON_CHOOSER = 12; // 0xc
@@ -3047,6 +3049,7 @@
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
+ field public static final int FLAG_SEND_MOTION_EVENTS = 16384; // 0x4000
field public static final int FLAG_SERVICE_HANDLES_DOUBLE_TAP = 2048; // 0x800
field public int eventTypes;
field public int feedbackType;
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index a167ab1..d6d4e4f 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -150,6 +150,8 @@
private boolean mRequestTwoFingerPassthrough;
+ private boolean mSendMotionEvents;
+
boolean mRequestFilterKeyEvents;
boolean mRetrieveInteractiveWindows;
@@ -329,6 +331,8 @@
& AccessibilityServiceInfo.FLAG_REQUEST_MULTI_FINGER_GESTURES) != 0;
mRequestTwoFingerPassthrough =
(info.flags & AccessibilityServiceInfo.FLAG_REQUEST_2_FINGER_PASSTHROUGH) != 0;
+ mSendMotionEvents =
+ (info.flags & AccessibilityServiceInfo.FLAG_SEND_MOTION_EVENTS) != 0;
mRequestFilterKeyEvents =
(info.flags & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0;
mRetrieveInteractiveWindows = (info.flags
@@ -1780,6 +1784,10 @@
return mRequestTwoFingerPassthrough;
}
+ public boolean isSendMotionEventsEnabled() {
+ return mSendMotionEvents;
+ }
+
@Override
public void setGestureDetectionPassthroughRegion(int displayId, Region region) {
mSystemSupport.setGestureDetectionPassthroughRegion(displayId, region);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index cd9ab8d..857ac6a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -119,12 +119,19 @@
static final int FLAG_REQUEST_MULTI_FINGER_GESTURES = 0x00000100;
/**
- * Flag for enabling multi-finger gestures.
+ * Flag for enabling two-finger passthrough when multi-finger gestures are enabled.
*
* @see #setUserAndEnabledFeatures(int, int)
*/
static final int FLAG_REQUEST_2_FINGER_PASSTHROUGH = 0x00000200;
+ /**
+ * Flag for including motion events when dispatching a gesture.
+ *
+ * @see #setUserAndEnabledFeatures(int, int)
+ */
+ static final int FLAG_SEND_MOTION_EVENTS = 0x00000400;
+
static final int FEATURES_AFFECTING_MOTION_EVENTS =
FLAG_FEATURE_INJECT_MOTION_EVENTS
| FLAG_FEATURE_AUTOCLICK
@@ -432,6 +439,9 @@
if ((mEnabledFeatures & FLAG_REQUEST_2_FINGER_PASSTHROUGH) != 0) {
explorer.setTwoFingerPassthroughEnabled(true);
}
+ if ((mEnabledFeatures & FLAG_SEND_MOTION_EVENTS) != 0) {
+ explorer.setSendMotionEventsEnabled(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 e1c4993..35481a2 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1867,6 +1867,10 @@
if (userState.isFilterKeyEventsEnabledLocked()) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_FILTER_KEY_EVENTS;
}
+ if (userState.isSendMotionEventsEnabled()) {
+ flags |= AccessibilityInputFilter.FLAG_SEND_MOTION_EVENTS;
+ }
+
if (userState.isAutoclickEnabledLocked()) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_AUTOCLICK;
}
@@ -2147,6 +2151,7 @@
boolean serviceHandlesDoubleTapEnabled = false;
boolean requestMultiFingerGestures = false;
boolean requestTwoFingerPassthrough = false;
+ boolean sendMotionEvents = false;
final int serviceCount = userState.mBoundServices.size();
for (int i = 0; i < serviceCount; i++) {
AccessibilityServiceConnection service = userState.mBoundServices.get(i);
@@ -2155,6 +2160,7 @@
serviceHandlesDoubleTapEnabled = service.isServiceHandlesDoubleTapEnabled();
requestMultiFingerGestures = service.isMultiFingerGesturesEnabled();
requestTwoFingerPassthrough = service.isTwoFingerPassthroughEnabled();
+ sendMotionEvents = service.isSendMotionEventsEnabled();
break;
}
}
@@ -2172,6 +2178,7 @@
userState.setServiceHandlesDoubleTapLocked(serviceHandlesDoubleTapEnabled);
userState.setMultiFingerGesturesLocked(requestMultiFingerGestures);
userState.setTwoFingerPassthroughLocked(requestTwoFingerPassthrough);
+ userState.setSendMotionEventsEnabled(sendMotionEvents);
}
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 4c9e444..240c7ff 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -111,6 +111,7 @@
private boolean mServiceHandlesDoubleTap;
private boolean mRequestMultiFingerGestures;
private boolean mRequestTwoFingerPassthrough;
+ private boolean mSendMotionEventsEnabled;
private int mUserInteractiveUiTimeout;
private int mUserNonInteractiveUiTimeout;
private int mNonInteractiveUiTimeout = 0;
@@ -171,6 +172,7 @@
mServiceHandlesDoubleTap = false;
mRequestMultiFingerGestures = false;
mRequestTwoFingerPassthrough = false;
+ mSendMotionEventsEnabled = false;
mIsDisplayMagnificationEnabled = false;
mIsAutoclickEnabled = false;
mUserNonInteractiveUiTimeout = 0;
@@ -460,6 +462,7 @@
.append(String.valueOf(mRequestMultiFingerGestures));
pw.append(", requestTwoFingerPassthrough=")
.append(String.valueOf(mRequestTwoFingerPassthrough));
+ pw.append(", sendMotionEventsEnabled").append(String.valueOf(mSendMotionEventsEnabled));
pw.append(", displayMagnificationEnabled=").append(String.valueOf(
mIsDisplayMagnificationEnabled));
pw.append(", autoclickEnabled=").append(String.valueOf(mIsAutoclickEnabled));
@@ -802,6 +805,13 @@
mRequestTwoFingerPassthrough = enabled;
}
+ public boolean isSendMotionEventsEnabled() {
+ return mSendMotionEventsEnabled;
+ }
+
+ public void setSendMotionEventsEnabled(boolean mode) {
+ mSendMotionEventsEnabled = mode;
+ }
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 14af8c6..2c38dc3 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
@@ -99,11 +99,15 @@
boolean mMultiFingerGesturesEnabled;
// Whether the two-finger passthrough is enabled when multi-finger gestures are enabled.
private boolean mTwoFingerPassthroughEnabled;
+ // Whether to send the motion events during gesture dispatch.
+ private boolean mSendMotionEventsEnabled = false;
// A list of all the multi-finger gestures, for easy adding and removal.
private final List<GestureMatcher> mMultiFingerGestures = new ArrayList<>();
// A list of two-finger swipes, for easy adding and removal when turning on or off two-finger
// passthrough.
private final List<GestureMatcher> mTwoFingerSwipes = new ArrayList<>();
+ // The list of motion events for the current gesture.
+ private List<MotionEvent> mEvents = new ArrayList<>();
// Shared state information.
private TouchState mState;
@@ -230,6 +234,9 @@
return false;
}
}
+ if (mSendMotionEventsEnabled) {
+ mEvents.add(MotionEvent.obtainNoHistory(rawEvent));
+ }
for (GestureMatcher matcher : mGestures) {
if (matcher.getState() != GestureMatcher.STATE_GESTURE_CANCELED) {
if (DEBUG) {
@@ -240,9 +247,8 @@
Slog.d(LOG_TAG, matcher.toString());
}
if (matcher.getState() == GestureMatcher.STATE_GESTURE_COMPLETED) {
- // Here we just clear and return. The actual gesture dispatch is done in
+ // Here we just return. The actual gesture dispatch is done in
// onStateChanged().
- clear();
// No need to process this event any further.
return true;
}
@@ -255,6 +261,11 @@
for (GestureMatcher matcher : mGestures) {
matcher.clear();
}
+ if (mEvents != null) {
+ while (mEvents.size() > 0) {
+ mEvents.remove(0).recycle();
+ }
+ }
}
/**
@@ -340,29 +351,28 @@
case GESTURE_DOUBLE_TAP:
if (mServiceHandlesDoubleTap) {
AccessibilityGestureEvent gestureEvent =
- new AccessibilityGestureEvent(gestureId, event.getDisplayId());
+ new AccessibilityGestureEvent(gestureId, event.getDisplayId(), mEvents);
mListener.onGestureCompleted(gestureEvent);
} else {
mListener.onDoubleTap(event, rawEvent, policyFlags);
}
- clear();
break;
case GESTURE_DOUBLE_TAP_AND_HOLD:
if (mServiceHandlesDoubleTap) {
AccessibilityGestureEvent gestureEvent =
- new AccessibilityGestureEvent(gestureId, event.getDisplayId());
+ new AccessibilityGestureEvent(gestureId, event.getDisplayId(), mEvents);
mListener.onGestureCompleted(gestureEvent);
} else {
mListener.onDoubleTapAndHold(event, rawEvent, policyFlags);
}
- clear();
break;
default:
AccessibilityGestureEvent gestureEvent =
- new AccessibilityGestureEvent(gestureId, event.getDisplayId());
+ new AccessibilityGestureEvent(gestureId, event.getDisplayId(), mEvents);
mListener.onGestureCompleted(gestureEvent);
break;
}
+ clear();
}
public boolean isMultiFingerGesturesEnabled() {
@@ -406,4 +416,25 @@
public boolean isServiceHandlesDoubleTapEnabled() {
return mServiceHandlesDoubleTap;
}
+
+ public void setSendMotionEventsEnabled(boolean mode) {
+ mSendMotionEventsEnabled = mode;
+ if (!mode) {
+ while (mEvents.size() > 0) {
+ mEvents.remove(0).recycle();
+ }
+ }
+ }
+
+ public boolean isSendMotionEventsEnabled() {
+ return mSendMotionEventsEnabled;
+ }
+
+ /**
+ * Returns the current list of motion events. It is the caller's responsibility to copy the list
+ * if they want it to persist after a call to clear().
+ */
+ public List<MotionEvent> getMotionEvents() {
+ return mEvents;
+ }
}
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 d8c692b8..5460e80 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -37,6 +37,7 @@
import static com.android.server.accessibility.gestures.TouchState.ALL_POINTER_ID_BITS;
import android.accessibilityservice.AccessibilityGestureEvent;
+import android.accessibilityservice.AccessibilityService;
import android.annotation.NonNull;
import android.content.Context;
import android.graphics.Region;
@@ -340,6 +341,14 @@
public void onDoubleTapAndHold(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
if (mDispatcher.longPressWithTouchEvents(event, policyFlags)) {
sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
+ if (isSendMotionEventsEnabled()) {
+ AccessibilityGestureEvent gestureEvent =
+ new AccessibilityGestureEvent(
+ AccessibilityService.GESTURE_DOUBLE_TAP_AND_HOLD,
+ event.getDisplayId(),
+ mGestureDetector.getMotionEvents());
+ mAms.onGesture(gestureEvent);
+ }
mState.startDelegating();
}
}
@@ -350,7 +359,14 @@
// Remove pending event deliveries.
mSendHoverEnterAndMoveDelayed.cancel();
mSendHoverExitDelayed.cancel();
-
+ if (isSendMotionEventsEnabled()) {
+ AccessibilityGestureEvent gestureEvent =
+ new AccessibilityGestureEvent(
+ AccessibilityService.GESTURE_DOUBLE_TAP,
+ event.getDisplayId(),
+ mGestureDetector.getMotionEvents());
+ mAms.onGesture(gestureEvent);
+ }
if (mSendTouchExplorationEndDelayed.isPending()) {
mSendTouchExplorationEndDelayed.forceSendAndRemove();
}
@@ -385,6 +401,9 @@
@Override
public boolean onGestureCompleted(AccessibilityGestureEvent gestureEvent) {
+ if (DEBUG) {
+ Slog.d(LOG_TAG, "Dispatching gesture event:" + gestureEvent.toString());
+ }
endGestureDetection(true);
mSendTouchInteractionEndDelayed.cancel();
mAms.onGesture(gestureEvent);
@@ -411,12 +430,24 @@
mDispatcher.sendMotionEvent(
event,
ACTION_HOVER_MOVE,
- mState.getLastReceivedEvent(),
+ event,
pointerIdBits,
policyFlags);
return true;
}
}
+ if (isSendMotionEventsEnabled()) {
+ // Send a gesture with motion events to represent the cancelled gesture.
+ AccessibilityGestureEvent gestureEvent =
+ new AccessibilityGestureEvent(
+ AccessibilityService.GESTURE_UNKNOWN,
+ event.getDisplayId(),
+ mGestureDetector.getMotionEvents());
+ if (DEBUG) {
+ Slog.d(LOG_TAG, "Dispatching gesture event:" + gestureEvent.toString());
+ }
+ mAms.onGesture(gestureEvent);
+ }
return false;
}
@@ -620,6 +651,14 @@
if (isDraggingGesture(event)) {
// Two pointers moving in the same direction within
// a given distance perform a drag.
+ if (isSendMotionEventsEnabled()) {
+ AccessibilityGestureEvent gestureEvent =
+ new AccessibilityGestureEvent(
+ AccessibilityService.GESTURE_PASSTHROUGH,
+ event.getDisplayId(),
+ mGestureDetector.getMotionEvents());
+ mAms.onGesture(gestureEvent);
+ }
computeDraggingPointerIdIfNeeded(event);
pointerIdBits = 1 << mDraggingPointerId;
event.setEdgeFlags(mReceivedPointerTracker.getLastReceivedDownEdgeFlags());
@@ -636,6 +675,14 @@
mState.startDragging();
} else {
// Two pointers moving arbitrary are delegated to the view hierarchy.
+ if (isSendMotionEventsEnabled()) {
+ AccessibilityGestureEvent gestureEvent =
+ new AccessibilityGestureEvent(
+ AccessibilityService.GESTURE_PASSTHROUGH,
+ event.getDisplayId(),
+ mGestureDetector.getMotionEvents());
+ mAms.onGesture(gestureEvent);
+ }
mState.startDelegating();
mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags);
}
@@ -650,6 +697,14 @@
if (DEBUG) {
Slog.d(LOG_TAG, "Three-finger edge swipe detected.");
}
+ if (isSendMotionEventsEnabled()) {
+ AccessibilityGestureEvent gestureEvent =
+ new AccessibilityGestureEvent(
+ AccessibilityService.GESTURE_PASSTHROUGH,
+ event.getDisplayId(),
+ mGestureDetector.getMotionEvents());
+ mAms.onGesture(gestureEvent);
+ }
mState.startDelegating();
if (mState.isTouchExploring()) {
mDispatcher.sendDownForAllNotInjectedPointers(event,
@@ -663,6 +718,14 @@
}
} else {
// More than two pointers are delegated to the view hierarchy.
+ if (isSendMotionEventsEnabled()) {
+ AccessibilityGestureEvent gestureEvent =
+ new AccessibilityGestureEvent(
+ AccessibilityService.GESTURE_PASSTHROUGH,
+ event.getDisplayId(),
+ mGestureDetector.getMotionEvents());
+ mAms.onGesture(gestureEvent);
+ }
mState.startDelegating();
event = MotionEvent.obtainNoHistory(event);
mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags);
@@ -1109,6 +1172,7 @@
public void setTwoFingerPassthroughEnabled(boolean enabled) {
mGestureDetector.setTwoFingerPassthroughEnabled(enabled);
}
+
public void setGestureDetectionPassthroughRegion(Region region) {
mGestureDetectionPassthroughRegion = region;
}
@@ -1117,6 +1181,17 @@
mTouchExplorationPassthroughRegion = region;
}
+ /**
+ * Whether to send the motion events that make up each gesture to the accessibility service.
+ */
+ public void setSendMotionEventsEnabled(boolean mode) {
+ mGestureDetector.setSendMotionEventsEnabled(mode);
+ }
+
+ public boolean isSendMotionEventsEnabled() {
+ return mGestureDetector.isSendMotionEventsEnabled();
+ }
+
private boolean shouldPerformGestureDetection(MotionEvent event) {
if (mState.isDelegating()) {
return false;
@@ -1213,7 +1288,14 @@
public void run() {
// Send an accessibility event to announce the touch exploration start.
mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_START);
-
+ if (isSendMotionEventsEnabled()) {
+ AccessibilityGestureEvent gestureEvent =
+ new AccessibilityGestureEvent(
+ AccessibilityService.GESTURE_TOUCH_EXPLORATION,
+ mState.getLastReceivedEvent().getDisplayId(),
+ mGestureDetector.getMotionEvents());
+ mAms.onGesture(gestureEvent);
+ }
if (!mEvents.isEmpty() && !mRawEvents.isEmpty()) {
// Deliver a down event.
mDispatcher.sendMotionEvent(mEvents.get(0), ACTION_HOVER_ENTER,