summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/GesturesObserver.java8
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureMatcher.java14
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGesturesObserver.java2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/SimpleSwipe.java7
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/TwoFingersDown.java74
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/TwoFingersDownOrSwipe.java123
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGesturesObserverTest.java22
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/SimpleSwipeTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownOrSwipeTest.java (renamed from services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownTest.java)80
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java60
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/utils/TouchEventGenerator.java142
12 files changed, 357 insertions, 183 deletions
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/GesturesObserver.java b/services/accessibility/java/com/android/server/accessibility/magnification/GesturesObserver.java
index feed18d438c7..3d8f5173d25a 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/GesturesObserver.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/GesturesObserver.java
@@ -98,8 +98,7 @@ public final class GesturesObserver implements GestureMatcher.StateChangeListene
}
mProcessMotionEvent = true;
for (int i = 0; i < mGestureMatchers.size(); i++) {
- final GestureMatcher matcher =
- mGestureMatchers.get(i);
+ final GestureMatcher matcher = mGestureMatchers.get(i);
matcher.onMotionEvent(event, rawEvent, policyFlags);
if (matcher.getState() == GestureMatcher.STATE_GESTURE_COMPLETED) {
clear();
@@ -128,7 +127,10 @@ public final class GesturesObserver implements GestureMatcher.StateChangeListene
MotionEvent rawEvent, int policyFlags) {
if (state == GestureMatcher.STATE_GESTURE_COMPLETED) {
mListener.onGestureCompleted(gestureId, event, rawEvent, policyFlags);
- //Clear the states in onMotionEvent().
+ // Ideally we clear the states in onMotionEvent(), this case is for hold gestures.
+ // If we clear before processing up event , then MultiTap matcher cancels the gesture
+ // due to incorrect state. It ends up listener#onGestureCancelled is called even
+ // the gesture is detected.
if (!mProcessMotionEvent) {
clear();
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureMatcher.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureMatcher.java
index 7a4d9e34b657..570e0ce5490d 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureMatcher.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureMatcher.java
@@ -33,7 +33,7 @@ import java.lang.annotation.RetentionPolicy;
class MagnificationGestureMatcher {
private static final int GESTURE_BASE = 100;
- public static final int GESTURE_TWO_FINGER_DOWN = GESTURE_BASE + 1;
+ public static final int GESTURE_TWO_FINGERS_DOWN_OR_SWIPE = GESTURE_BASE + 1;
public static final int GESTURE_SWIPE = GESTURE_BASE + 2;
public static final int GESTURE_SINGLE_TAP = GESTURE_BASE + 3;
public static final int GESTURE_SINGLE_TAP_AND_HOLD = GESTURE_BASE + 4;
@@ -41,7 +41,7 @@ class MagnificationGestureMatcher {
public static final int GESTURE_TRIPLE_TAP_AND_HOLD = GESTURE_BASE + 6;
@IntDef(prefix = {"GESTURE_MAGNIFICATION_"}, value = {
- GESTURE_TWO_FINGER_DOWN,
+ GESTURE_TWO_FINGERS_DOWN_OR_SWIPE,
GESTURE_SWIPE
})
@Retention(RetentionPolicy.SOURCE)
@@ -57,8 +57,8 @@ class MagnificationGestureMatcher {
switch (gestureId) {
case GESTURE_SWIPE:
return "GESTURE_SWIPE";
- case GESTURE_TWO_FINGER_DOWN:
- return "GESTURE_TWO_FINGER_DOWN";
+ case GESTURE_TWO_FINGERS_DOWN_OR_SWIPE:
+ return "GESTURE_TWO_FINGERS_DOWN_OR_SWIPE";
case GESTURE_SINGLE_TAP:
return "GESTURE_SINGLE_TAP";
case GESTURE_SINGLE_TAP_AND_HOLD:
@@ -71,6 +71,12 @@ class MagnificationGestureMatcher {
return "none";
}
+ /**
+ * @param context
+ * @return the duration in milliseconds between the first tap's down event and
+ * the second tap's down event to be considered that the user is going to performing
+ * panning/scaling gesture.
+ */
static int getMagnificationMultiTapTimeout(Context context) {
return ViewConfiguration.getDoubleTapTimeout() + context.getResources().getInteger(
R.integer.config_screen_magnification_multi_tap_adjustment);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGesturesObserver.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGesturesObserver.java
index a209086ba475..085c343ff631 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGesturesObserver.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGesturesObserver.java
@@ -65,7 +65,7 @@ class MagnificationGesturesObserver implements GesturesObserver.Listener {
* the last event before timeout.
*
* @see MagnificationGestureMatcher#GESTURE_SWIPE
- * @see MagnificationGestureMatcher#GESTURE_TWO_FINGER_DOWN
+ * @see MagnificationGestureMatcher#GESTURE_TWO_FINGERS_DOWN_OR_SWIPE
*/
void onGestureCompleted(@GestureId int gestureId, long lastDownEventTime,
List<MotionEventInfo> delayedEventQueue, MotionEvent event);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/SimpleSwipe.java b/services/accessibility/java/com/android/server/accessibility/magnification/SimpleSwipe.java
index cd5061fa3163..fa15ac1c0e4f 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/SimpleSwipe.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/SimpleSwipe.java
@@ -49,6 +49,11 @@ class SimpleSwipe extends GestureMatcher {
}
@Override
+ protected void onPointerDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ cancelGesture(event, rawEvent, policyFlags);
+ }
+
+ @Override
protected void onMove(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
if (gestureMatched(event, rawEvent, policyFlags)) {
completeGesture(event, rawEvent, policyFlags);
@@ -65,7 +70,7 @@ class SimpleSwipe extends GestureMatcher {
}
private boolean gestureMatched(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- return mLastDown != null && (distance(mLastDown, event) >= mSwipeMinDistance);
+ return mLastDown != null && (distance(mLastDown, event) > mSwipeMinDistance);
}
@Override
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/TwoFingersDown.java b/services/accessibility/java/com/android/server/accessibility/magnification/TwoFingersDown.java
deleted file mode 100644
index 173a5b82e003..000000000000
--- a/services/accessibility/java/com/android/server/accessibility/magnification/TwoFingersDown.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.accessibility.magnification;
-
-import android.content.Context;
-import android.os.Handler;
-import android.view.MotionEvent;
-
-import com.android.server.accessibility.gestures.GestureMatcher;
-
-/**
- *
- * This class is responsible for matching two fingers down gestures. The gesture matching
- * result is determined in a duration.
- */
-final class TwoFingersDown extends GestureMatcher {
-
- private MotionEvent mLastDown;
- private final int mDetectionDurationMillis;
-
- TwoFingersDown(Context context) {
- super(MagnificationGestureMatcher.GESTURE_TWO_FINGER_DOWN,
- new Handler(context.getMainLooper()), null);
- mDetectionDurationMillis = MagnificationGestureMatcher.getMagnificationMultiTapTimeout(
- context);
- }
-
- @Override
- protected void onDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- mLastDown = MotionEvent.obtain(event);
- cancelAfter(mDetectionDurationMillis, event, rawEvent, policyFlags);
- }
-
- @Override
- protected void onPointerDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- if (mLastDown == null) {
- cancelGesture(event, rawEvent, policyFlags);
- }
- completeGesture(event, rawEvent, policyFlags);
- }
-
- @Override
- protected void onUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- cancelGesture(event, rawEvent, policyFlags);
- }
-
- @Override
- public void clear() {
- if (mLastDown != null) {
- mLastDown.recycle();
- mLastDown = null;
- }
- super.clear();
- }
-
- @Override
- protected String getGestureName() {
- return this.getClass().getSimpleName();
- }
-}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/TwoFingersDownOrSwipe.java b/services/accessibility/java/com/android/server/accessibility/magnification/TwoFingersDownOrSwipe.java
new file mode 100644
index 000000000000..1742bd46d865
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/TwoFingersDownOrSwipe.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility.magnification;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Handler;
+import android.util.MathUtils;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+import com.android.server.accessibility.gestures.GestureMatcher;
+
+/**
+ * This class is responsible for detecting that the user is using two fingers to perform
+ * swiping gestures or just stay pressed on the screen. The gesture matching result is determined
+ * in a duration.
+ */
+final class TwoFingersDownOrSwipe extends GestureMatcher {
+
+ private final int mDoubleTapTimeout;
+ private final int mDetectionDurationMillis;
+ private final int mSwipeMinDistance;
+ private MotionEvent mFirstPointerDown;
+ private MotionEvent mSecondPointerDown;
+
+ TwoFingersDownOrSwipe(Context context) {
+ super(MagnificationGestureMatcher.GESTURE_TWO_FINGERS_DOWN_OR_SWIPE,
+ new Handler(context.getMainLooper()), null);
+ mDetectionDurationMillis = MagnificationGestureMatcher.getMagnificationMultiTapTimeout(
+ context);
+ mDoubleTapTimeout = ViewConfiguration.getDoubleTapTimeout();
+ mSwipeMinDistance = ViewConfiguration.get(context).getScaledTouchSlop();
+
+ }
+
+ @Override
+ protected void onDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ mFirstPointerDown = MotionEvent.obtain(event);
+ cancelAfter(mDetectionDurationMillis, event, rawEvent, policyFlags);
+ }
+
+ @Override
+ protected void onPointerDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mFirstPointerDown == null) {
+ cancelGesture(event, rawEvent, policyFlags);
+ }
+ if (event.getPointerCount() == 2) {
+ mSecondPointerDown = MotionEvent.obtain(event);
+ completeAfter(mDoubleTapTimeout, event, rawEvent, policyFlags);
+ } else {
+ cancelGesture(event, rawEvent, policyFlags);
+ }
+ }
+
+ @Override
+ protected void onMove(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mFirstPointerDown == null || mSecondPointerDown == null) {
+ return;
+ }
+ if (distance(mFirstPointerDown, /* move */ event) > mSwipeMinDistance) {
+ completeGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ if (distance(mSecondPointerDown, /* move */ event) > mSwipeMinDistance) {
+ // The second pointer is swiping.
+ completeGesture(event, rawEvent, policyFlags);
+ }
+ }
+
+ @Override
+ protected void onPointerUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ cancelGesture(event, rawEvent, policyFlags);
+ }
+
+ @Override
+ protected void onUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ cancelGesture(event, rawEvent, policyFlags);
+ }
+
+ @Override
+ public void clear() {
+ if (mFirstPointerDown != null) {
+ mFirstPointerDown.recycle();
+ mFirstPointerDown = null;
+ }
+ if (mSecondPointerDown != null) {
+ mSecondPointerDown.recycle();
+ mSecondPointerDown = null;
+ }
+ super.clear();
+ }
+
+ @Override
+ protected String getGestureName() {
+ return this.getClass().getSimpleName();
+ }
+
+ private static double distance(@NonNull MotionEvent downEvent, @NonNull MotionEvent moveEvent) {
+ final int downActionIndex = downEvent.getActionIndex();
+ final int downPointerId = downEvent.getPointerId(downActionIndex);
+ final int moveActionIndex = moveEvent.findPointerIndex(downPointerId);
+ if (moveActionIndex < 0) {
+ return -1;
+ }
+ return MathUtils.dist(downEvent.getX(downActionIndex), downEvent.getY(downActionIndex),
+ moveEvent.getX(moveActionIndex), moveEvent.getY(moveActionIndex));
+ }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
index 55a911eea821..fa3406217fa8 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
@@ -323,7 +323,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
* manipulate the window magnifier or want to interact with current UI. The rule of leaving
* this state is as follows:
* <ol>
- * <li> If {@link MagnificationGestureMatcher#GESTURE_TWO_FINGER_DOWN} is detected,
+ * <li> If {@link MagnificationGestureMatcher#GESTURE_TWO_FINGERS_DOWN_OR_SWIPE} is detected,
* {@link State} will be transited to {@link PanningScalingGestureState}.</li>
* <li> If other gesture is detected and the last motion event is neither ACTION_UP nor
* ACTION_CANCEL.
@@ -357,7 +357,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
new SimpleSwipe(context),
multiTap,
multiTapAndHold,
- new TwoFingersDown(context));
+ new TwoFingersDownOrSwipe(context));
}
@Override
@@ -399,7 +399,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
Slog.d(mLogTag,
"onGestureDetected : delayedEventQueue = " + delayedEventQueue);
}
- if (gestureId == MagnificationGestureMatcher.GESTURE_TWO_FINGER_DOWN
+ if (gestureId == MagnificationGestureMatcher.GESTURE_TWO_FINGERS_DOWN_OR_SWIPE
&& mWindowMagnificationMgr.pointersInWindow(mDisplayId, motionEvent) > 0) {
transitionTo(mObservePanningScalingState);
} else if (gestureId == MagnificationGestureMatcher.GESTURE_TRIPLE_TAP) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGesturesObserverTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGesturesObserverTest.java
index 895fb1757504..5dbf837b08b2 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGesturesObserverTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGesturesObserverTest.java
@@ -69,7 +69,7 @@ public class MagnificationGesturesObserverTest {
mContext = InstrumentationRegistry.getContext();
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mObserver = new MagnificationGesturesObserver(mCallback, new SimpleSwipe(mContext),
- new TwoFingersDown(mContext));
+ new TwoFingersDownOrSwipe(mContext));
}
@Test
@@ -77,9 +77,7 @@ public class MagnificationGesturesObserverTest {
final MotionEvent moveEvent = TouchEventGenerator.moveEvent(Display.DEFAULT_DISPLAY,
DEFAULT_X , DEFAULT_Y);
- mInstrumentation.runOnMainSync(() -> {
- mObserver.onMotionEvent(moveEvent, moveEvent, 0);
- });
+ mObserver.onMotionEvent(moveEvent, moveEvent, 0);
verify(mCallback).onGestureCancelled(eq(0L),
mEventInfoArgumentCaptor.capture(), argThat(new MotionEventMatcher(moveEvent)));
@@ -92,9 +90,7 @@ public class MagnificationGesturesObserverTest {
final MotionEvent downEvent = TouchEventGenerator.downEvent(Display.DEFAULT_DISPLAY,
DEFAULT_X , DEFAULT_Y);
- mInstrumentation.runOnMainSync(() -> {
- mObserver.onMotionEvent(downEvent, downEvent, 0);
- });
+ mObserver.onMotionEvent(downEvent, downEvent, 0);
verify(mCallback).onGestureCancelled(eq(0L),
mEventInfoArgumentCaptor.capture(), argThat(new MotionEventMatcher(downEvent)));
@@ -108,9 +104,7 @@ public class MagnificationGesturesObserverTest {
final int timeoutMillis = MagnificationGestureMatcher.getMagnificationMultiTapTimeout(
mContext) + 100;
- mInstrumentation.runOnMainSync(() -> {
- mObserver.onMotionEvent(downEvent, downEvent, 0);
- });
+ mObserver.onMotionEvent(downEvent, downEvent, 0);
verify(mCallback, timeout(timeoutMillis)).onGestureCancelled(eq(downEvent.getDownTime()),
mEventInfoArgumentCaptor.capture(), argThat(new MotionEventMatcher(downEvent)));
@@ -121,14 +115,12 @@ public class MagnificationGesturesObserverTest {
public void sendEventsOfSwiping_onGestureCompleted() {
final MotionEvent downEvent = TouchEventGenerator.downEvent(Display.DEFAULT_DISPLAY,
DEFAULT_X, DEFAULT_Y);
- final float swipeDistance = ViewConfiguration.get(mContext).getScaledTouchSlop();
+ final float swipeDistance = ViewConfiguration.get(mContext).getScaledTouchSlop() + 1;
final MotionEvent moveEvent = TouchEventGenerator.moveEvent(Display.DEFAULT_DISPLAY,
DEFAULT_X + swipeDistance, DEFAULT_Y + swipeDistance);
- mInstrumentation.runOnMainSync(() -> {
- mObserver.onMotionEvent(downEvent, downEvent, 0);
- mObserver.onMotionEvent(moveEvent, moveEvent, 0);
- });
+ mObserver.onMotionEvent(downEvent, downEvent, 0);
+ mObserver.onMotionEvent(moveEvent, moveEvent, 0);
verify(mCallback).onGestureCompleted(eq(MagnificationGestureMatcher.GESTURE_SWIPE),
eq(downEvent.getDownTime()), mEventInfoArgumentCaptor.capture(),
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/SimpleSwipeTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/SimpleSwipeTest.java
index 01631bf21a63..0ca631e4ed62 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/SimpleSwipeTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/SimpleSwipeTest.java
@@ -78,7 +78,7 @@ public class SimpleSwipeTest {
@Test
public void sendSwipeEvent_onGestureCompleted() {
- final float swipeDistance = ViewConfiguration.get(mContext).getScaledTouchSlop();
+ final float swipeDistance = ViewConfiguration.get(mContext).getScaledTouchSlop() + 1;
final MotionEvent downEvent = TouchEventGenerator.downEvent(Display.DEFAULT_DISPLAY,
DEFAULT_X, DEFAULT_Y);
final MotionEvent moveEvent = TouchEventGenerator.moveEvent(Display.DEFAULT_DISPLAY,
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownOrSwipeTest.java
index ed8dc4e470de..162d2a9d98af 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownOrSwipeTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,14 +16,20 @@
package com.android.server.accessibility.magnification;
+import static com.android.server.accessibility.utils.TouchEventGenerator.movePointer;
+import static com.android.server.accessibility.utils.TouchEventGenerator.twoPointersDownEvents;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.after;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import android.content.Context;
+import android.graphics.PointF;
import android.view.Display;
import android.view.MotionEvent;
+import android.view.ViewConfiguration;
import androidx.test.InstrumentationRegistry;
@@ -35,18 +41,20 @@ import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.List;
+
/**
- * Tests for {@link TwoFingersDown}.
+ * Tests for {@link TwoFingersDownOrSwipe}.
*/
-public class TwoFingersDownTest {
+public class TwoFingersDownOrSwipeTest {
private static final float DEFAULT_X = 100f;
private static final float DEFAULT_Y = 100f;
- private static Context sContext;
+ private static float sSwipeMinDistance;
private static int sTimeoutMillis;
+ private static Context sContext;
- private Context mContext;
private GesturesObserver mGesturesObserver;
@Mock
private GesturesObserver.Listener mListener;
@@ -56,13 +64,13 @@ public class TwoFingersDownTest {
sContext = InstrumentationRegistry.getContext();
sTimeoutMillis = MagnificationGestureMatcher.getMagnificationMultiTapTimeout(
sContext) + 100;
+ sSwipeMinDistance = ViewConfiguration.get(sContext).getScaledTouchSlop() + 1;
}
@Before
public void setUp() {
- mContext = InstrumentationRegistry.getContext();
MockitoAnnotations.initMocks(this);
- mGesturesObserver = new GesturesObserver(mListener, new TwoFingersDown(mContext));
+ mGesturesObserver = new GesturesObserver(mListener, new TwoFingersDownOrSwipe(sContext));
}
@Test
@@ -78,24 +86,16 @@ public class TwoFingersDownTest {
@Test
public void sendTwoFingerDownEvent_onGestureCompleted() {
- final MotionEvent downEvent = TouchEventGenerator.downEvent(Display.DEFAULT_DISPLAY,
- DEFAULT_X, DEFAULT_Y);
- final MotionEvent.PointerCoords defPointerCoords = new MotionEvent.PointerCoords();
- defPointerCoords.x = DEFAULT_X;
- defPointerCoords.y = DEFAULT_Y;
- final MotionEvent.PointerCoords secondPointerCoords = new MotionEvent.PointerCoords();
- secondPointerCoords.x = DEFAULT_X + 10;
- secondPointerCoords.y = DEFAULT_Y + 10;
+ final List<MotionEvent> downEvents = twoPointersDownEvents(Display.DEFAULT_DISPLAY,
+ new PointF(DEFAULT_X, DEFAULT_Y), new PointF(DEFAULT_X + 10, DEFAULT_Y + 10));
- final MotionEvent twoPointersDownEvent = TouchEventGenerator.twoPointersDownEvent(
- Display.DEFAULT_DISPLAY, defPointerCoords, secondPointerCoords);
-
- mGesturesObserver.onMotionEvent(downEvent, downEvent, 0);
- mGesturesObserver.onMotionEvent(twoPointersDownEvent, twoPointersDownEvent, 0);
+ for (MotionEvent event : downEvents) {
+ mGesturesObserver.onMotionEvent(event, event, 0);
+ }
verify(mListener, timeout(sTimeoutMillis)).onGestureCompleted(
- MagnificationGestureMatcher.GESTURE_TWO_FINGER_DOWN, twoPointersDownEvent,
- twoPointersDownEvent, 0);
+ MagnificationGestureMatcher.GESTURE_TWO_FINGERS_DOWN_OR_SWIPE, downEvents.get(1),
+ downEvents.get(1), 0);
}
@Test
@@ -108,7 +108,39 @@ public class TwoFingersDownTest {
mGesturesObserver.onMotionEvent(downEvent, downEvent, 0);
mGesturesObserver.onMotionEvent(upEvent, upEvent, 0);
- verify(mListener, timeout(sTimeoutMillis)).onGestureCancelled(any(MotionEvent.class),
- any(MotionEvent.class), eq(0));
+ verify(mListener, after(ViewConfiguration.getDoubleTapTimeout())).onGestureCancelled(
+ any(MotionEvent.class), any(MotionEvent.class), eq(0));
+ }
+
+ @Test
+ public void firstPointerMove_twoPointersDown_onGestureCompleted() {
+ final List<MotionEvent> downEvents = twoPointersDownEvents(Display.DEFAULT_DISPLAY,
+ new PointF(DEFAULT_X, DEFAULT_Y), new PointF(DEFAULT_X + 10, DEFAULT_Y + 10));
+ for (MotionEvent event : downEvents) {
+ mGesturesObserver.onMotionEvent(event, event, 0);
+ }
+ final MotionEvent moveEvent = movePointer(downEvents.get(1), 0, sSwipeMinDistance, 0);
+
+ mGesturesObserver.onMotionEvent(moveEvent, moveEvent, 0);
+
+ verify(mListener).onGestureCompleted(
+ MagnificationGestureMatcher.GESTURE_TWO_FINGERS_DOWN_OR_SWIPE, moveEvent,
+ moveEvent, 0);
+ }
+
+ @Test
+ public void secondPointerMove_twoPointersDown_onGestureCompleted() {
+ final List<MotionEvent> downEvents = twoPointersDownEvents(Display.DEFAULT_DISPLAY,
+ new PointF(DEFAULT_X, DEFAULT_Y), new PointF(DEFAULT_X + 10, DEFAULT_Y + 10));
+ for (MotionEvent event : downEvents) {
+ mGesturesObserver.onMotionEvent(event, event, 0);
+ }
+ final MotionEvent moveEvent = movePointer(downEvents.get(1), 1, sSwipeMinDistance, 0);
+
+ mGesturesObserver.onMotionEvent(moveEvent, moveEvent, 0);
+
+ verify(mListener).onGestureCompleted(
+ MagnificationGestureMatcher.GESTURE_TWO_FINGERS_DOWN_OR_SWIPE, moveEvent,
+ moveEvent, 0);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
index 4b7ebbc29b46..b9498d641ed7 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
@@ -24,13 +24,15 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.content.Context;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.os.RemoteException;
import android.util.DebugUtils;
import android.view.InputDevice;
import android.view.MotionEvent;
+import android.view.ViewConfiguration;
-import androidx.test.InstrumentationRegistry;
+import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.accessibility.EventStreamTransformation;
@@ -43,6 +45,7 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.List;
import java.util.function.IntConsumer;
/**
@@ -75,7 +78,7 @@ public class WindowMagnificationGestureHandlerTest {
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
- mContext = InstrumentationRegistry.getContext();
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
mWindowMagnificationManager = new WindowMagnificationManager(mContext, 0,
mock(WindowMagnificationManager.Callback.class));
mMockConnection = new MockWindowMagnificationConnection();
@@ -100,8 +103,8 @@ public class WindowMagnificationGestureHandlerTest {
* Covers following paths to get to and back between each state and {@link #STATE_IDLE}.
* <p>
* <br> IDLE -> SHOW_MAGNIFIER [label="a11y\nbtn"]
- * <br> SHOW_MAGNIFIER -> TWO_FINGER_DOWN [label="2hold"]
- * <br> TWO_FINGER_DOWN -> SHOW_MAGNIFIER [label="release"]
+ * <br> SHOW_MAGNIFIER -> TWO_FINGERS_DOWN [label="2hold"]
+ * <br> TWO_FINGERS_DOWN -> SHOW_MAGNIFIER [label="release"]
* <br> SHOW_MAGNIFIER -> IDLE [label="a11y\nbtn"]
* <br> IDLE -> SHOW_MAGNIFIER_TRIPLE_TAP [label="3tap"]
* <br> SHOW_MAGNIFIER_TRIPLE_TAP -> IDLE [label="3tap"]
@@ -112,18 +115,16 @@ public class WindowMagnificationGestureHandlerTest {
*/
@Test
public void testEachState_isReachableAndRecoverable() {
- InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- forEachState(state -> {
- goFromStateIdleTo(state);
- assertIn(state);
- returnToNormalFrom(state);
- try {
- assertIn(STATE_IDLE);
- } catch (AssertionError e) {
- throw new AssertionError("Failed while testing state " + stateToString(state),
- e);
- }
- });
+ forEachState(state -> {
+ goFromStateIdleTo(state);
+ assertIn(state);
+ returnToNormalFrom(state);
+ try {
+ assertIn(STATE_IDLE);
+ } catch (AssertionError e) {
+ throw new AssertionError("Failed while testing state " + stateToString(state),
+ e);
+ }
});
}
@@ -209,10 +210,19 @@ public class WindowMagnificationGestureHandlerTest {
case STATE_TWO_FINGERS_DOWN: {
goFromStateIdleTo(STATE_SHOW_MAGNIFIER_SHORTCUT);
final Rect frame = mMockConnection.getMirrorWindowFrame();
- send(downEvent(frame.centerX(), frame.centerY()));
- //Second finger is outside the window.
- send(twoPointerDownEvent(new float[]{frame.centerX(), frame.centerX() + 10},
- new float[]{frame.centerY(), frame.centerY() + 10}));
+ final PointF firstPointerDown = new PointF(frame.centerX(), frame.centerY());
+ // The second finger is outside the window.
+ final PointF secondPointerDown = new PointF(frame.right + 10,
+ frame.bottom + 10);
+ final List<MotionEvent> motionEvents =
+ TouchEventGenerator.twoPointersDownEvents(DISPLAY_0,
+ firstPointerDown, secondPointerDown);
+ for (MotionEvent downEvent: motionEvents) {
+ send(downEvent);
+ }
+ // Wait for two-finger down gesture completed.
+ Thread.sleep(ViewConfiguration.getDoubleTapTimeout());
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
break;
case STATE_SHOW_MAGNIFIER_TRIPLE_TAP: {
@@ -301,16 +311,6 @@ public class WindowMagnificationGestureHandlerTest {
send(upEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y));
}
- private MotionEvent twoPointerDownEvent(float[] x, float[] y) {
- final MotionEvent.PointerCoords defPointerCoords = new MotionEvent.PointerCoords();
- defPointerCoords.x = x[0];
- defPointerCoords.y = y[0];
- final MotionEvent.PointerCoords pointerCoords = new MotionEvent.PointerCoords();
- pointerCoords.x = x[1];
- pointerCoords.y = y[1];
- return TouchEventGenerator.twoPointersDownEvent(DISPLAY_0, defPointerCoords, pointerCoords);
- }
-
private String stateDump() {
return "\nCurrent state dump:\n" + mWindowMagnificationGestureHandler.mCurrentState;
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/utils/TouchEventGenerator.java b/services/tests/servicestests/src/com/android/server/accessibility/utils/TouchEventGenerator.java
index a05881f78892..fbcde533aa9f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/utils/TouchEventGenerator.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/utils/TouchEventGenerator.java
@@ -19,16 +19,19 @@ package com.android.server.accessibility.utils;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_POINTER_DOWN;
+import static android.view.MotionEvent.ACTION_POINTER_UP;
import static android.view.MotionEvent.ACTION_UP;
-import static android.view.MotionEvent.PointerCoords;
+import android.graphics.PointF;
import android.os.SystemClock;
import android.view.InputDevice;
import android.view.MotionEvent;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* generates {@link MotionEvent} with source {@link InputDevice#SOURCE_TOUCHSCREEN}
- *
*/
public class TouchEventGenerator {
@@ -39,44 +42,68 @@ public class TouchEventGenerator {
public static MotionEvent moveEvent(int displayId, float x, float y) {
return generateSingleTouchEvent(displayId, ACTION_MOVE, x, y);
}
+
public static MotionEvent upEvent(int displayId, float x, float y) {
return generateSingleTouchEvent(displayId, ACTION_UP, x, y);
}
- public static MotionEvent twoPointersDownEvent(int displayId, PointerCoords defPointerCoords,
- PointerCoords pointerCoords) {
- return generateTwoPointersEvent(displayId, ACTION_POINTER_DOWN, defPointerCoords,
- pointerCoords);
- }
-
private static MotionEvent generateSingleTouchEvent(int displayId, int action, float x,
float y) {
- final long downTime = SystemClock.uptimeMillis();
- final MotionEvent ev = MotionEvent.obtain(downTime, downTime,
- action, x, y, 0);
- ev.setDisplayId(displayId);
- ev.setSource(InputDevice.SOURCE_TOUCHSCREEN);
- return ev;
+ return generateMultiplePointersEvent(displayId, action, new PointF(x, y));
}
- private static MotionEvent generateTwoPointersEvent(int displayId, int action,
- PointerCoords defPointerCoords, PointerCoords pointerCoords) {
- final long downTime = SystemClock.uptimeMillis();
- MotionEvent.PointerProperties defPointerProperties = new MotionEvent.PointerProperties();
- defPointerProperties.id = 0;
- defPointerProperties.toolType = MotionEvent.TOOL_TYPE_FINGER;
- MotionEvent.PointerProperties pointerProperties = new MotionEvent.PointerProperties();
- pointerProperties.id = 1;
- pointerProperties.toolType = MotionEvent.TOOL_TYPE_FINGER;
+ /**
+ * Creates a list of {@link MotionEvent} with given pointers location.
+ *
+ * @param displayId the display id
+ * @param pointF1 location on the screen of the second pointer.
+ * @param pointF2 location on the screen of the second pointer.
+ * @return a list of {@link MotionEvent} with {@link MotionEvent#ACTION_DOWN} and {@link
+ * MotionEvent#ACTION_POINTER_DOWN}.
+ */
+ public static List<MotionEvent> twoPointersDownEvents(int displayId, PointF pointF1,
+ PointF pointF2) {
+ final List<MotionEvent> downEvents = new ArrayList<>();
+ final MotionEvent downEvent = generateMultiplePointersEvent(displayId,
+ MotionEvent.ACTION_DOWN, pointF1);
+ downEvents.add(downEvent);
+ final int actionIndex = 1 << MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+ final int action = ACTION_POINTER_DOWN | actionIndex;
+
+ final MotionEvent twoPointersDownEvent = generateMultiplePointersEvent(displayId, action,
+ pointF1, pointF2);
+ downEvents.add(twoPointersDownEvent);
+ return downEvents;
+ }
+
+ private static MotionEvent generateMultiplePointersEvent(int displayId, int action,
+ PointF... pointFs) {
+ final int length = pointFs.length;
+ final MotionEvent.PointerCoords[] pointerCoordsArray =
+ new MotionEvent.PointerCoords[length];
+ final MotionEvent.PointerProperties[] pointerPropertiesArray =
+ new MotionEvent.PointerProperties[length];
+ for (int i = 0; i < length; i++) {
+ MotionEvent.PointerCoords pointerCoords = new MotionEvent.PointerCoords();
+ pointerCoords.x = pointFs[i].x;
+ pointerCoords.y = pointFs[i].y;
+ pointerCoordsArray[i] = pointerCoords;
+
+ MotionEvent.PointerProperties pointerProperties = new MotionEvent.PointerProperties();
+ pointerProperties.id = i;
+ pointerProperties.toolType = MotionEvent.TOOL_TYPE_FINGER;
+ pointerPropertiesArray[i] = pointerProperties;
+ }
+
+ final long downTime = SystemClock.uptimeMillis();
final MotionEvent ev = MotionEvent.obtain(
/* downTime */ downTime,
/* eventTime */ downTime,
/* action */ action,
- /* pointerCount */ 2,
- /* pointerProperties */ new MotionEvent.PointerProperties[] {
- defPointerProperties, pointerProperties},
- /* pointerCoords */ new PointerCoords[] { defPointerCoords, pointerCoords },
+ /* pointerCount */ length,
+ /* pointerProperties */ pointerPropertiesArray,
+ /* pointerCoords */ pointerCoordsArray,
/* metaState */ 0,
/* buttonState */ 0,
/* xPrecision */ 1.0f,
@@ -88,4 +115,65 @@ public class TouchEventGenerator {
ev.setDisplayId(displayId);
return ev;
}
+
+ /**
+ * Generates a move event that moves the pointer of the original event with given index.
+ * The original event should not be up event and we don't support
+ * {@link MotionEvent#ACTION_POINTER_UP} now.
+ *
+ * @param originalEvent the move or down event
+ * @param pointerIndex the index of the pointer we want to move.
+ * @param offsetX the offset in X coordinate.
+ * @param offsetY the offset in Y coordinate.
+ * @return a motion event with move action.
+ */
+ public static MotionEvent movePointer(MotionEvent originalEvent, int pointerIndex,
+ float offsetX, float offsetY) {
+ if (originalEvent.getActionMasked() == ACTION_UP) {
+ throw new IllegalArgumentException("No pointer is on the screen");
+ }
+
+ if (originalEvent.getActionMasked() == ACTION_POINTER_UP) {
+ throw new IllegalArgumentException("unsupported yet,please implement it first");
+ }
+
+ final int pointerCount = originalEvent.getPointerCount();
+ if (pointerIndex >= pointerCount) {
+ throw new IllegalArgumentException(
+ pointerIndex + "is not available with pointer count" + pointerCount);
+ }
+ final int action = MotionEvent.ACTION_MOVE;
+ final MotionEvent.PointerProperties[] pp = new MotionEvent.PointerProperties[pointerCount];
+ for (int i = 0; i < pointerCount; i++) {
+ MotionEvent.PointerProperties pointerProperty = new MotionEvent.PointerProperties();
+ originalEvent.getPointerProperties(i, pointerProperty);
+ pp[i] = pointerProperty;
+ }
+
+ final MotionEvent.PointerCoords[] pc = new MotionEvent.PointerCoords[pointerCount];
+ for (int i = 0; i < pointerCount; i++) {
+ MotionEvent.PointerCoords pointerCoord = new MotionEvent.PointerCoords();
+ originalEvent.getPointerCoords(i, pointerCoord);
+ pc[i] = pointerCoord;
+ }
+ pc[pointerIndex].x += offsetX;
+ pc[pointerIndex].y += offsetY;
+ final MotionEvent ev = MotionEvent.obtain(
+ /* downTime */ originalEvent.getDownTime(),
+ /* eventTime */ SystemClock.uptimeMillis(),
+ /* action */ action,
+ /* pointerCount */ 2,
+ /* pointerProperties */ pp,
+ /* pointerCoords */ pc,
+ /* metaState */ 0,
+ /* buttonState */ 0,
+ /* xPrecision */ 1.0f,
+ /* yPrecision */ 1.0f,
+ /* deviceId */ 0,
+ /* edgeFlags */ 0,
+ /* source */ originalEvent.getSource(),
+ /* flags */ originalEvent.getFlags());
+ ev.setDisplayId(originalEvent.getDisplayId());
+ return ev;
+ }
}