summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Jean Chen <chenjean@google.com> 2023-11-08 04:04:38 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2023-11-08 04:04:38 +0000
commit79d91aa990ba6d2c39233449dd1fc0e8b209a84e (patch)
treed5f4e6402d25ccbc2e32eea80e1dad4ef0588e45
parentf6235a2397f3565739b5e8bec2c5f2593b3cca5a (diff)
parent767587b577200de01de72919743353dd62d625aa (diff)
Merge "feat(MultiFingerMultiTap): Implement two finger triple tap feature on WindowMagnificationGestureHandler" into main
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java4
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java4
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java15
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java130
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/utils/TouchEventGenerator.java26
5 files changed, 162 insertions, 17 deletions
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java
index 46b46288cf84..9c6dfc61cb3b 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java
@@ -31,7 +31,7 @@ import java.util.Arrays;
* This class matches multi-finger multi-tap gestures. The number of fingers and the number of taps
* for each instance is specified in the constructor.
*/
-class MultiFingerMultiTap extends GestureMatcher {
+public class MultiFingerMultiTap extends GestureMatcher {
// The target number of taps.
final int mTargetTapCount;
@@ -56,7 +56,7 @@ class MultiFingerMultiTap extends GestureMatcher {
* @throws IllegalArgumentException if <code>fingers<code/> is less than 2
* or <code>taps<code/> is not positive.
*/
- MultiFingerMultiTap(
+ public MultiFingerMultiTap(
Context context,
int fingers,
int taps,
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java
index 9c541008f33f..f586036f62a0 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java
@@ -23,9 +23,9 @@ import android.view.MotionEvent;
* This class matches gestures of the form multi-finger multi-tap and hold. The number of fingers
* and taps for each instance is specified in the constructor.
*/
-class MultiFingerMultiTapAndHold extends MultiFingerMultiTap {
+public class MultiFingerMultiTapAndHold extends MultiFingerMultiTap {
- MultiFingerMultiTapAndHold(
+ public MultiFingerMultiTapAndHold(
Context context,
int fingers,
int taps,
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 5953d0d309de..36e751181cfd 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
@@ -41,6 +41,8 @@ import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.EventStreamTransformation;
import com.android.server.accessibility.Flags;
import com.android.server.accessibility.gestures.GestureMatcher;
+import com.android.server.accessibility.gestures.MultiFingerMultiTap;
+import com.android.server.accessibility.gestures.MultiFingerMultiTapAndHold;
import com.android.server.accessibility.gestures.MultiTap;
import com.android.server.accessibility.gestures.MultiTapAndHold;
@@ -476,6 +478,15 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
null));
mGestureMatchers.add(new TwoFingersDownOrSwipe(context));
+ if (mDetectTwoFingerTripleTap) {
+ mGestureMatchers.add(new MultiFingerMultiTap(context, /* fingers= */ 2,
+ /* taps= */ 3, MagnificationGestureMatcher.GESTURE_TRIPLE_TAP,
+ null));
+ mGestureMatchers.add(new MultiFingerMultiTapAndHold(context, /* fingers= */ 2,
+ /* taps= */ 3, MagnificationGestureMatcher.GESTURE_TRIPLE_TAP_AND_HOLD,
+ null));
+ }
+
mGesturesObserver = new MagnificationGesturesObserver(this,
mGestureMatchers.toArray(new GestureMatcher[mGestureMatchers.size()]));
} else {
@@ -512,7 +523,9 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
@Override
public boolean shouldStopDetection(MotionEvent motionEvent) {
return !mWindowMagnificationMgr.isWindowMagnifierEnabled(mDisplayId)
- && !mDetectSingleFingerTripleTap;
+ && !mDetectSingleFingerTripleTap
+ && !(mDetectTwoFingerTripleTap
+ && Flags.enableMagnificationMultipleFingerMultipleTapGesture());
}
@Override
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 c88d6e45218f..612a091a6b1b 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
@@ -29,6 +29,9 @@ import android.graphics.PointF;
import android.graphics.Rect;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.testing.TestableContext;
import android.util.DebugUtils;
import android.view.InputDevice;
@@ -40,6 +43,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.EventStreamTransformation;
+import com.android.server.accessibility.Flags;
import com.android.server.accessibility.utils.TouchEventGenerator;
import org.junit.After;
@@ -59,20 +63,29 @@ import java.util.function.IntConsumer;
@RunWith(AndroidJUnit4.class)
public class WindowMagnificationGestureHandlerTest {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
public static final int STATE_IDLE = 1;
public static final int STATE_SHOW_MAGNIFIER_SHORTCUT = 2;
public static final int STATE_TWO_FINGERS_DOWN = 3;
public static final int STATE_SHOW_MAGNIFIER_TRIPLE_TAP = 4;
public static final int STATE_NOT_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD = 5;
public static final int STATE_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD = 6;
+ public static final int STATE_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP = 7;
+ public static final int STATE_NOT_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP_AND_HOLD = 8;
+ public static final int STATE_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP_AND_HOLD = 9;
//TODO: Test it after can injecting Handler to GestureMatcher is available.
public static final int FIRST_STATE = STATE_IDLE;
public static final int LAST_STATE = STATE_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD;
+ public static final int LAST_STATE_WITH_MULTI_FINGER =
+ STATE_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP_AND_HOLD;
// Co-prime x and y, to potentially catch x-y-swapped errors
public static final float DEFAULT_TAP_X = 301;
public static final float DEFAULT_TAP_Y = 299;
+ public static final PointF DEFAULT_POINT = new PointF(DEFAULT_TAP_X, DEFAULT_TAP_Y);
private static final int DISPLAY_0 = MockWindowMagnificationConnection.TEST_DISPLAY;
@Rule
@@ -141,7 +154,22 @@ public class WindowMagnificationGestureHandlerTest {
throw new AssertionError("Failed while testing state " + stateToString(state),
e);
}
- });
+ }, LAST_STATE);
+ }
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
+ public void testEachState_enabledMultiFinger_isReachableAndRecoverable() {
+ 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);
+ }
+ }, LAST_STATE_WITH_MULTI_FINGER);
}
@Test
@@ -159,8 +187,29 @@ public class WindowMagnificationGestureHandlerTest {
returnToNormalFrom(state1);
}
}
- });
- });
+ }, LAST_STATE);
+ }, LAST_STATE);
+ }
+
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
+ public void testStates_enabledMultiFinger_areMutuallyExclusive() {
+ forEachState(state1 -> {
+ forEachState(state2 -> {
+ if (state1 < state2) {
+ goFromStateIdleTo(state1);
+ try {
+ assertIn(state2);
+ fail("State " + stateToString(state1) + " also implies state "
+ + stateToString(state2) + stateDump());
+ } catch (AssertionError e) {
+ // expected
+ returnToNormalFrom(state1);
+ }
+ }
+ }, LAST_STATE_WITH_MULTI_FINGER);
+ }, LAST_STATE_WITH_MULTI_FINGER);
}
@Test
@@ -187,8 +236,8 @@ public class WindowMagnificationGestureHandlerTest {
returnToNormalFrom(STATE_SHOW_MAGNIFIER_TRIPLE_TAP);
}
- private void forEachState(IntConsumer action) {
- for (int state = FIRST_STATE; state <= LAST_STATE; state++) {
+ private void forEachState(IntConsumer action, int lastState) {
+ for (int state = FIRST_STATE; state <= lastState; state++) {
action.accept(state);
}
}
@@ -207,14 +256,16 @@ public class WindowMagnificationGestureHandlerTest {
}
break;
case STATE_SHOW_MAGNIFIER_SHORTCUT:
- case STATE_SHOW_MAGNIFIER_TRIPLE_TAP: {
+ case STATE_SHOW_MAGNIFIER_TRIPLE_TAP:
+ case STATE_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP:
check(isWindowMagnifierEnabled(DISPLAY_0), state);
check(mWindowMagnificationGestureHandler.mCurrentState
== mWindowMagnificationGestureHandler.mDetectingState, state);
- }
break;
case STATE_NOT_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD:
- case STATE_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD: {
+ case STATE_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD:
+ case STATE_NOT_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP_AND_HOLD:
+ case STATE_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP_AND_HOLD: {
check(isWindowMagnifierEnabled(DISPLAY_0), state);
check(mWindowMagnificationGestureHandler.mCurrentState
== mWindowMagnificationGestureHandler.mViewportDraggingState, state);
@@ -286,6 +337,29 @@ public class WindowMagnificationGestureHandlerTest {
tapAndHold();
}
break;
+ case STATE_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP: {
+ twoFingerTap();
+ twoFingerTap();
+ twoFingerTap();
+ // Wait for two-finger tap gesture completed.
+ SystemClock.sleep(ViewConfiguration.getDoubleTapMinTime() + 500);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+ break;
+ case STATE_NOT_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP_AND_HOLD: {
+ twoFingerTap();
+ twoFingerTap();
+ twoFingerTapAndHold();
+ }
+ break;
+ case STATE_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP_AND_HOLD: {
+ // enabled then perform two finger triple tap and hold gesture
+ goFromStateIdleTo(STATE_SHOW_MAGNIFIER_SHORTCUT);
+ twoFingerTap();
+ twoFingerTap();
+ twoFingerTapAndHold();
+ }
+ break;
default:
throw new IllegalArgumentException("Illegal state: " + state);
}
@@ -319,13 +393,22 @@ public class WindowMagnificationGestureHandlerTest {
tap();
}
break;
- case STATE_NOT_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD: {
+ case STATE_NOT_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD:
+ case STATE_NOT_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP_AND_HOLD:
send(upEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y));
- }
- break;
- case STATE_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD: {
+ break;
+ case STATE_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD:
+ case STATE_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP_AND_HOLD:
send(upEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y));
returnToNormalFrom(STATE_SHOW_MAGNIFIER_SHORTCUT);
+ break;
+ case STATE_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP: {
+ twoFingerTap();
+ twoFingerTap();
+ twoFingerTap();
+ // Wait for two-finger tap gesture completed.
+ SystemClock.sleep(ViewConfiguration.getDoubleTapMinTime() + 500);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
break;
default:
@@ -365,6 +448,16 @@ public class WindowMagnificationGestureHandlerTest {
return TouchEventGenerator.downEvent(DISPLAY_0, x, y);
}
+ private MotionEvent pointerDownEvent(float x, float y) {
+ return TouchEventGenerator.pointerDownEvent(DISPLAY_0,
+ new PointF[] {DEFAULT_POINT, new PointF(x, y)});
+ }
+
+ private MotionEvent pointerUpEvent(float x, float y) {
+ return TouchEventGenerator.pointerUpEvent(DISPLAY_0,
+ new PointF[] {DEFAULT_POINT, new PointF(x, y)});
+ }
+
private MotionEvent upEvent(float x, float y) {
return TouchEventGenerator.upEvent(DISPLAY_0, x, y);
}
@@ -379,6 +472,19 @@ public class WindowMagnificationGestureHandlerTest {
SystemClock.sleep(ViewConfiguration.getLongPressTimeout() + 100);
}
+ private void twoFingerTap() {
+ send(downEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y));
+ send(pointerDownEvent(DEFAULT_TAP_X * 2, DEFAULT_TAP_Y));
+ send(pointerUpEvent(DEFAULT_TAP_X * 2, DEFAULT_TAP_Y));
+ send(upEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y));
+ }
+
+ private void twoFingerTapAndHold() {
+ send(downEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y));
+ send(pointerDownEvent(DEFAULT_TAP_X * 2, DEFAULT_TAP_Y));
+ SystemClock.sleep(ViewConfiguration.getLongPressTimeout() + 100);
+ }
+
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 fbcde533aa9f..fcd16a09457e 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
@@ -39,10 +39,36 @@ public class TouchEventGenerator {
return generateSingleTouchEvent(displayId, ACTION_DOWN, x, y);
}
+ /**
+ * Create a test {@link MotionEvent#ACTION_POINTER_DOWN}, filling in all the basic values that
+ * define the motion.
+ *
+ * @param displayId The id of the display
+ * @param pointFs location on the screen of the all pointers
+ */
+ public static MotionEvent pointerDownEvent(int displayId, PointF[] pointFs) {
+ final int actionIndex = 1 << MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+ final int action = ACTION_POINTER_DOWN | actionIndex;
+ return generateMultiplePointersEvent(displayId, action, pointFs);
+ }
+
public static MotionEvent moveEvent(int displayId, float x, float y) {
return generateSingleTouchEvent(displayId, ACTION_MOVE, x, y);
}
+ /**
+ * Create a test {@link MotionEvent#ACTION_POINTER_UP}, filling in all the basic values that
+ * define the motion.
+ *
+ * @param displayId the id of the display
+ * @param pointFs location on the screen of the all pointers
+ */
+ public static MotionEvent pointerUpEvent(int displayId, PointF[] pointFs) {
+ final int actionIndex = 1 << MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+ final int action = ACTION_POINTER_UP | actionIndex;
+ return generateMultiplePointersEvent(displayId, action, pointFs);
+ }
+
public static MotionEvent upEvent(int displayId, float x, float y) {
return generateSingleTouchEvent(displayId, ACTION_UP, x, y);
}