Enable two-finger passthrough swipes.
Bug: 162521649
Test: atest GestureManifoldTest TouchExplorerTest AccessibilityGestureDetectorTest FrameworksServicesTests:TouchExplorerTest
Change-Id: I9e6b691295a0fa006578d7b8ad8468579c66939c
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index ca22bf4..d334de6 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -364,6 +364,18 @@
*/
public static final int FLAG_REQUEST_MULTI_FINGER_GESTURES = 0x0001000;
+ /**
+ * This flag requests that when when {@link #FLAG_REQUEST_MULTI_FINGER_GESTURES} is enabled,
+ * two-finger passthrough gestures are re-enabled. Two-finger swipe gestures are not detected,
+ * but instead passed through as one-finger gestures. In addition, three-finger swipes from the
+ * bottom of the screen are not detected, and instead are passed through unchanged. If {@link
+ * #FLAG_REQUEST_MULTI_FINGER_GESTURES} is disabled this flag has no effect.
+ *
+ * @see #FLAG_REQUEST_TOUCH_EXPLORATION_MODE
+ * @hide
+ */
+ public static final int FLAG_REQUEST_2_FINGER_PASSTHROUGH = 0x0002000;
+
/** {@hide} */
public static final int FLAG_FORCE_DIRECT_BOOT_AWARE = 0x00010000;
@@ -624,6 +636,7 @@
0);
flags = asAttributes.getInt(
com.android.internal.R.styleable.AccessibilityService_accessibilityFlags, 0);
+ flags |= FLAG_REQUEST_2_FINGER_PASSTHROUGH;
mSettingsActivityName = asAttributes.getString(
com.android.internal.R.styleable.AccessibilityService_settingsActivity);
if (asAttributes.getBoolean(com.android.internal.R.styleable
@@ -1261,6 +1274,8 @@
return "FLAG_SERVICE_HANDLES_DOUBLE_TAP";
case FLAG_REQUEST_MULTI_FINGER_GESTURES:
return "FLAG_REQUEST_MULTI_FINGER_GESTURES";
+ case FLAG_REQUEST_2_FINGER_PASSTHROUGH:
+ return "FLAG_REQUEST_2_FINGER_PASSTHROUGH";
case FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY:
return "FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY";
case FLAG_REPORT_VIEW_IDS:
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 9ad808a..a167ab1 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -148,6 +148,8 @@
private boolean mRequestMultiFingerGestures;
+ private boolean mRequestTwoFingerPassthrough;
+
boolean mRequestFilterKeyEvents;
boolean mRetrieveInteractiveWindows;
@@ -325,8 +327,10 @@
& 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;
+ mRequestTwoFingerPassthrough =
+ (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_2_FINGER_PASSTHROUGH) != 0;
+ mRequestFilterKeyEvents =
+ (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0;
mRetrieveInteractiveWindows = (info.flags
& AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS) != 0;
mCaptureFingerprintGestures = (info.flags
@@ -1772,6 +1776,10 @@
return mRequestMultiFingerGestures;
}
+ public boolean isTwoFingerPassthroughEnabled() {
+ return mRequestTwoFingerPassthrough;
+ }
+
@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 8b40f61..cd9ab8d 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -118,6 +118,13 @@
*/
static final int FLAG_REQUEST_MULTI_FINGER_GESTURES = 0x00000100;
+ /**
+ * Flag for enabling multi-finger gestures.
+ *
+ * @see #setUserAndEnabledFeatures(int, int)
+ */
+ static final int FLAG_REQUEST_2_FINGER_PASSTHROUGH = 0x00000200;
+
static final int FEATURES_AFFECTING_MOTION_EVENTS =
FLAG_FEATURE_INJECT_MOTION_EVENTS
| FLAG_FEATURE_AUTOCLICK
@@ -125,7 +132,8 @@
| FLAG_FEATURE_SCREEN_MAGNIFIER
| FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER
| FLAG_SERVICE_HANDLES_DOUBLE_TAP
- | FLAG_REQUEST_MULTI_FINGER_GESTURES;
+ | FLAG_REQUEST_MULTI_FINGER_GESTURES
+ | FLAG_REQUEST_2_FINGER_PASSTHROUGH;
private final Context mContext;
@@ -421,6 +429,9 @@
if ((mEnabledFeatures & FLAG_REQUEST_MULTI_FINGER_GESTURES) != 0) {
explorer.setMultiFingerGesturesEnabled(true);
}
+ if ((mEnabledFeatures & FLAG_REQUEST_2_FINGER_PASSTHROUGH) != 0) {
+ explorer.setTwoFingerPassthroughEnabled(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 b134022..833aeec 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1837,6 +1837,9 @@
if (userState.isMultiFingerGesturesEnabledLocked()) {
flags |= AccessibilityInputFilter.FLAG_REQUEST_MULTI_FINGER_GESTURES;
}
+ if (userState.isTwoFingerPassthroughEnabledLocked()) {
+ flags |= AccessibilityInputFilter.FLAG_REQUEST_2_FINGER_PASSTHROUGH;
+ }
}
if (userState.isFilterKeyEventsEnabledLocked()) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_FILTER_KEY_EVENTS;
@@ -2120,6 +2123,7 @@
boolean touchExplorationEnabled = mUiAutomationManager.isTouchExplorationEnabledLocked();
boolean serviceHandlesDoubleTapEnabled = false;
boolean requestMultiFingerGestures = false;
+ boolean requestTwoFingerPassthrough = false;
final int serviceCount = userState.mBoundServices.size();
for (int i = 0; i < serviceCount; i++) {
AccessibilityServiceConnection service = userState.mBoundServices.get(i);
@@ -2127,6 +2131,7 @@
touchExplorationEnabled = true;
serviceHandlesDoubleTapEnabled = service.isServiceHandlesDoubleTapEnabled();
requestMultiFingerGestures = service.isMultiFingerGesturesEnabled();
+ requestTwoFingerPassthrough = service.isTwoFingerPassthroughEnabled();
break;
}
}
@@ -2143,6 +2148,7 @@
}
userState.setServiceHandlesDoubleTapLocked(serviceHandlesDoubleTapEnabled);
userState.setMultiFingerGesturesLocked(requestMultiFingerGestures);
+ userState.setTwoFingerPassthroughLocked(requestTwoFingerPassthrough);
}
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 f865aa7..4c9e444 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -110,6 +110,7 @@
private boolean mIsTouchExplorationEnabled;
private boolean mServiceHandlesDoubleTap;
private boolean mRequestMultiFingerGestures;
+ private boolean mRequestTwoFingerPassthrough;
private int mUserInteractiveUiTimeout;
private int mUserNonInteractiveUiTimeout;
private int mNonInteractiveUiTimeout = 0;
@@ -169,6 +170,7 @@
mIsTouchExplorationEnabled = false;
mServiceHandlesDoubleTap = false;
mRequestMultiFingerGestures = false;
+ mRequestTwoFingerPassthrough = false;
mIsDisplayMagnificationEnabled = false;
mIsAutoclickEnabled = false;
mUserNonInteractiveUiTimeout = 0;
@@ -456,6 +458,8 @@
.append(String.valueOf(mServiceHandlesDoubleTap));
pw.append(", requestMultiFingerGestures=")
.append(String.valueOf(mRequestMultiFingerGestures));
+ pw.append(", requestTwoFingerPassthrough=")
+ .append(String.valueOf(mRequestTwoFingerPassthrough));
pw.append(", displayMagnificationEnabled=").append(String.valueOf(
mIsDisplayMagnificationEnabled));
pw.append(", autoclickEnabled=").append(String.valueOf(mIsAutoclickEnabled));
@@ -790,6 +794,14 @@
public void setMultiFingerGesturesLocked(boolean enabled) {
mRequestMultiFingerGestures = enabled;
}
+ public boolean isTwoFingerPassthroughEnabledLocked() {
+ return mRequestTwoFingerPassthrough;
+ }
+
+ public void setTwoFingerPassthroughLocked(boolean enabled) {
+ mRequestTwoFingerPassthrough = 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 e9c70c6..8604fe7 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
@@ -94,8 +94,13 @@
private boolean mServiceHandlesDoubleTap = false;
// Whether multi-finger gestures are enabled.
boolean mMultiFingerGesturesEnabled;
+ // Whether the two-finger passthrough is enabled when multi-finger gestures are enabled.
+ private boolean mTwoFingerPassthroughEnabled;
// 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<>();
// Shared state information.
private TouchState mState;
@@ -105,6 +110,7 @@
mListener = listener;
mState = state;
mMultiFingerGesturesEnabled = false;
+ mTwoFingerPassthroughEnabled = false;
// Set up gestures.
// Start with double tap.
mGestures.add(new MultiTap(context, 2, GESTURE_DOUBLE_TAP, this));
@@ -161,14 +167,14 @@
mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 4, 3, GESTURE_4_FINGER_TRIPLE_TAP, this));
// Two-finger swipes.
- mMultiFingerGestures.add(
+ mTwoFingerSwipes.add(
new MultiFingerSwipe(context, 2, DOWN, GESTURE_2_FINGER_SWIPE_DOWN, this));
- mMultiFingerGestures.add(
+ mTwoFingerSwipes.add(
new MultiFingerSwipe(context, 2, LEFT, GESTURE_2_FINGER_SWIPE_LEFT, this));
- mMultiFingerGestures.add(
+ mTwoFingerSwipes.add(
new MultiFingerSwipe(context, 2, RIGHT, GESTURE_2_FINGER_SWIPE_RIGHT, this));
- mMultiFingerGestures.add(
- new MultiFingerSwipe(context, 2, UP, GESTURE_2_FINGER_SWIPE_UP, this));
+ mTwoFingerSwipes.add(new MultiFingerSwipe(context, 2, UP, GESTURE_2_FINGER_SWIPE_UP, this));
+ mMultiFingerGestures.addAll(mTwoFingerSwipes);
// Three-finger swipes.
mMultiFingerGestures.add(
new MultiFingerSwipe(context, 3, DOWN, GESTURE_3_FINGER_SWIPE_DOWN, this));
@@ -360,6 +366,25 @@
}
}
+ public boolean isTwoFingerPassthroughEnabled() {
+ return mTwoFingerPassthroughEnabled;
+ }
+
+ public void setTwoFingerPassthroughEnabled(boolean mode) {
+ if (mTwoFingerPassthroughEnabled != mode) {
+ mTwoFingerPassthroughEnabled = mode;
+ if (!mode) {
+ mMultiFingerGestures.addAll(mTwoFingerSwipes);
+ if (mMultiFingerGesturesEnabled) {
+ mGestures.addAll(mTwoFingerSwipes);
+ }
+ } else {
+ mMultiFingerGestures.removeAll(mTwoFingerSwipes);
+ mGestures.removeAll(mTwoFingerSwipes);
+ }
+ }
+ }
+
public void setServiceHandlesDoubleTap(boolean mode) {
mServiceHandlesDoubleTap = mode;
}
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 696702f..e1baefe 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -41,6 +41,7 @@
import android.content.Context;
import android.graphics.Region;
import android.os.Handler;
+import android.util.DisplayMetrics;
import android.util.Slog;
import android.view.InputDevice;
import android.view.MotionEvent;
@@ -91,12 +92,21 @@
// The timeout after which we are no longer trying to detect a gesture.
private static final int EXIT_GESTURE_DETECTION_TIMEOUT = 2000;
+ // The height of the top and bottom edges for edge-swipes.
+ // For now this is only used to allow three-finger edge-swipes from the bottom.
+ private static final float EDGE_SWIPE_HEIGHT_CM = 0.25f;
+
+ // The calculated edge height for the top and bottom edges.
+ private final float mEdgeSwipeHeightPixels;
// Timeout before trying to decide what the user is trying to do.
private final int mDetermineUserIntentTimeout;
// Slop between the first and second tap to be a double tap.
private final int mDoubleTapSlop;
+ // Slop to move before being considered a move rather than a tap.
+ private final int mTouchSlop;
+
// The current state of the touch explorer.
private TouchState mState;
@@ -174,6 +184,9 @@
mDispatcher = new EventDispatcher(context, mAms, super.getNext(), mState);
mDetermineUserIntentTimeout = ViewConfiguration.getDoubleTapTimeout();
mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
+ mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
+ mEdgeSwipeHeightPixels = metrics.ydpi / GestureUtils.CM_PER_INCH * EDGE_SWIPE_HEIGHT_CM;
mHandler = mainHandler;
mExitGestureDetectionModeDelayed = new ExitGestureDetectionModeDelayed();
mSendHoverEnterAndMoveDelayed = new SendHoverEnterAndMoveDelayed();
@@ -219,16 +232,10 @@
if (mState.isTouchExploring()) {
// If a touch exploration gesture is in progress send events for its end.
sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
- } else if (mState.isDragging()) {
- mDraggingPointerId = INVALID_POINTER_ID;
- // Send exit to all pointers that we have delivered.
- mDispatcher.sendUpForInjectedDownPointers(event, policyFlags);
- } else if (mState.isDelegating()) {
- // Send exit to all pointers that we have delivered.
- mDispatcher.sendUpForInjectedDownPointers(event, policyFlags);
- } else if (mState.isGestureDetecting()) {
- // No state specific cleanup required.
}
+ mDraggingPointerId = INVALID_POINTER_ID;
+ // Send exit to any pointers that we have delivered as part of delegating or dragging.
+ mDispatcher.sendUpForInjectedDownPointers(event, policyFlags);
// Remove all pending callbacks.
mSendHoverEnterAndMoveDelayed.cancel();
mSendHoverExitDelayed.cancel();
@@ -554,7 +561,26 @@
// stream consistent.
sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
}
+ if (mGestureDetector.isMultiFingerGesturesEnabled()
+ && mGestureDetector.isTwoFingerPassthroughEnabled()) {
+ if (event.getPointerCount() == 3) {
+ boolean isOnBottomEdge = false;
+ // If three fingers go down on the bottom edge of the screen, delegate immediately.
+ final long screenHeight = mContext.getResources().getDisplayMetrics().heightPixels;
+ for (int i = 0; i < TouchState.MAX_POINTER_COUNT; ++i) {
+ if (mReceivedPointerTracker.getReceivedPointerDownY(i)
+ > (screenHeight - mEdgeSwipeHeightPixels)) {
+ isOnBottomEdge = true;
+ }
+ }
+ if (isOnBottomEdge) {
+ mState.startDelegating();
+ mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags);
+ }
+ }
+ }
}
+
/**
* Handles ACTION_MOVE while in the touch interacting state. This is where transitions to
* delegating and dragging states are handled.
@@ -563,7 +589,7 @@
MotionEvent event, MotionEvent rawEvent, int policyFlags) {
final int pointerId = mReceivedPointerTracker.getPrimaryPointerId();
final int pointerIndex = event.findPointerIndex(pointerId);
- final int pointerIdBits = (1 << pointerId);
+ int pointerIdBits = (1 << pointerId);
switch (event.getPointerCount()) {
case 1:
// We have not started sending events since we try to
@@ -574,12 +600,26 @@
}
break;
case 2:
- if (mGestureDetector.isMultiFingerGesturesEnabled()) {
+ if (mGestureDetector.isMultiFingerGesturesEnabled()
+ && !mGestureDetector.isTwoFingerPassthroughEnabled()) {
return;
}
// Make sure we don't have any pending transitions to touch exploration
mSendHoverEnterAndMoveDelayed.cancel();
mSendHoverExitDelayed.cancel();
+ if (mGestureDetector.isMultiFingerGesturesEnabled()
+ && mGestureDetector.isTwoFingerPassthroughEnabled()) {
+ final float deltaX =
+ mReceivedPointerTracker.getReceivedPointerDownX(pointerId)
+ - rawEvent.getX(pointerIndex);
+ final float deltaY =
+ mReceivedPointerTracker.getReceivedPointerDownY(pointerId)
+ - rawEvent.getY(pointerIndex);
+ final double moveDelta = Math.hypot(deltaX, deltaY);
+ if (moveDelta < mTouchSlop) {
+ return;
+ }
+ }
// More than one pointer so the user is not touch exploring
// and now we have to decide whether to delegate or drag.
// Remove move history before send injected non-move events
@@ -588,8 +628,8 @@
// Two pointers moving in the same direction within
// a given distance perform a drag.
mState.startDragging();
- mDraggingPointerId = pointerId;
adjustEventLocationForDrag(event);
+ pointerIdBits = 1 << mDraggingPointerId;
event.setEdgeFlags(mReceivedPointerTracker.getLastReceivedDownEdgeFlags());
mDispatcher.sendMotionEvent(
event, ACTION_DOWN, rawEvent, pointerIdBits, policyFlags);
@@ -648,7 +688,8 @@
event, ACTION_HOVER_MOVE, rawEvent, pointerIdBits, policyFlags);
break;
case 2:
- if (mGestureDetector.isMultiFingerGesturesEnabled()) {
+ if (mGestureDetector.isMultiFingerGesturesEnabled()
+ && !mGestureDetector.isTwoFingerPassthroughEnabled()) {
return;
}
if (mSendHoverEnterAndMoveDelayed.isPending()) {
@@ -703,7 +744,8 @@
*/
private void handleMotionEventStateDragging(
MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- if (mGestureDetector.isMultiFingerGesturesEnabled()) {
+ if (mGestureDetector.isMultiFingerGesturesEnabled()
+ && !mGestureDetector.isTwoFingerPassthroughEnabled()) {
// Multi-finger gestures conflict with this functionality.
return;
}
@@ -756,6 +798,7 @@
// The two pointers are moving either in different directions or
// no close enough => delegate the gesture to the view hierarchy.
mState.startDelegating();
+ mDraggingPointerId = INVALID_POINTER_ID;
// Remove move history before send injected non-move events
event = MotionEvent.obtainNoHistory(event);
// Send an event to the end of the drag gesture.
@@ -767,6 +810,7 @@
break;
default:
mState.startDelegating();
+ mDraggingPointerId = INVALID_POINTER_ID;
event = MotionEvent.obtainNoHistory(event);
// Send an event to the end of the drag gesture.
mDispatcher.sendMotionEvent(
@@ -784,15 +828,16 @@
}
break;
case ACTION_UP:
- mAms.onTouchInteractionEnd();
- // Announce the end of a new touch interaction.
- mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_INTERACTION_END);
if (event.getPointerId(GestureUtils.getActionIndex(event)) == mDraggingPointerId) {
mDraggingPointerId = INVALID_POINTER_ID;
// Send an event to the end of the drag gesture.
mDispatcher.sendMotionEvent(
event, ACTION_UP, rawEvent, pointerIdBits, policyFlags);
}
+ mAms.onTouchInteractionEnd();
+ // Announce the end of a new touch interaction.
+ mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_INTERACTION_END);
+
break;
}
}
@@ -911,21 +956,55 @@
}
/**
- * Adjust the location of an injected event when performing a drag The new location will be in
- * between the two fingers touching the screen.
+ * Adjust the location of an injected event when performing a drag. The location will be the
+ * location of the finger closest to an edge of the screen.
*/
private void adjustEventLocationForDrag(MotionEvent event) {
-
final float firstPtrX = event.getX(0);
final float firstPtrY = event.getY(0);
+ final int firstPtrId = event.getPointerId(0);
final float secondPtrX = event.getX(1);
final float secondPtrY = event.getY(1);
- final int pointerIndex = event.findPointerIndex(mDraggingPointerId);
- final float deltaX =
- (pointerIndex == 0) ? (secondPtrX - firstPtrX) : (firstPtrX - secondPtrX);
- final float deltaY =
- (pointerIndex == 0) ? (secondPtrY - firstPtrY) : (firstPtrY - secondPtrY);
- event.offsetLocation(deltaX / 2, deltaY / 2);
+ final int secondPtrId = event.getPointerId(1);
+ float draggingX;
+ float draggingY;
+ if (mDraggingPointerId == INVALID_POINTER_ID) {
+ // The goal is to use the coordinates of the finger that is closest to its closest edge.
+ if (getDistanceToClosestEdge(firstPtrX, firstPtrY)
+ < getDistanceToClosestEdge(secondPtrX, secondPtrY)) {
+ draggingX = firstPtrX;
+ draggingY = firstPtrY;
+ mDraggingPointerId = firstPtrId;
+ } else {
+ draggingX = secondPtrX;
+ draggingY = secondPtrY;
+ mDraggingPointerId = secondPtrId;
+ }
+ } else {
+ // Just use the coordinates of the dragging pointer.
+ int pointerIndex = event.findPointerIndex(mDraggingPointerId);
+ draggingX = event.getX(pointerIndex);
+ draggingY = event.getY(pointerIndex);
+ }
+ event.setLocation(draggingX, draggingY);
+ }
+
+ private float getDistanceToClosestEdge(float x, float y) {
+ final long width = mContext.getResources().getDisplayMetrics().widthPixels;
+ final long height = mContext.getResources().getDisplayMetrics().heightPixels;
+ float distance = Float.MAX_VALUE;
+ if (x < (width - x)) {
+ distance = x;
+ } else {
+ distance = width - x;
+ }
+ if (distance > y) {
+ distance = y;
+ }
+ if (distance > (height - y)) {
+ distance = (height - y);
+ }
+ return distance;
}
public TouchState getState() {
@@ -954,6 +1033,13 @@
mGestureDetector.setMultiFingerGesturesEnabled(enabled);
}
+ /**
+ * This function turns on and off two-finger passthrough gestures such as drag and pinch when
+ * multi-finger gestures are enabled.
+ */
+ public void setTwoFingerPassthroughEnabled(boolean enabled) {
+ mGestureDetector.setTwoFingerPassthroughEnabled(enabled);
+ }
public void setGestureDetectionPassthroughRegion(Region region) {
mGestureDetectionPassthroughRegion = region;
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
index 6450a0f..763654d 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
@@ -42,6 +42,7 @@
import android.testing.DexmakerShareClassLoaderRule;
import android.view.InputDevice;
import android.view.MotionEvent;
+import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityEvent;
import androidx.test.InstrumentationRegistry;
@@ -87,6 +88,8 @@
private MotionEvent mLastEvent;
private TestHandler mHandler;
private TouchExplorer mTouchExplorer;
+ private Context mContext;
+ private int mTouchSlop;
private long mLastDownTime = Integer.MIN_VALUE;
// mock package-private GestureManifold class
@@ -121,12 +124,13 @@
if (Looper.myLooper() == null) {
Looper.prepare();
}
- Context context = InstrumentationRegistry.getContext();
- AccessibilityManagerService ams = new AccessibilityManagerService(context);
+ mContext = InstrumentationRegistry.getContext();
+ mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
+ AccessibilityManagerService ams = new AccessibilityManagerService(mContext);
GestureManifold detector = mock(GestureManifold.class);
mCaptor = new EventCaptor();
mHandler = new TestHandler();
- mTouchExplorer = new TouchExplorer(context, ams, detector, mHandler);
+ mTouchExplorer = new TouchExplorer(mContext, ams, detector, mHandler);
mTouchExplorer.setNext(mCaptor);
}
@@ -354,12 +358,12 @@
break;
case STATE_DRAGGING_2FINGERS:
goFromStateClearTo(STATE_TOUCH_EXPLORING_2FINGER);
- moveEachPointers(mLastEvent, p(10, 0), p(10, 0));
+ moveEachPointers(mLastEvent, p(mTouchSlop, 0), p(mTouchSlop, 0));
send(mLastEvent);
break;
case STATE_PINCH_2FINGERS:
goFromStateClearTo(STATE_DRAGGING_2FINGERS);
- moveEachPointers(mLastEvent, p(10, 0), p(-10, 1));
+ moveEachPointers(mLastEvent, p(mTouchSlop, 0), p(-mTouchSlop, 1));
send(mLastEvent);
break;
case STATE_MOVING_3FINGERS: