Loading services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java +2 −2 Original line number Diff line number Diff line Loading @@ -111,9 +111,9 @@ class GestureManifold implements GestureMatcher.StateChangeListener { // Shared state information. private TouchState mState; GestureManifold(Context context, Listener listener, TouchState state) { GestureManifold(Context context, Listener listener, TouchState state, Handler handler) { mContext = context; mHandler = new Handler(context.getMainLooper()); mHandler = handler; mListener = listener; mState = state; mMultiFingerGesturesEnabled = false; Loading services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java +11 −3 Original line number Diff line number Diff line Loading @@ -199,7 +199,7 @@ public class TouchExplorer extends BaseEventStreamTransformation new SendAccessibilityEventDelayed( TYPE_TOUCH_INTERACTION_END, mDetermineUserIntentTimeout); if (detector == null) { mGestureDetector = new GestureManifold(context, this, mState); mGestureDetector = new GestureManifold(context, this, mState, mHandler); } else { mGestureDetector = detector; } Loading Loading @@ -391,7 +391,6 @@ public class TouchExplorer extends BaseEventStreamTransformation public boolean onGestureStarted() { // We have to perform gesture detection, so // clear the current state and try to detect. mState.startGestureDetecting(); mSendHoverEnterAndMoveDelayed.cancel(); mSendHoverExitDelayed.cancel(); mExitGestureDetectionModeDelayed.post(); Loading Loading @@ -1195,7 +1194,7 @@ public class TouchExplorer extends BaseEventStreamTransformation } private boolean shouldPerformGestureDetection(MotionEvent event) { if (mState.isDelegating()) { if (mState.isDelegating() || mState.isDragging()) { return false; } if (event.getActionMasked() == ACTION_DOWN) { Loading Loading @@ -1288,6 +1287,15 @@ public class TouchExplorer extends BaseEventStreamTransformation } public void run() { if (mReceivedPointerTracker.getReceivedPointerDownCount() > 1) { // Multi-finger touch exploration doesn't make sense. Slog.e( LOG_TAG, "Attempted touch exploration with " + mReceivedPointerTracker.getReceivedPointerDownCount() + " pointers down."); return; } // Send an accessibility event to announce the touch exploration start. mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_START); if (isSendMotionEventsEnabled()) { Loading services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java +0 −3 Original line number Diff line number Diff line Loading @@ -207,9 +207,6 @@ public class TouchState { case AccessibilityEvent.TYPE_GESTURE_DETECTION_START: startGestureDetecting(); break; case AccessibilityEvent.TYPE_GESTURE_DETECTION_END: startTouchInteracting(); break; default: break; } Loading services/tests/servicestests/src/com/android/server/accessibility/gestures/GestureManifoldTest.java +3 −1 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.accessibilityservice.AccessibilityService; import android.content.Context; import android.graphics.Point; import android.graphics.PointF; import android.os.Handler; import android.view.MotionEvent; import androidx.test.InstrumentationRegistry; Loading Loading @@ -56,7 +57,8 @@ public class GestureManifoldTest { // Construct a testable GestureManifold. mResultListener = mock(GestureManifold.Listener.class); mState = new TouchState(); mManifold = new GestureManifold(context, mResultListener, mState); Handler handler = new Handler(context.getMainLooper()); mManifold = new GestureManifold(context, mResultListener, mState, handler); // Play the role of touch explorer in updating the shared state. when(mResultListener.onGestureStarted()).thenReturn(onGestureStarted()); Loading services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java +43 −9 Original line number Diff line number Diff line Loading @@ -35,7 +35,6 @@ import static com.android.server.accessibility.gestures.TouchState.STATE_TOUCH_E import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import android.content.Context; import android.graphics.PointF; Loading Loading @@ -129,10 +128,9 @@ public class TouchExplorerTest { 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(mContext, ams, detector, mHandler); mTouchExplorer = new TouchExplorer(mContext, ams, null, mHandler); mTouchExplorer.setNext(mCaptor); } Loading Loading @@ -272,8 +270,8 @@ public class TouchExplorerTest { // Wait for the views responding to hover enter/move events. mHandler.fastForward(oneThirdUserIntentTimeout); // Simulate receiving the a11y exit event sent by the first view. AccessibilityEvent a11yExitEvent = AccessibilityEvent.obtain( AccessibilityEvent.TYPE_VIEW_HOVER_EXIT); AccessibilityEvent a11yExitEvent = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT); mTouchExplorer.onAccessibilityEvent(a11yExitEvent); // Wait for running the hover exit event runnable. After it, touch-exploration end event Loading Loading @@ -364,6 +362,39 @@ public class TouchExplorerTest { assertCapturedEventsNoHistory(); } @Test public void testCanceledGesture_shouldDoNothing() { mTouchExplorer.setMultiFingerGesturesEnabled(true); mTouchExplorer.setTwoFingerPassthroughEnabled(true); // Start a three-finger swipe. send(downEvent()); send(pointerDownEvent()); send(thirdPointerDownEvent()); moveEachPointers(mLastEvent, p(0, 200), p(0, 200), p(0, 200)); send(mLastEvent); assertState(STATE_GESTURE_DETECTING); mHandler.fastForward(2 * (int) Swipe.MAX_TIME_TO_CONTINUE_SWIPE_MS); // Lift the third finger but keep the other two going. send(thirdPointerUpEvent()); // Manually construct the next move event. Using moveEachPointers() will batch the move // event onto the pointer up event which will mean that the move event still has a pointer // count of 3. // Todo: refactor to avoid using batching as there is no special reason to do it that way. float[] x = new float[2]; float[] y = new float[2]; x[0] = mLastEvent.getX(0) + 100; x[1] = mLastEvent.getX(1) + 100; y[0] = mLastEvent.getY(0) + 100; y[1] = mLastEvent.getY(1) + 100; send(manyPointerEvent(ACTION_MOVE, x, y)); // Ensure that no two-finger passthrough is being executed. assertState(STATE_GESTURE_DETECTING); assertNoCapturedEvents(); send(pointerUpEvent()); send(upEvent()); mTouchExplorer.setMultiFingerGesturesEnabled(false); } private static MotionEvent fromTouchscreen(MotionEvent ev) { ev.setSource(InputDevice.SOURCE_TOUCHSCREEN); return ev; Loading Loading @@ -414,8 +445,7 @@ public class TouchExplorerTest { throw new IllegalArgumentException("Illegal state: " + state); } } catch (Throwable t) { throw new RuntimeException( "Failed to go to state " + stateToString(state), t); throw new RuntimeException("Failed to go to state " + stateToString(state), t); } } Loading Loading @@ -465,6 +495,10 @@ public class TouchExplorerTest { TouchState.getStateSymbolicName(mTouchExplorer.getState().getState())); } private void assertNoCapturedEvents() { assertEquals(0, getCapturedEvents().size()); } private void assertCapturedEvents(int... actionsInOrder) { final int eventCount = actionsInOrder.length; assertEquals(eventCount, getCapturedEvents().size()); Loading Loading @@ -623,8 +657,8 @@ public class TouchExplorerTest { } /** * A {@link android.os.Handler} that doesn't process messages until {@link * #fastForward(int)} is invoked. * A {@link android.os.Handler} that doesn't process messages until {@link #fastForward(int)} is * invoked. * * @see com.android.server.testutils.TestHandler */ Loading Loading
services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java +2 −2 Original line number Diff line number Diff line Loading @@ -111,9 +111,9 @@ class GestureManifold implements GestureMatcher.StateChangeListener { // Shared state information. private TouchState mState; GestureManifold(Context context, Listener listener, TouchState state) { GestureManifold(Context context, Listener listener, TouchState state, Handler handler) { mContext = context; mHandler = new Handler(context.getMainLooper()); mHandler = handler; mListener = listener; mState = state; mMultiFingerGesturesEnabled = false; Loading
services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java +11 −3 Original line number Diff line number Diff line Loading @@ -199,7 +199,7 @@ public class TouchExplorer extends BaseEventStreamTransformation new SendAccessibilityEventDelayed( TYPE_TOUCH_INTERACTION_END, mDetermineUserIntentTimeout); if (detector == null) { mGestureDetector = new GestureManifold(context, this, mState); mGestureDetector = new GestureManifold(context, this, mState, mHandler); } else { mGestureDetector = detector; } Loading Loading @@ -391,7 +391,6 @@ public class TouchExplorer extends BaseEventStreamTransformation public boolean onGestureStarted() { // We have to perform gesture detection, so // clear the current state and try to detect. mState.startGestureDetecting(); mSendHoverEnterAndMoveDelayed.cancel(); mSendHoverExitDelayed.cancel(); mExitGestureDetectionModeDelayed.post(); Loading Loading @@ -1195,7 +1194,7 @@ public class TouchExplorer extends BaseEventStreamTransformation } private boolean shouldPerformGestureDetection(MotionEvent event) { if (mState.isDelegating()) { if (mState.isDelegating() || mState.isDragging()) { return false; } if (event.getActionMasked() == ACTION_DOWN) { Loading Loading @@ -1288,6 +1287,15 @@ public class TouchExplorer extends BaseEventStreamTransformation } public void run() { if (mReceivedPointerTracker.getReceivedPointerDownCount() > 1) { // Multi-finger touch exploration doesn't make sense. Slog.e( LOG_TAG, "Attempted touch exploration with " + mReceivedPointerTracker.getReceivedPointerDownCount() + " pointers down."); return; } // Send an accessibility event to announce the touch exploration start. mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_START); if (isSendMotionEventsEnabled()) { Loading
services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java +0 −3 Original line number Diff line number Diff line Loading @@ -207,9 +207,6 @@ public class TouchState { case AccessibilityEvent.TYPE_GESTURE_DETECTION_START: startGestureDetecting(); break; case AccessibilityEvent.TYPE_GESTURE_DETECTION_END: startTouchInteracting(); break; default: break; } Loading
services/tests/servicestests/src/com/android/server/accessibility/gestures/GestureManifoldTest.java +3 −1 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.accessibilityservice.AccessibilityService; import android.content.Context; import android.graphics.Point; import android.graphics.PointF; import android.os.Handler; import android.view.MotionEvent; import androidx.test.InstrumentationRegistry; Loading Loading @@ -56,7 +57,8 @@ public class GestureManifoldTest { // Construct a testable GestureManifold. mResultListener = mock(GestureManifold.Listener.class); mState = new TouchState(); mManifold = new GestureManifold(context, mResultListener, mState); Handler handler = new Handler(context.getMainLooper()); mManifold = new GestureManifold(context, mResultListener, mState, handler); // Play the role of touch explorer in updating the shared state. when(mResultListener.onGestureStarted()).thenReturn(onGestureStarted()); Loading
services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java +43 −9 Original line number Diff line number Diff line Loading @@ -35,7 +35,6 @@ import static com.android.server.accessibility.gestures.TouchState.STATE_TOUCH_E import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import android.content.Context; import android.graphics.PointF; Loading Loading @@ -129,10 +128,9 @@ public class TouchExplorerTest { 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(mContext, ams, detector, mHandler); mTouchExplorer = new TouchExplorer(mContext, ams, null, mHandler); mTouchExplorer.setNext(mCaptor); } Loading Loading @@ -272,8 +270,8 @@ public class TouchExplorerTest { // Wait for the views responding to hover enter/move events. mHandler.fastForward(oneThirdUserIntentTimeout); // Simulate receiving the a11y exit event sent by the first view. AccessibilityEvent a11yExitEvent = AccessibilityEvent.obtain( AccessibilityEvent.TYPE_VIEW_HOVER_EXIT); AccessibilityEvent a11yExitEvent = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT); mTouchExplorer.onAccessibilityEvent(a11yExitEvent); // Wait for running the hover exit event runnable. After it, touch-exploration end event Loading Loading @@ -364,6 +362,39 @@ public class TouchExplorerTest { assertCapturedEventsNoHistory(); } @Test public void testCanceledGesture_shouldDoNothing() { mTouchExplorer.setMultiFingerGesturesEnabled(true); mTouchExplorer.setTwoFingerPassthroughEnabled(true); // Start a three-finger swipe. send(downEvent()); send(pointerDownEvent()); send(thirdPointerDownEvent()); moveEachPointers(mLastEvent, p(0, 200), p(0, 200), p(0, 200)); send(mLastEvent); assertState(STATE_GESTURE_DETECTING); mHandler.fastForward(2 * (int) Swipe.MAX_TIME_TO_CONTINUE_SWIPE_MS); // Lift the third finger but keep the other two going. send(thirdPointerUpEvent()); // Manually construct the next move event. Using moveEachPointers() will batch the move // event onto the pointer up event which will mean that the move event still has a pointer // count of 3. // Todo: refactor to avoid using batching as there is no special reason to do it that way. float[] x = new float[2]; float[] y = new float[2]; x[0] = mLastEvent.getX(0) + 100; x[1] = mLastEvent.getX(1) + 100; y[0] = mLastEvent.getY(0) + 100; y[1] = mLastEvent.getY(1) + 100; send(manyPointerEvent(ACTION_MOVE, x, y)); // Ensure that no two-finger passthrough is being executed. assertState(STATE_GESTURE_DETECTING); assertNoCapturedEvents(); send(pointerUpEvent()); send(upEvent()); mTouchExplorer.setMultiFingerGesturesEnabled(false); } private static MotionEvent fromTouchscreen(MotionEvent ev) { ev.setSource(InputDevice.SOURCE_TOUCHSCREEN); return ev; Loading Loading @@ -414,8 +445,7 @@ public class TouchExplorerTest { throw new IllegalArgumentException("Illegal state: " + state); } } catch (Throwable t) { throw new RuntimeException( "Failed to go to state " + stateToString(state), t); throw new RuntimeException("Failed to go to state " + stateToString(state), t); } } Loading Loading @@ -465,6 +495,10 @@ public class TouchExplorerTest { TouchState.getStateSymbolicName(mTouchExplorer.getState().getState())); } private void assertNoCapturedEvents() { assertEquals(0, getCapturedEvents().size()); } private void assertCapturedEvents(int... actionsInOrder) { final int eventCount = actionsInOrder.length; assertEquals(eventCount, getCapturedEvents().size()); Loading Loading @@ -623,8 +657,8 @@ public class TouchExplorerTest { } /** * A {@link android.os.Handler} that doesn't process messages until {@link * #fastForward(int)} is invoked. * A {@link android.os.Handler} that doesn't process messages until {@link #fastForward(int)} is * invoked. * * @see com.android.server.testutils.TestHandler */ Loading