1 /** 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 * express or implied. See the License for the specific language governing permissions and 12 * limitations under the License. 13 */ 14 15 package android.accessibilityservice.cts; 16 17 import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityOnSpecifiedDisplayAndWaitForItToBeOnscreen; 18 import static android.accessibilityservice.cts.utils.DisplayUtils.VirtualDisplaySession; 19 import static android.accessibilityservice.cts.utils.GestureUtils.add; 20 import static android.accessibilityservice.cts.utils.GestureUtils.click; 21 import static android.accessibilityservice.cts.utils.GestureUtils.diff; 22 import static android.accessibilityservice.cts.utils.GestureUtils.endTimeOf; 23 import static android.accessibilityservice.cts.utils.GestureUtils.getGestureBuilder; 24 import static android.accessibilityservice.cts.utils.GestureUtils.longClick; 25 import static android.accessibilityservice.cts.utils.GestureUtils.startingAt; 26 import static android.app.UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES; 27 28 import static org.junit.Assume.assumeTrue; 29 import static org.mockito.Mockito.any; 30 import static org.mockito.Mockito.timeout; 31 import static org.mockito.Mockito.verify; 32 33 import android.accessibility.cts.common.AccessibilityDumpOnFailureRule; 34 import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule; 35 import android.accessibilityservice.AccessibilityService; 36 import android.accessibilityservice.GestureDescription; 37 import android.accessibilityservice.GestureDescription.StrokeDescription; 38 import android.accessibilityservice.cts.activities.AccessibilityWindowQueryActivity; 39 import android.accessibilityservice.cts.utils.GestureUtils; 40 import android.app.Activity; 41 import android.app.Instrumentation; 42 import android.app.UiAutomation; 43 import android.content.Context; 44 import android.content.pm.PackageManager; 45 import android.graphics.Path; 46 import android.graphics.Point; 47 import android.graphics.PointF; 48 import android.platform.test.annotations.AppModeFull; 49 import android.util.DisplayMetrics; 50 import android.view.Display; 51 import android.view.WindowManager; 52 import android.view.accessibility.AccessibilityEvent; 53 54 import androidx.test.InstrumentationRegistry; 55 import androidx.test.runner.AndroidJUnit4; 56 57 import org.junit.AfterClass; 58 import org.junit.Before; 59 import org.junit.BeforeClass; 60 import org.junit.Rule; 61 import org.junit.Test; 62 import org.junit.rules.RuleChain; 63 import org.junit.runner.RunWith; 64 import org.mockito.Mock; 65 import org.mockito.MockitoAnnotations; 66 67 /** Verify that motion events are recognized as accessibility gestures. */ 68 @RunWith(AndroidJUnit4.class) 69 public class AccessibilityGestureDetectorTest { 70 71 // Constants 72 private static final float GESTURE_LENGTH_INCHES = 1.0f; 73 // The movement should exceed the threshold 1 cm in 150 ms defined in Swipe.java. It means the 74 // swipe velocity in testing should be greater than 2.54 cm / 381 ms. Therefore the 75 // duration should be smaller than 381. 76 private static final long STROKE_MS = 380; 77 private static final long GESTURE_DISPATCH_TIMEOUT_MS = 3000; 78 private static final long EVENT_DISPATCH_TIMEOUT_MS = 3000; 79 private static final PointF FINGER_OFFSET_PX = new PointF(100f, -50f); 80 81 private static Instrumentation sInstrumentation; 82 private static UiAutomation sUiAutomation; 83 84 private InstrumentedAccessibilityServiceTestRule<GestureDetectionStubAccessibilityService> 85 mServiceRule = new InstrumentedAccessibilityServiceTestRule<>( 86 GestureDetectionStubAccessibilityService.class, false); 87 88 private AccessibilityDumpOnFailureRule mDumpOnFailureRule = 89 new AccessibilityDumpOnFailureRule(); 90 91 @Rule 92 public final RuleChain mRuleChain = RuleChain 93 .outerRule(mServiceRule) 94 .around(mDumpOnFailureRule); 95 96 // Test AccessibilityService that collects gestures. 97 GestureDetectionStubAccessibilityService mService; 98 boolean mHasTouchScreen; 99 boolean mScreenBigEnough; 100 int mStrokeLenPxX; // Gesture stroke size, in pixels 101 int mStrokeLenPxY; 102 Point mCenter; // Center of screen. Gestures all start from this point. 103 PointF mTapLocation; 104 @Mock AccessibilityService.GestureResultCallback mGestureDispatchCallback; 105 106 @BeforeClass oneTimeSetup()107 public static void oneTimeSetup() { 108 sInstrumentation = InstrumentationRegistry.getInstrumentation(); 109 sUiAutomation = sInstrumentation.getUiAutomation(FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES); 110 } 111 112 @AfterClass finalTearDown()113 public static void finalTearDown() { 114 sUiAutomation.destroy(); 115 } 116 117 @Before setUp()118 public void setUp() throws Exception { 119 MockitoAnnotations.initMocks(this); 120 121 // Check that device has a touch screen. 122 PackageManager pm = sInstrumentation.getContext().getPackageManager(); 123 mHasTouchScreen = pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN) 124 || pm.hasSystemFeature(PackageManager.FEATURE_FAKETOUCH); 125 if (!mHasTouchScreen) { 126 return; 127 } 128 129 // Find screen size, check that it is big enough for gestures. 130 // Gestures will start in the center of the screen, so we need enough horiz/vert space. 131 WindowManager windowManager = (WindowManager) sInstrumentation.getContext() 132 .getSystemService(Context.WINDOW_SERVICE); 133 final DisplayMetrics metrics = new DisplayMetrics(); 134 windowManager.getDefaultDisplay().getRealMetrics(metrics); 135 mCenter = new Point((int) metrics.widthPixels / 2, (int) metrics.heightPixels / 2); 136 mTapLocation = new PointF(mCenter); 137 mStrokeLenPxX = (int) (GESTURE_LENGTH_INCHES * metrics.xdpi); 138 // The threshold is determined by xdpi. 139 mStrokeLenPxY = mStrokeLenPxX; 140 final boolean screenWideEnough = metrics.widthPixels / 2 > mStrokeLenPxX; 141 final boolean screenHighEnough = metrics.heightPixels / 2 > mStrokeLenPxY; 142 mScreenBigEnough = screenWideEnough && screenHighEnough; 143 if (!mScreenBigEnough) { 144 return; 145 } 146 // Start stub accessibility service. 147 mService = mServiceRule.enableService(); 148 } 149 150 @Test 151 @AppModeFull testRecognizeGesturePath()152 public void testRecognizeGesturePath() { 153 if (!mHasTouchScreen || !mScreenBigEnough) { 154 return; 155 } 156 157 runGestureDetectionTestOnDisplay(Display.DEFAULT_DISPLAY); 158 runMultiFingerGestureDetectionTestOnDisplay(Display.DEFAULT_DISPLAY); 159 } 160 161 @Test 162 @AppModeFull testRecognizeGesturePathOnVirtualDisplay()163 public void testRecognizeGesturePathOnVirtualDisplay() throws Exception { 164 assumeTrue(sInstrumentation.getContext().getPackageManager() 165 .hasSystemFeature(PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS)); 166 167 if (!mHasTouchScreen || !mScreenBigEnough) { 168 return; 169 } 170 171 try (final VirtualDisplaySession displaySession = new VirtualDisplaySession()) { 172 final int displayId = displaySession.createDisplayWithDefaultDisplayMetricsAndWait( 173 sInstrumentation.getTargetContext(), false).getDisplayId(); 174 // Launches an activity on virtual display to meet a real situation. 175 final Activity activity = launchActivityOnSpecifiedDisplayAndWaitForItToBeOnscreen( 176 sInstrumentation, sUiAutomation, AccessibilityWindowQueryActivity.class, 177 displayId); 178 179 try { 180 runGestureDetectionTestOnDisplay(displayId); 181 runMultiFingerGestureDetectionTestOnDisplay(displayId); 182 } finally { 183 sInstrumentation.runOnMainSync(() -> { 184 activity.finish(); 185 }); 186 sInstrumentation.waitForIdleSync(); 187 } 188 } 189 } 190 runGestureDetectionTestOnDisplay(int displayId)191 private void runGestureDetectionTestOnDisplay(int displayId) { 192 // Compute gesture stroke lengths, in pixels. 193 final int dx = mStrokeLenPxX; 194 final int dy = mStrokeLenPxY; 195 196 // Test recognizing various gestures. 197 testGesture( 198 doubleTap(displayId), 199 AccessibilityService.GESTURE_DOUBLE_TAP, 200 displayId); 201 testGesture( 202 doubleTapAndHold(displayId), 203 AccessibilityService.GESTURE_DOUBLE_TAP_AND_HOLD, 204 displayId); 205 testPath(p(-dx, +0), AccessibilityService.GESTURE_SWIPE_LEFT, displayId); 206 testPath(p(+dx, +0), AccessibilityService.GESTURE_SWIPE_RIGHT, displayId); 207 testPath(p(+0, -dy), AccessibilityService.GESTURE_SWIPE_UP, displayId); 208 testPath(p(+0, +dy), AccessibilityService.GESTURE_SWIPE_DOWN, displayId); 209 210 testPath(p(-dx, +0), p(+0, +0), AccessibilityService.GESTURE_SWIPE_LEFT_AND_RIGHT, 211 displayId); 212 testPath(p(-dx, +0), p(-dx, -dy), AccessibilityService.GESTURE_SWIPE_LEFT_AND_UP, 213 displayId); 214 testPath(p(-dx, +0), p(-dx, +dy), AccessibilityService.GESTURE_SWIPE_LEFT_AND_DOWN, 215 displayId); 216 217 testPath(p(+dx, +0), p(+0, +0), AccessibilityService.GESTURE_SWIPE_RIGHT_AND_LEFT, 218 displayId); 219 testPath(p(+dx, +0), p(+dx, -dy), AccessibilityService.GESTURE_SWIPE_RIGHT_AND_UP, 220 displayId); 221 testPath(p(+dx, +0), p(+dx, +dy), AccessibilityService.GESTURE_SWIPE_RIGHT_AND_DOWN, 222 displayId); 223 224 testPath(p(+0, -dy), p(-dx, -dy), AccessibilityService.GESTURE_SWIPE_UP_AND_LEFT, 225 displayId); 226 testPath(p(+0, -dy), p(+dx, -dy), AccessibilityService.GESTURE_SWIPE_UP_AND_RIGHT, 227 displayId); 228 testPath(p(+0, -dy), p(+0, +0), AccessibilityService.GESTURE_SWIPE_UP_AND_DOWN, 229 displayId); 230 231 testPath(p(+0, +dy), p(-dx, +dy), AccessibilityService.GESTURE_SWIPE_DOWN_AND_LEFT, 232 displayId); 233 testPath(p(+0, +dy), p(+dx, +dy), AccessibilityService.GESTURE_SWIPE_DOWN_AND_RIGHT, 234 displayId); 235 testPath(p(+0, +dy), p(+0, +0), AccessibilityService.GESTURE_SWIPE_DOWN_AND_UP, 236 displayId); 237 } 238 runMultiFingerGestureDetectionTestOnDisplay(int displayId)239 private void runMultiFingerGestureDetectionTestOnDisplay(int displayId) { 240 // Compute gesture stroke lengths, in pixels. 241 final int dx = mStrokeLenPxX; 242 final int dy = mStrokeLenPxY; 243 testGesture( 244 twoFingerSingleTap(displayId), 245 AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP, 246 displayId); 247 testGesture( 248 twoFingerTripleTapAndHold(displayId), 249 AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP_AND_HOLD, 250 displayId); 251 testGesture( 252 twoFingerDoubleTap(displayId), 253 AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP, 254 displayId); 255 testGesture( 256 twoFingerDoubleTapAndHold(displayId), 257 AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD, 258 displayId); 259 testGesture( 260 twoFingerTripleTap(displayId), 261 AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP, 262 displayId); 263 264 testGesture( 265 threeFingerSingleTap(displayId), 266 AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP, 267 displayId); 268 testGesture( 269 threeFingerSingleTapAndHold(displayId), 270 AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD, 271 displayId); 272 testGesture( 273 threeFingerDoubleTap(displayId), 274 AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP, 275 displayId); 276 testGesture( 277 threeFingerDoubleTapAndHold(displayId), 278 AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD, 279 displayId); 280 testGesture( 281 threeFingerTripleTap(displayId), 282 AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP, 283 displayId); 284 testGesture( 285 threeFingerTripleTapAndHold(displayId), 286 AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP_AND_HOLD, 287 displayId); 288 289 testGesture( 290 fourFingerSingleTap(displayId), 291 AccessibilityService.GESTURE_4_FINGER_SINGLE_TAP, 292 displayId); 293 testGesture( 294 fourFingerDoubleTap(displayId), 295 AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP, 296 displayId); 297 testGesture( 298 fourFingerDoubleTapAndHold(displayId), 299 AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD, 300 displayId); 301 testGesture( 302 fourFingerTripleTap(displayId), 303 AccessibilityService.GESTURE_4_FINGER_TRIPLE_TAP, 304 displayId); 305 306 testGesture( 307 MultiFingerSwipe(displayId, 3, 0, dy), 308 AccessibilityService.GESTURE_3_FINGER_SWIPE_DOWN, 309 displayId); 310 testGesture( 311 MultiFingerSwipe(displayId, 3, -dx, 0), 312 AccessibilityService.GESTURE_3_FINGER_SWIPE_LEFT, 313 displayId); 314 testGesture( 315 MultiFingerSwipe(displayId, 3, dx, 0), 316 AccessibilityService.GESTURE_3_FINGER_SWIPE_RIGHT, 317 displayId); 318 testGesture( 319 MultiFingerSwipe(displayId, 3, 0, -dy), 320 AccessibilityService.GESTURE_3_FINGER_SWIPE_UP, 321 displayId); 322 testGesture( 323 MultiFingerSwipe(displayId, 4, 0, dy), 324 AccessibilityService.GESTURE_4_FINGER_SWIPE_DOWN, 325 displayId); 326 testGesture( 327 MultiFingerSwipe(displayId, 4, -dx, 0), 328 AccessibilityService.GESTURE_4_FINGER_SWIPE_LEFT, 329 displayId); 330 testGesture( 331 MultiFingerSwipe(displayId, 4, dx, 0), 332 AccessibilityService.GESTURE_4_FINGER_SWIPE_RIGHT, 333 displayId); 334 testGesture( 335 MultiFingerSwipe(displayId, 4, 0, -dy), 336 AccessibilityService.GESTURE_4_FINGER_SWIPE_UP, 337 displayId); 338 } 339 340 /** Convenient short alias to make a Point. */ p(int x, int y)341 private static Point p(int x, int y) { 342 return new Point(x, y); 343 } 344 345 /** Test recognizing path from PATH_START to PATH_START+delta on default display. */ testPath(Point delta, int gestureId)346 private void testPath(Point delta, int gestureId) { 347 testPath(delta, null, gestureId, Display.DEFAULT_DISPLAY); 348 } 349 350 /** Test recognizing path from PATH_START to PATH_START+delta on specified display. */ testPath(Point delta, int gestureId, int displayId)351 private void testPath(Point delta, int gestureId, int displayId) { 352 testPath(delta, null, gestureId, displayId); 353 } 354 /** Test recognizing path from PATH_START to PATH_START+delta on default display. */ testPath(Point delta1, Point delta2, int gestureId)355 private void testPath(Point delta1, Point delta2, int gestureId) { 356 testPath(delta1, delta2, gestureId, Display.DEFAULT_DISPLAY); 357 } 358 359 /** 360 * Test recognizing path from PATH_START to PATH_START+delta1 to PATH_START+delta2. on specified 361 * display. 362 */ testPath(Point delta1, Point delta2, int gestureId, int displayId)363 private void testPath(Point delta1, Point delta2, int gestureId, int displayId) { 364 // Create gesture motions. 365 int numPathSegments = (delta2 == null) ? 1 : 2; 366 long pathDurationMs = numPathSegments * STROKE_MS; 367 GestureDescription gesture = new GestureDescription.Builder() 368 .addStroke(new StrokeDescription( 369 linePath(mCenter, delta1, delta2), 0, pathDurationMs, false)) 370 .setDisplayId(displayId) 371 .build(); 372 373 testGesture(gesture, gestureId, displayId); 374 } 375 376 /** Dispatch a gesture and make sure it is detected as the specified gesture id. */ testGesture(GestureDescription gesture, int gestureId, int displayId)377 private void testGesture(GestureDescription gesture, int gestureId, int displayId) { 378 // Dispatch gesture motions to specified display with GestureDescription.. 379 // Use AccessibilityService.dispatchGesture() instead of Instrumentation.sendPointerSync() 380 // because accessibility services read gesture events upstream from the point where 381 // sendPointerSync() injects events. 382 mService.runOnServiceSync(() -> 383 mService.dispatchGesture(gesture, mGestureDispatchCallback, null)); 384 verify(mGestureDispatchCallback, timeout(GESTURE_DISPATCH_TIMEOUT_MS).atLeastOnce()) 385 .onCompleted(any()); 386 387 // Wait for gesture recognizer, and check recognized gesture. 388 mService.assertGestureReceived(gestureId, displayId); 389 } 390 /** Create a path from startPoint, moving by delta1, then delta2. (delta2 may be null.) */ linePath(Point startPoint, Point delta1, Point delta2)391 Path linePath(Point startPoint, Point delta1, Point delta2) { 392 Path path = new Path(); 393 path.moveTo(startPoint.x, startPoint.y); 394 path.lineTo(startPoint.x + delta1.x, startPoint.y + delta1.y); 395 if (delta2 != null) { 396 path.lineTo(startPoint.x + delta2.x, startPoint.y + delta2.y); 397 } 398 return path; 399 } 400 401 @Test 402 @AppModeFull testVerifyGestureTouchEvent()403 public void testVerifyGestureTouchEvent() { 404 if (!mHasTouchScreen || !mScreenBigEnough) { 405 return; 406 } 407 408 verifyGestureTouchEventOnDisplay(Display.DEFAULT_DISPLAY); 409 verifyMultiFingerGestureTouchEventOnDisplay(Display.DEFAULT_DISPLAY); 410 } 411 412 @Test 413 @AppModeFull testVerifyGestureTouchEventOnVirtualDisplay()414 public void testVerifyGestureTouchEventOnVirtualDisplay() throws Exception { 415 assumeTrue(sInstrumentation.getContext().getPackageManager() 416 .hasSystemFeature(PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS)); 417 if (!mHasTouchScreen || !mScreenBigEnough) { 418 return; 419 } 420 421 try (final VirtualDisplaySession displaySession = new VirtualDisplaySession()) { 422 final int displayId = displaySession.createDisplayWithDefaultDisplayMetricsAndWait( 423 sInstrumentation.getTargetContext(), 424 false).getDisplayId(); 425 426 // Launches an activity on virtual display to meet a real situation. 427 final Activity activity = launchActivityOnSpecifiedDisplayAndWaitForItToBeOnscreen( 428 sInstrumentation, sUiAutomation, AccessibilityWindowQueryActivity.class, 429 displayId); 430 try { 431 verifyGestureTouchEventOnDisplay(displayId); 432 verifyMultiFingerGestureTouchEventOnDisplay(displayId); 433 } finally { 434 sInstrumentation.runOnMainSync(() -> { 435 activity.finish(); 436 }); 437 sInstrumentation.waitForIdleSync(); 438 } 439 } 440 } 441 verifyGestureTouchEventOnDisplay(int displayId)442 private void verifyGestureTouchEventOnDisplay(int displayId) { 443 assertEventAfterGesture(swipe(displayId), 444 AccessibilityEvent.TYPE_TOUCH_INTERACTION_START, 445 AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); 446 447 assertEventAfterGesture(tap(displayId), 448 AccessibilityEvent.TYPE_TOUCH_INTERACTION_START, 449 AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START, 450 AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END, 451 AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); 452 453 assertEventAfterGesture(doubleTap(displayId), 454 AccessibilityEvent.TYPE_TOUCH_INTERACTION_START, 455 AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); 456 457 assertEventAfterGesture(doubleTapAndHold(displayId), 458 AccessibilityEvent.TYPE_TOUCH_INTERACTION_START, 459 AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); 460 } 461 verifyMultiFingerGestureTouchEventOnDisplay(int displayId)462 private void verifyMultiFingerGestureTouchEventOnDisplay(int displayId) { 463 assertEventAfterGesture(twoFingerSingleTap(displayId), 464 AccessibilityEvent.TYPE_TOUCH_INTERACTION_START, 465 AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); 466 assertEventAfterGesture(twoFingerDoubleTap(displayId), 467 AccessibilityEvent.TYPE_TOUCH_INTERACTION_START, 468 AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); 469 assertEventAfterGesture(twoFingerTripleTap(displayId), 470 AccessibilityEvent.TYPE_TOUCH_INTERACTION_START, 471 AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); 472 473 assertEventAfterGesture(threeFingerSingleTap(displayId), 474 AccessibilityEvent.TYPE_TOUCH_INTERACTION_START, 475 AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); 476 assertEventAfterGesture(threeFingerDoubleTap(displayId), 477 AccessibilityEvent.TYPE_TOUCH_INTERACTION_START, 478 AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); 479 assertEventAfterGesture(threeFingerTripleTap(displayId), 480 AccessibilityEvent.TYPE_TOUCH_INTERACTION_START, 481 AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); 482 } 483 484 @Test 485 @AppModeFull testDispatchGesture_privateDisplay_gestureCancelled()486 public void testDispatchGesture_privateDisplay_gestureCancelled() throws Exception{ 487 assumeTrue(sInstrumentation.getContext().getPackageManager() 488 .hasSystemFeature(PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS)); 489 if (!mHasTouchScreen || !mScreenBigEnough) { 490 return; 491 } 492 493 try (final VirtualDisplaySession displaySession = new VirtualDisplaySession()) { 494 final int displayId = displaySession.createDisplayWithDefaultDisplayMetricsAndWait 495 (sInstrumentation.getTargetContext(), 496 true).getDisplayId(); 497 GestureDescription gesture = swipe(displayId); 498 mService.clearGestures(); 499 mService.runOnServiceSync(() -> 500 mService.dispatchGesture(gesture, mGestureDispatchCallback, null)); 501 verify(mGestureDispatchCallback, timeout(GESTURE_DISPATCH_TIMEOUT_MS).atLeastOnce()) 502 .onCancelled(any()); 503 } 504 } 505 506 /** Test touch for accessibility events */ assertEventAfterGesture(GestureDescription gesture, int... events)507 private void assertEventAfterGesture(GestureDescription gesture, int... events) { 508 mService.clearEvents(); 509 mService.runOnServiceSync( 510 () -> mService.dispatchGesture(gesture, mGestureDispatchCallback, null)); 511 verify(mGestureDispatchCallback, timeout(EVENT_DISPATCH_TIMEOUT_MS).atLeastOnce()) 512 .onCompleted(any()); 513 514 mService.assertPropagated(events); 515 } 516 swipe(int displayId)517 private GestureDescription swipe(int displayId) { 518 StrokeDescription swipe = new StrokeDescription( 519 linePath(mCenter, p(0, mStrokeLenPxY), null), 0, STROKE_MS, false); 520 return getGestureBuilder(displayId, swipe).build(); 521 } 522 tap(int displayId)523 private GestureDescription tap(int displayId) { 524 StrokeDescription tap = click(mTapLocation); 525 return getGestureBuilder(displayId, tap).build(); 526 } 527 doubleTap(int displayId)528 private GestureDescription doubleTap(int displayId) { 529 StrokeDescription tap1 = click(mTapLocation); 530 StrokeDescription tap2 = startingAt(endTimeOf(tap1) + 20, click(mTapLocation)); 531 return getGestureBuilder(displayId, tap1, tap2).build(); 532 } 533 doubleTapAndHold(int displayId)534 private GestureDescription doubleTapAndHold(int displayId) { 535 StrokeDescription tap1 = click(mTapLocation); 536 StrokeDescription tap2 = startingAt(endTimeOf(tap1) + 20, longClick(mTapLocation)); 537 return getGestureBuilder(displayId, tap1, tap2).build(); 538 } 539 twoFingerSingleTap(int displayId)540 private GestureDescription twoFingerSingleTap(int displayId) { 541 return multiFingerMultiTap(2, 1, displayId); 542 } 543 twoFingerTripleTapAndHold(int displayId)544 private GestureDescription twoFingerTripleTapAndHold(int displayId) { 545 return multiFingerMultiTapAndHold(2, 3, displayId); 546 } 547 twoFingerDoubleTap(int displayId)548 private GestureDescription twoFingerDoubleTap(int displayId) { 549 return multiFingerMultiTap(2, 2, displayId); 550 } 551 twoFingerDoubleTapAndHold(int displayId)552 private GestureDescription twoFingerDoubleTapAndHold(int displayId) { 553 return multiFingerMultiTapAndHold(2, 2, displayId); 554 } 555 twoFingerTripleTap(int displayId)556 private GestureDescription twoFingerTripleTap(int displayId) { 557 return multiFingerMultiTap(2, 3, displayId); 558 } 559 threeFingerSingleTap(int displayId)560 private GestureDescription threeFingerSingleTap(int displayId) { 561 return multiFingerMultiTap(3, 1, displayId); 562 } 563 threeFingerSingleTapAndHold(int displayId)564 private GestureDescription threeFingerSingleTapAndHold(int displayId) { 565 return multiFingerMultiTapAndHold(3, 1, displayId); 566 } 567 threeFingerDoubleTap(int displayId)568 private GestureDescription threeFingerDoubleTap(int displayId) { 569 return multiFingerMultiTap(3, 2, displayId); 570 } 571 threeFingerDoubleTapAndHold(int displayId)572 private GestureDescription threeFingerDoubleTapAndHold(int displayId) { 573 return multiFingerMultiTapAndHold(3, 2, displayId); 574 } 575 threeFingerTripleTap(int displayId)576 private GestureDescription threeFingerTripleTap(int displayId) { 577 return multiFingerMultiTap(3, 3, displayId); 578 } 579 threeFingerTripleTapAndHold(int displayId)580 private GestureDescription threeFingerTripleTapAndHold(int displayId) { 581 return multiFingerMultiTapAndHold(3, 3, displayId); 582 } 583 fourFingerSingleTap(int displayId)584 private GestureDescription fourFingerSingleTap(int displayId) { 585 return multiFingerMultiTap(4, 1, displayId); 586 } 587 fourFingerDoubleTap(int displayId)588 private GestureDescription fourFingerDoubleTap(int displayId) { 589 return multiFingerMultiTap(4, 2, displayId); 590 } 591 fourFingerDoubleTapAndHold(int displayId)592 private GestureDescription fourFingerDoubleTapAndHold(int displayId) { 593 return multiFingerMultiTapAndHold(4, 2, displayId); 594 } 595 fourFingerTripleTap(int displayId)596 private GestureDescription fourFingerTripleTap(int displayId) { 597 return multiFingerMultiTap(4, 3, displayId); 598 } 599 multiFingerMultiTap(int fingerCount, int tapCount, int displayId)600 private GestureDescription multiFingerMultiTap(int fingerCount, int tapCount, int displayId) { 601 // We dispatch the first finger, base, placed at left down side by an offset 602 // from the center of the display and the rest ones at right up side by delta 603 // from the base. 604 final PointF base = diff(mTapLocation, FINGER_OFFSET_PX); 605 return GestureUtils.multiFingerMultiTap( 606 base, FINGER_OFFSET_PX, fingerCount, tapCount, /* slop= */ 0, displayId); 607 } 608 multiFingerMultiTapAndHold( int fingerCount, int tapCount, int displayId)609 private GestureDescription multiFingerMultiTapAndHold( 610 int fingerCount, int tapCount, int displayId) { 611 // We dispatch the first finger, base, placed at left down side by an offset 612 // from the center of the display and the rest ones at right up side by delta 613 // from the base. 614 final PointF base = diff(mTapLocation, FINGER_OFFSET_PX); 615 return GestureUtils.multiFingerMultiTapAndHold( 616 base, FINGER_OFFSET_PX, fingerCount, tapCount, /* slop= */ 0, displayId); 617 } 618 MultiFingerSwipe( int displayId, int fingerCount, float dx, float dy)619 private GestureDescription MultiFingerSwipe( 620 int displayId, int fingerCount, float dx, float dy) { 621 float fingerOffset = 10f; 622 GestureDescription.Builder builder = new GestureDescription.Builder(); 623 builder.setDisplayId(displayId); 624 for (int currentFinger = 0; currentFinger < fingerCount; ++currentFinger) { 625 builder.addStroke( 626 GestureUtils.swipe( 627 add(mTapLocation, fingerOffset * currentFinger, 0), 628 add(mTapLocation, dx + (fingerOffset * currentFinger), dy), 629 STROKE_MS)); 630 } 631 return builder.build(); 632 } 633 } 634