diff options
8 files changed, 206 insertions, 39 deletions
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java index ca22bf4a62dc..d334de60713a 100644 --- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java +++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java @@ -364,6 +364,18 @@ public class AccessibilityServiceInfo implements Parcelable { */ 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 @@ public class AccessibilityServiceInfo implements Parcelable { 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 @@ public class AccessibilityServiceInfo implements Parcelable { 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 9ad808a685a6..a167ab16f944 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 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ private boolean mRequestMultiFingerGestures; + private boolean mRequestTwoFingerPassthrough; + boolean mRequestFilterKeyEvents; boolean mRetrieveInteractiveWindows; @@ -325,8 +327,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ & 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 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ 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 8b40f610b4b3..cd9ab8db0854 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 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo */ 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 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo | 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 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo 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 b1340228ffa0..833aeecc4c47 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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub 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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub 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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub touchExplorationEnabled = true; serviceHandlesDoubleTapEnabled = service.isServiceHandlesDoubleTapEnabled(); requestMultiFingerGestures = service.isMultiFingerGesturesEnabled(); + requestTwoFingerPassthrough = service.isTwoFingerPassthroughEnabled(); break; } } @@ -2143,6 +2148,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } 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 f865aa7d6e37..4c9e44403026 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 @@ class AccessibilityUserState { 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 @@ class AccessibilityUserState { mIsTouchExplorationEnabled = false; mServiceHandlesDoubleTap = false; mRequestMultiFingerGestures = false; + mRequestTwoFingerPassthrough = false; mIsDisplayMagnificationEnabled = false; mIsAutoclickEnabled = false; mUserNonInteractiveUiTimeout = 0; @@ -456,6 +458,8 @@ class AccessibilityUserState { .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 @@ class AccessibilityUserState { 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 e9c70c60a322..8604fe7a7359 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 @@ class GestureManifold implements GestureMatcher.StateChangeListener { 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 @@ class GestureManifold implements GestureMatcher.StateChangeListener { 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 @@ class GestureManifold implements GestureMatcher.StateChangeListener { 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 @@ class GestureManifold implements GestureMatcher.StateChangeListener { } } + 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 696702fad730..e1baefed4425 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.annotation.NonNull; 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 @@ public class TouchExplorer extends BaseEventStreamTransformation // 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 @@ public class TouchExplorer extends BaseEventStreamTransformation 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 @@ public class TouchExplorer extends BaseEventStreamTransformation 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 @@ public class TouchExplorer extends BaseEventStreamTransformation // 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 @@ public class TouchExplorer extends BaseEventStreamTransformation 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 @@ public class TouchExplorer extends BaseEventStreamTransformation } 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 @@ public class TouchExplorer extends BaseEventStreamTransformation // 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 @@ public class TouchExplorer extends BaseEventStreamTransformation 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 @@ public class TouchExplorer extends BaseEventStreamTransformation */ 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 @@ public class TouchExplorer extends BaseEventStreamTransformation // 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 @@ public class TouchExplorer extends BaseEventStreamTransformation 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 @@ public class TouchExplorer extends BaseEventStreamTransformation } 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 @@ public class TouchExplorer extends BaseEventStreamTransformation } /** - * 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 @@ public class TouchExplorer extends BaseEventStreamTransformation 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 6450a0fc1453..763654d24047 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.os.SystemClock; 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 @@ public class TouchExplorerTest { 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 @@ public class TouchExplorerTest { 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 @@ public class TouchExplorerTest { 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: |