Do not transition from gesture detection state after gesture cancelation.
The agreed-upon behavior has always been that after gesture cancelation, touch explorer would exist in essentially a dead state. It should not be possible to execute a two-finger passthrough gesture for example after doing an invalid gesture. This is likely why we see GESTURE_PASSTHROUGH in cases where we expect GESTURE_UNKNOWN. Fix: 171421786 Test: manual Change-Id: Idce921c5278dea174ec5dad5cecf046f12ab113d Change-Id: If9aeb611417bba08edd9f5a11f3d107c0b477bb8
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
@@ -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) {
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user