1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.accessibility; 18 19 import static android.view.MotionEvent.ACTION_DOWN; 20 import static android.view.MotionEvent.ACTION_UP; 21 import static android.view.WindowManagerPolicyConstants.FLAG_PASS_TO_USER; 22 import static org.hamcrest.CoreMatchers.allOf; 23 import static org.hamcrest.CoreMatchers.anyOf; 24 import static org.hamcrest.CoreMatchers.everyItem; 25 import static org.hamcrest.MatcherAssert.assertThat; 26 import static org.junit.Assert.assertEquals; 27 import static org.junit.Assert.assertFalse; 28 import static org.junit.Assert.assertTrue; 29 import static org.mockito.Matchers.anyBoolean; 30 import static org.mockito.Matchers.anyInt; 31 import static org.mockito.Matchers.eq; 32 import static org.mockito.Mockito.mock; 33 import static org.mockito.Mockito.reset; 34 import static org.mockito.Mockito.times; 35 import static org.mockito.Mockito.verify; 36 import static org.mockito.Mockito.verifyNoMoreInteractions; 37 import static org.mockito.Mockito.verifyZeroInteractions; 38 import static org.mockito.hamcrest.MockitoHamcrest.argThat; 39 40 import android.accessibilityservice.GestureDescription.GestureStep; 41 import android.accessibilityservice.GestureDescription.TouchPoint; 42 import android.accessibilityservice.IAccessibilityServiceClient; 43 import android.graphics.Point; 44 import android.os.Handler; 45 import android.os.Looper; 46 import android.os.Message; 47 import android.os.RemoteException; 48 import android.support.test.runner.AndroidJUnit4; 49 import android.util.Log; 50 import android.util.Pair; 51 import android.view.InputDevice; 52 import android.view.KeyEvent; 53 import android.view.MotionEvent; 54 55 import java.util.ArrayList; 56 import java.util.Arrays; 57 import java.util.List; 58 59 import android.view.accessibility.AccessibilityEvent; 60 import org.hamcrest.Description; 61 import org.hamcrest.Matcher; 62 import org.hamcrest.TypeSafeMatcher; 63 import org.junit.Before; 64 import org.junit.BeforeClass; 65 import org.junit.Test; 66 import org.junit.runner.RunWith; 67 import org.mockito.ArgumentCaptor; 68 69 /** 70 * Tests for MotionEventInjector 71 */ 72 @RunWith(AndroidJUnit4.class) 73 public class MotionEventInjectorTest { 74 private static final String LOG_TAG = "MotionEventInjectorTest"; 75 private static final Matcher<MotionEvent> IS_ACTION_DOWN = 76 new MotionEventActionMatcher(ACTION_DOWN); 77 private static final Matcher<MotionEvent> IS_ACTION_POINTER_DOWN = 78 new MotionEventActionMatcher(MotionEvent.ACTION_POINTER_DOWN); 79 private static final Matcher<MotionEvent> IS_ACTION_UP = 80 new MotionEventActionMatcher(ACTION_UP); 81 private static final Matcher<MotionEvent> IS_ACTION_POINTER_UP = 82 new MotionEventActionMatcher(MotionEvent.ACTION_POINTER_UP); 83 private static final Matcher<MotionEvent> IS_ACTION_CANCEL = 84 new MotionEventActionMatcher(MotionEvent.ACTION_CANCEL); 85 private static final Matcher<MotionEvent> IS_ACTION_MOVE = 86 new MotionEventActionMatcher(MotionEvent.ACTION_MOVE); 87 88 private static final Point LINE_START = new Point(100, 200); 89 private static final Point LINE_END = new Point(100, 300); 90 private static final int LINE_DURATION = 100; 91 private static final int LINE_SEQUENCE = 50; 92 93 private static final Point CLICK_POINT = new Point(1000, 2000); 94 private static final int CLICK_DURATION = 10; 95 private static final int CLICK_SEQUENCE = 51; 96 97 private static final int MOTION_EVENT_SOURCE = InputDevice.SOURCE_TOUCHSCREEN; 98 private static final int OTHER_EVENT_SOURCE = InputDevice.SOURCE_MOUSE; 99 100 private static final Point CONTINUED_LINE_START = new Point(500, 300); 101 private static final Point CONTINUED_LINE_MID1 = new Point(500, 400); 102 private static final Point CONTINUED_LINE_MID2 = new Point(600, 300); 103 private static final Point CONTINUED_LINE_END = new Point(600, 400); 104 private static final int CONTINUED_LINE_STROKE_ID_1 = 100; 105 private static final int CONTINUED_LINE_STROKE_ID_2 = 101; 106 private static final int CONTINUED_LINE_INTERVAL = 100; 107 private static final int CONTINUED_LINE_SEQUENCE_1 = 52; 108 private static final int CONTINUED_LINE_SEQUENCE_2 = 53; 109 110 MotionEventInjector mMotionEventInjector; 111 IAccessibilityServiceClient mServiceInterface; 112 List<GestureStep> mLineList = new ArrayList<>(); 113 List<GestureStep> mClickList = new ArrayList<>(); 114 List<GestureStep> mContinuedLineList1 = new ArrayList<>(); 115 List<GestureStep> mContinuedLineList2 = new ArrayList<>(); 116 117 MotionEvent mClickDownEvent; 118 MotionEvent mClickUpEvent; 119 120 ArgumentCaptor<MotionEvent> mCaptor1 = ArgumentCaptor.forClass(MotionEvent.class); 121 ArgumentCaptor<MotionEvent> mCaptor2 = ArgumentCaptor.forClass(MotionEvent.class); 122 MessageCapturingHandler mMessageCapturingHandler; 123 Matcher<MotionEvent> mIsLineStart; 124 Matcher<MotionEvent> mIsLineMiddle; 125 Matcher<MotionEvent> mIsLineEnd; 126 Matcher<MotionEvent> mIsClickDown; 127 Matcher<MotionEvent> mIsClickUp; 128 129 @BeforeClass 130 public static void oneTimeInitialization() { 131 if (Looper.myLooper() == null) { 132 Looper.prepare(); 133 } 134 } 135 136 @Before 137 public void setUp() { 138 mMessageCapturingHandler = new MessageCapturingHandler(new Handler.Callback() { 139 @Override 140 public boolean handleMessage(Message msg) { 141 return mMotionEventInjector.handleMessage(msg); 142 } 143 }); 144 mMotionEventInjector = new MotionEventInjector(mMessageCapturingHandler); 145 mServiceInterface = mock(IAccessibilityServiceClient.class); 146 147 mLineList = createSimpleGestureFromPoints(0, 0, false, LINE_DURATION, LINE_START, LINE_END); 148 mClickList = createSimpleGestureFromPoints( 149 0, 0, false, CLICK_DURATION, CLICK_POINT, CLICK_POINT); 150 mContinuedLineList1 = createSimpleGestureFromPoints(CONTINUED_LINE_STROKE_ID_1, 0, true, 151 CONTINUED_LINE_INTERVAL, CONTINUED_LINE_START, CONTINUED_LINE_MID1); 152 mContinuedLineList2 = createSimpleGestureFromPoints(CONTINUED_LINE_STROKE_ID_2, 153 CONTINUED_LINE_STROKE_ID_1, false, CONTINUED_LINE_INTERVAL, CONTINUED_LINE_MID1, 154 CONTINUED_LINE_MID2, CONTINUED_LINE_END); 155 156 mClickDownEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, CLICK_POINT.x, CLICK_POINT.y, 0); 157 mClickDownEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN); 158 mClickUpEvent = MotionEvent.obtain(0, CLICK_DURATION, ACTION_UP, CLICK_POINT.x, 159 CLICK_POINT.y, 0); 160 mClickUpEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN); 161 162 mIsLineStart = allOf(IS_ACTION_DOWN, isAtPoint(LINE_START), hasStandardInitialization(), 163 hasTimeFromDown(0)); 164 mIsLineMiddle = allOf(IS_ACTION_MOVE, isAtPoint(LINE_END), hasStandardInitialization(), 165 hasTimeFromDown(LINE_DURATION)); 166 mIsLineEnd = allOf(IS_ACTION_UP, isAtPoint(LINE_END), hasStandardInitialization(), 167 hasTimeFromDown(LINE_DURATION)); 168 mIsClickDown = allOf(IS_ACTION_DOWN, isAtPoint(CLICK_POINT), hasStandardInitialization(), 169 hasTimeFromDown(0)); 170 mIsClickUp = allOf(IS_ACTION_UP, isAtPoint(CLICK_POINT), hasStandardInitialization(), 171 hasTimeFromDown(CLICK_DURATION)); 172 } 173 174 @Test 175 public void testInjectEvents_shouldEmergeInOrderWithCorrectTiming() throws RemoteException { 176 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 177 injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE); 178 verifyNoMoreInteractions(next); 179 mMessageCapturingHandler.sendOneMessage(); // Send a motion event 180 181 verify(next).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), eq(FLAG_PASS_TO_USER)); 182 verify(next).onMotionEvent(argThat(mIsLineStart), argThat(mIsLineStart), 183 eq(FLAG_PASS_TO_USER)); 184 verifyNoMoreInteractions(next); 185 reset(next); 186 187 Matcher<MotionEvent> hasRightDownTime = hasDownTime(mCaptor1.getValue().getDownTime()); 188 189 mMessageCapturingHandler.sendOneMessage(); // Send a motion event 190 verify(next).onMotionEvent(argThat(allOf(mIsLineMiddle, hasRightDownTime)), 191 argThat(allOf(mIsLineMiddle, hasRightDownTime)), eq(FLAG_PASS_TO_USER)); 192 verifyNoMoreInteractions(next); 193 reset(next); 194 195 verifyZeroInteractions(mServiceInterface); 196 197 mMessageCapturingHandler.sendOneMessage(); // Send a motion event 198 verify(next).onMotionEvent(argThat(allOf(mIsLineEnd, hasRightDownTime)), 199 argThat(allOf(mIsLineEnd, hasRightDownTime)), eq(FLAG_PASS_TO_USER)); 200 verifyNoMoreInteractions(next); 201 202 verify(mServiceInterface).onPerformGestureResult(LINE_SEQUENCE, true); 203 verifyNoMoreInteractions(mServiceInterface); 204 } 205 206 @Test 207 public void testInjectEvents_gestureWithTooManyPoints_shouldNotCrash() throws Exception { 208 int tooManyPointsCount = 20; 209 TouchPoint[] startTouchPoints = new TouchPoint[tooManyPointsCount]; 210 TouchPoint[] endTouchPoints = new TouchPoint[tooManyPointsCount]; 211 for (int i = 0; i < tooManyPointsCount; i++) { 212 startTouchPoints[i] = new TouchPoint(); 213 startTouchPoints[i].mIsStartOfPath = true; 214 startTouchPoints[i].mX = i; 215 startTouchPoints[i].mY = i; 216 endTouchPoints[i] = new TouchPoint(); 217 endTouchPoints[i].mIsEndOfPath = true; 218 endTouchPoints[i].mX = i; 219 endTouchPoints[i].mY = i; 220 } 221 List<GestureStep> events = Arrays.asList( 222 new GestureStep(0, tooManyPointsCount, startTouchPoints), 223 new GestureStep(CLICK_DURATION, tooManyPointsCount, endTouchPoints)); 224 attachMockNext(mMotionEventInjector); 225 injectEventsSync(events, mServiceInterface, CLICK_SEQUENCE); 226 mMessageCapturingHandler.sendAllMessages(); 227 verify(mServiceInterface).onPerformGestureResult(eq(CLICK_SEQUENCE), anyBoolean()); 228 } 229 230 @Test 231 public void testRegularEvent_afterGestureComplete_shouldPassToNext() { 232 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 233 injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE); 234 mMessageCapturingHandler.sendAllMessages(); // Send all motion events 235 reset(next); 236 mMotionEventInjector.onMotionEvent(mClickDownEvent, mClickDownEvent, 0); 237 verify(next).onMotionEvent(argThat(mIsClickDown), argThat(mIsClickDown), eq(0)); 238 } 239 240 @Test 241 public void testInjectEvents_withRealGestureUnderway_shouldCancelRealAndPassInjected() { 242 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 243 mMotionEventInjector.onMotionEvent(mClickDownEvent, mClickDownEvent, 0); 244 injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE); 245 246 verify(next, times(2)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt()); 247 assertThat(mCaptor1.getAllValues().get(0), mIsClickDown); 248 assertThat(mCaptor1.getAllValues().get(1), IS_ACTION_CANCEL); 249 reset(next); 250 251 mMessageCapturingHandler.sendOneMessage(); // Send a motion event 252 verify(next).onMotionEvent( 253 argThat(mIsLineStart), argThat(mIsLineStart), eq(FLAG_PASS_TO_USER)); 254 } 255 256 @Test 257 public void testInjectEvents_withRealMouseGestureUnderway_shouldContinueRealAndPassInjected() { 258 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 259 MotionEvent mouseEvent = MotionEvent.obtain(mClickDownEvent); 260 mouseEvent.setSource(InputDevice.SOURCE_MOUSE); 261 MotionEventMatcher isMouseEvent = new MotionEventMatcher(mouseEvent); 262 mMotionEventInjector.onMotionEvent(mouseEvent, mouseEvent, 0); 263 injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE); 264 265 mMessageCapturingHandler.sendOneMessage(); // Send a motion event 266 verify(next, times(2)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt()); 267 assertThat(mCaptor1.getAllValues().get(0), isMouseEvent); 268 assertThat(mCaptor1.getAllValues().get(1), mIsLineStart); 269 } 270 271 @Test 272 public void testInjectEvents_withRealGestureFinished_shouldJustPassInjected() { 273 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 274 mMotionEventInjector.onMotionEvent(mClickDownEvent, mClickDownEvent, 0); 275 mMotionEventInjector.onMotionEvent(mClickUpEvent, mClickUpEvent, 0); 276 277 injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE); 278 verify(next, times(2)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt()); 279 assertThat(mCaptor1.getAllValues().get(0), mIsClickDown); 280 assertThat(mCaptor1.getAllValues().get(1), mIsClickUp); 281 reset(next); 282 283 mMessageCapturingHandler.sendOneMessage(); // Send a motion event 284 verify(next).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), eq(FLAG_PASS_TO_USER)); 285 verify(next).onMotionEvent( 286 argThat(mIsLineStart), argThat(mIsLineStart), eq(FLAG_PASS_TO_USER)); 287 } 288 289 @Test 290 public void testOnMotionEvents_openInjectedGestureInProgress_shouldCancelAndNotifyAndPassReal() 291 throws RemoteException { 292 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 293 injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE); 294 mMessageCapturingHandler.sendOneMessage(); // Send a motion event 295 mMotionEventInjector.onMotionEvent(mClickDownEvent, mClickDownEvent, 0); 296 297 verify(next, times(3)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt()); 298 assertThat(mCaptor1.getAllValues().get(0), mIsLineStart); 299 assertThat(mCaptor1.getAllValues().get(1), IS_ACTION_CANCEL); 300 assertThat(mCaptor1.getAllValues().get(2), mIsClickDown); 301 verify(mServiceInterface).onPerformGestureResult(LINE_SEQUENCE, false); 302 } 303 304 @Test 305 public void testOnMotionEvents_closedInjectedGestureInProgress_shouldOnlyNotifyAndPassReal() 306 throws RemoteException { 307 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 308 // Tack a click down to the end of the line 309 TouchPoint clickTouchPoint = new TouchPoint(); 310 clickTouchPoint.mIsStartOfPath = true; 311 clickTouchPoint.mX = CLICK_POINT.x; 312 clickTouchPoint.mY = CLICK_POINT.y; 313 mLineList.add(new GestureStep(0, 1, new TouchPoint[] {clickTouchPoint})); 314 315 injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE); 316 317 // Send 3 motion events, leaving the extra down in the queue 318 mMessageCapturingHandler.sendOneMessage(); 319 mMessageCapturingHandler.sendOneMessage(); 320 mMessageCapturingHandler.sendOneMessage(); 321 322 mMotionEventInjector.onMotionEvent(mClickDownEvent, mClickDownEvent, 0); 323 324 verify(next, times(4)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt()); 325 assertThat(mCaptor1.getAllValues().get(0), mIsLineStart); 326 assertThat(mCaptor1.getAllValues().get(1), mIsLineMiddle); 327 assertThat(mCaptor1.getAllValues().get(2), mIsLineEnd); 328 assertThat(mCaptor1.getAllValues().get(3), mIsClickDown); 329 verify(mServiceInterface).onPerformGestureResult(LINE_SEQUENCE, false); 330 assertFalse(mMessageCapturingHandler.hasMessages()); 331 } 332 333 @Test 334 public void testInjectEvents_openInjectedGestureInProgress_shouldCancelAndNotifyAndPassNew() 335 throws RemoteException { 336 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 337 injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE); 338 mMessageCapturingHandler.sendOneMessage(); // Send a motion event 339 340 injectEventsSync(mClickList, mServiceInterface, CLICK_SEQUENCE); 341 mMessageCapturingHandler.sendOneMessage(); // Send a motion event 342 343 verify(mServiceInterface, times(1)).onPerformGestureResult(LINE_SEQUENCE, false); 344 verify(next, times(3)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt()); 345 assertThat(mCaptor1.getAllValues().get(0), mIsLineStart); 346 assertThat(mCaptor1.getAllValues().get(1), IS_ACTION_CANCEL); 347 assertThat(mCaptor1.getAllValues().get(2), mIsClickDown); 348 } 349 350 @Test 351 public void testInjectEvents_closedInjectedGestureInProgress_shouldOnlyNotifyAndPassNew() 352 throws RemoteException { 353 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 354 // Tack a click down to the end of the line 355 TouchPoint clickTouchPoint = new TouchPoint(); 356 clickTouchPoint.mIsStartOfPath = true; 357 clickTouchPoint.mX = CLICK_POINT.x; 358 clickTouchPoint.mY = CLICK_POINT.y; 359 mLineList.add(new GestureStep(0, 1, new TouchPoint[] {clickTouchPoint})); 360 injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE); 361 362 // Send 3 motion events, leaving newEvent in the queue 363 mMessageCapturingHandler.sendOneMessage(); 364 mMessageCapturingHandler.sendOneMessage(); 365 mMessageCapturingHandler.sendOneMessage(); 366 367 injectEventsSync(mClickList, mServiceInterface, CLICK_SEQUENCE); 368 mMessageCapturingHandler.sendOneMessage(); // Send a motion event 369 370 verify(mServiceInterface).onPerformGestureResult(LINE_SEQUENCE, false); 371 verify(next, times(4)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt()); 372 assertThat(mCaptor1.getAllValues().get(0), mIsLineStart); 373 assertThat(mCaptor1.getAllValues().get(1), mIsLineMiddle); 374 assertThat(mCaptor1.getAllValues().get(2), mIsLineEnd); 375 assertThat(mCaptor1.getAllValues().get(3), mIsClickDown); 376 } 377 378 @Test 379 public void testContinuedGesture_continuationArrivesAfterDispatched_gestureCompletes() 380 throws Exception { 381 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 382 injectEventsSync(mContinuedLineList1, mServiceInterface, CONTINUED_LINE_SEQUENCE_1); 383 mMessageCapturingHandler.sendAllMessages(); // Send all motion events 384 verify(mServiceInterface).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_1, true); 385 injectEventsSync(mContinuedLineList2, mServiceInterface, CONTINUED_LINE_SEQUENCE_2); 386 mMessageCapturingHandler.sendAllMessages(); // Send all motion events 387 verify(mServiceInterface).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_2, true); 388 verify(next, times(5)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt()); 389 List<MotionEvent> events = mCaptor1.getAllValues(); 390 long downTime = events.get(0).getDownTime(); 391 assertThat(events.get(0), allOf(isAtPoint(CONTINUED_LINE_START), IS_ACTION_DOWN, 392 hasEventTime(downTime))); 393 assertThat(events, everyItem(hasDownTime(downTime))); 394 assertThat(events.get(1), allOf(isAtPoint(CONTINUED_LINE_MID1), IS_ACTION_MOVE, 395 hasEventTime(downTime + CONTINUED_LINE_INTERVAL))); 396 // Timing will restart when the gesture continues 397 long secondSequenceStart = events.get(2).getEventTime(); 398 assertTrue(secondSequenceStart >= events.get(1).getEventTime()); 399 assertThat(events.get(2), allOf(isAtPoint(CONTINUED_LINE_MID2), IS_ACTION_MOVE)); 400 assertThat(events.get(3), allOf(isAtPoint(CONTINUED_LINE_END), IS_ACTION_MOVE, 401 hasEventTime(secondSequenceStart + CONTINUED_LINE_INTERVAL))); 402 assertThat(events.get(4), allOf(isAtPoint(CONTINUED_LINE_END), IS_ACTION_UP, 403 hasEventTime(secondSequenceStart + CONTINUED_LINE_INTERVAL))); 404 } 405 406 @Test 407 public void testContinuedGesture_withTwoTouchPoints_gestureCompletes() 408 throws Exception { 409 // Run one point through the continued line backwards 410 int backLineId1 = 30; 411 int backLineId2 = 30; 412 List<GestureStep> continuedBackLineList1 = createSimpleGestureFromPoints(backLineId1, 0, 413 true, CONTINUED_LINE_INTERVAL, CONTINUED_LINE_END, CONTINUED_LINE_MID2); 414 List<GestureStep> continuedBackLineList2 = createSimpleGestureFromPoints(backLineId2, 415 backLineId1, false, CONTINUED_LINE_INTERVAL, CONTINUED_LINE_MID2, 416 CONTINUED_LINE_MID1, CONTINUED_LINE_START); 417 List<GestureStep> combinedLines1 = combineGestureSteps( 418 mContinuedLineList1, continuedBackLineList1); 419 List<GestureStep> combinedLines2 = combineGestureSteps( 420 mContinuedLineList2, continuedBackLineList2); 421 422 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 423 injectEventsSync(combinedLines1, mServiceInterface, CONTINUED_LINE_SEQUENCE_1); 424 injectEventsSync(combinedLines2, mServiceInterface, CONTINUED_LINE_SEQUENCE_2); 425 mMessageCapturingHandler.sendAllMessages(); // Send all motion events 426 verify(mServiceInterface).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_1, true); 427 verify(mServiceInterface).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_2, true); 428 verify(next, times(7)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt()); 429 List<MotionEvent> events = mCaptor1.getAllValues(); 430 long downTime = events.get(0).getDownTime(); 431 assertThat(events.get(0), allOf( 432 anyOf(isAtPoint(CONTINUED_LINE_END), isAtPoint(CONTINUED_LINE_START)), 433 IS_ACTION_DOWN, hasEventTime(downTime))); 434 assertThat(events, everyItem(hasDownTime(downTime))); 435 assertThat(events.get(1), allOf(containsPoints(CONTINUED_LINE_START, CONTINUED_LINE_END), 436 IS_ACTION_POINTER_DOWN, hasEventTime(downTime))); 437 assertThat(events.get(2), allOf(containsPoints(CONTINUED_LINE_MID1, CONTINUED_LINE_MID2), 438 IS_ACTION_MOVE, hasEventTime(downTime + CONTINUED_LINE_INTERVAL))); 439 assertThat(events.get(3), allOf(containsPoints(CONTINUED_LINE_MID1, CONTINUED_LINE_MID2), 440 IS_ACTION_MOVE, hasEventTime(downTime + CONTINUED_LINE_INTERVAL * 2))); 441 assertThat(events.get(4), allOf(containsPoints(CONTINUED_LINE_START, CONTINUED_LINE_END), 442 IS_ACTION_MOVE, hasEventTime(downTime + CONTINUED_LINE_INTERVAL * 3))); 443 assertThat(events.get(5), allOf(containsPoints(CONTINUED_LINE_START, CONTINUED_LINE_END), 444 IS_ACTION_POINTER_UP, hasEventTime(downTime + CONTINUED_LINE_INTERVAL * 3))); 445 assertThat(events.get(6), allOf( 446 anyOf(isAtPoint(CONTINUED_LINE_END), isAtPoint(CONTINUED_LINE_START)), 447 IS_ACTION_UP, hasEventTime(downTime + CONTINUED_LINE_INTERVAL * 3))); 448 } 449 450 451 @Test 452 public void testContinuedGesture_continuationArrivesWhileDispatching_gestureCompletes() 453 throws Exception { 454 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 455 injectEventsSync(mContinuedLineList1, mServiceInterface, CONTINUED_LINE_SEQUENCE_1); 456 mMessageCapturingHandler.sendOneMessage(); // Send a motion event 457 injectEventsSync(mContinuedLineList2, mServiceInterface, CONTINUED_LINE_SEQUENCE_2); 458 mMessageCapturingHandler.sendAllMessages(); // Send all motion events 459 verify(mServiceInterface).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_1, true); 460 verify(mServiceInterface).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_2, true); 461 verify(next, times(5)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt()); 462 List<MotionEvent> events = mCaptor1.getAllValues(); 463 long downTime = events.get(0).getDownTime(); 464 assertThat(events.get(0), allOf(isAtPoint(CONTINUED_LINE_START), IS_ACTION_DOWN, 465 hasEventTime(downTime))); 466 assertThat(events, everyItem(hasDownTime(downTime))); 467 assertThat(events.get(1), allOf(isAtPoint(CONTINUED_LINE_MID1), IS_ACTION_MOVE, 468 hasEventTime(downTime + CONTINUED_LINE_INTERVAL))); 469 assertThat(events.get(2), allOf(isAtPoint(CONTINUED_LINE_MID2), IS_ACTION_MOVE, 470 hasEventTime(downTime + CONTINUED_LINE_INTERVAL * 2))); 471 assertThat(events.get(3), allOf(isAtPoint(CONTINUED_LINE_END), IS_ACTION_MOVE, 472 hasEventTime(downTime + CONTINUED_LINE_INTERVAL * 3))); 473 assertThat(events.get(4), allOf(isAtPoint(CONTINUED_LINE_END), IS_ACTION_UP, 474 hasEventTime(downTime + CONTINUED_LINE_INTERVAL * 3))); 475 } 476 477 @Test 478 public void testContinuedGesture_twoContinuationsArriveWhileDispatching_gestureCompletes() 479 throws Exception { 480 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 481 injectEventsSync(mContinuedLineList1, mServiceInterface, CONTINUED_LINE_SEQUENCE_1); 482 // Continue line again 483 List<GestureStep> continuedLineList2 = createSimpleGestureFromPoints( 484 CONTINUED_LINE_STROKE_ID_2, CONTINUED_LINE_STROKE_ID_1, true, 485 CONTINUED_LINE_INTERVAL, CONTINUED_LINE_MID1, 486 CONTINUED_LINE_MID2, CONTINUED_LINE_END); 487 // Finish line by backtracking 488 int strokeId3 = CONTINUED_LINE_STROKE_ID_2 + 1; 489 int sequence3 = CONTINUED_LINE_SEQUENCE_2 + 1; 490 List<GestureStep> continuedLineList3 = createSimpleGestureFromPoints(strokeId3, 491 CONTINUED_LINE_STROKE_ID_2, false, CONTINUED_LINE_INTERVAL, CONTINUED_LINE_END, 492 CONTINUED_LINE_MID2); 493 494 mMessageCapturingHandler.sendOneMessage(); // Send a motion event 495 injectEventsSync(continuedLineList2, mServiceInterface, CONTINUED_LINE_SEQUENCE_2); 496 injectEventsSync(continuedLineList3, mServiceInterface, sequence3); 497 mMessageCapturingHandler.sendAllMessages(); // Send all motion events 498 verify(mServiceInterface).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_1, true); 499 verify(mServiceInterface).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_2, true); 500 verify(mServiceInterface).onPerformGestureResult(sequence3, true); 501 verify(next, times(6)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt()); 502 List<MotionEvent> events = mCaptor1.getAllValues(); 503 long downTime = events.get(0).getDownTime(); 504 assertThat(events.get(0), allOf(isAtPoint(CONTINUED_LINE_START), IS_ACTION_DOWN, 505 hasEventTime(downTime))); 506 assertThat(events, everyItem(hasDownTime(downTime))); 507 assertThat(events.get(1), allOf(isAtPoint(CONTINUED_LINE_MID1), IS_ACTION_MOVE, 508 hasEventTime(downTime + CONTINUED_LINE_INTERVAL))); 509 assertThat(events.get(2), allOf(isAtPoint(CONTINUED_LINE_MID2), IS_ACTION_MOVE, 510 hasEventTime(downTime + CONTINUED_LINE_INTERVAL * 2))); 511 assertThat(events.get(3), allOf(isAtPoint(CONTINUED_LINE_END), IS_ACTION_MOVE, 512 hasEventTime(downTime + CONTINUED_LINE_INTERVAL * 3))); 513 assertThat(events.get(4), allOf(isAtPoint(CONTINUED_LINE_MID2), IS_ACTION_MOVE, 514 hasEventTime(downTime + CONTINUED_LINE_INTERVAL * 4))); 515 assertThat(events.get(5), allOf(isAtPoint(CONTINUED_LINE_MID2), IS_ACTION_UP, 516 hasEventTime(downTime + CONTINUED_LINE_INTERVAL * 4))); 517 } 518 519 @Test 520 public void testContinuedGesture_nonContinuingGestureArrivesDuringDispatch_gestureCanceled() 521 throws Exception { 522 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 523 injectEventsSync(mContinuedLineList1, mServiceInterface, CONTINUED_LINE_SEQUENCE_1); 524 mMessageCapturingHandler.sendOneMessage(); // Send a motion event 525 injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE); 526 mMessageCapturingHandler.sendAllMessages(); // Send all motion events 527 verify(mServiceInterface).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_1, false); 528 verify(mServiceInterface).onPerformGestureResult(LINE_SEQUENCE, true); 529 verify(next, times(5)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt()); 530 List<MotionEvent> events = mCaptor1.getAllValues(); 531 assertThat(events.get(0), allOf(isAtPoint(CONTINUED_LINE_START), IS_ACTION_DOWN)); 532 assertThat(events.get(1), IS_ACTION_CANCEL); 533 assertThat(events.get(2), allOf(isAtPoint(LINE_START), IS_ACTION_DOWN)); 534 assertThat(events.get(3), allOf(isAtPoint(LINE_END), IS_ACTION_MOVE)); 535 assertThat(events.get(4), allOf(isAtPoint(LINE_END), IS_ACTION_UP)); 536 } 537 538 @Test 539 public void testContinuedGesture_nonContinuingGestureArrivesAfterDispatch_gestureCanceled() 540 throws Exception { 541 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 542 injectEventsSync(mContinuedLineList1, mServiceInterface, CONTINUED_LINE_SEQUENCE_1); 543 mMessageCapturingHandler.sendAllMessages(); // Send all motion events 544 injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE); 545 mMessageCapturingHandler.sendAllMessages(); // Send all motion events 546 verify(mServiceInterface).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_1, true); 547 verify(mServiceInterface).onPerformGestureResult(LINE_SEQUENCE, true); 548 verify(next, times(6)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt()); 549 List<MotionEvent> events = mCaptor1.getAllValues(); 550 assertThat(events.get(0), allOf(isAtPoint(CONTINUED_LINE_START), IS_ACTION_DOWN)); 551 assertThat(events.get(1), allOf(isAtPoint(CONTINUED_LINE_MID1), IS_ACTION_MOVE)); 552 assertThat(events.get(2), IS_ACTION_CANCEL); 553 assertThat(events.get(3), allOf(isAtPoint(LINE_START), IS_ACTION_DOWN)); 554 assertThat(events.get(4), allOf(isAtPoint(LINE_END), IS_ACTION_MOVE)); 555 assertThat(events.get(5), allOf(isAtPoint(LINE_END), IS_ACTION_UP)); 556 } 557 558 @Test 559 public void testContinuedGesture_misMatchedContinuationArrives_bothGesturesCanceled() 560 throws Exception { 561 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 562 injectEventsSync(mContinuedLineList1, mServiceInterface, CONTINUED_LINE_SEQUENCE_1); 563 mMessageCapturingHandler.sendAllMessages(); // Send all motion events 564 verify(mServiceInterface).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_1, true); 565 List<GestureStep> discontinuousGesture = mContinuedLineList2 566 .subList(1, mContinuedLineList2.size()); 567 injectEventsSync(discontinuousGesture, mServiceInterface, CONTINUED_LINE_SEQUENCE_2); 568 mMessageCapturingHandler.sendAllMessages(); // Send all motion events 569 verify(mServiceInterface).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_2, false); 570 verify(next, times(3)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt()); 571 List<MotionEvent> events = mCaptor1.getAllValues(); 572 assertThat(events.get(0), allOf(isAtPoint(CONTINUED_LINE_START), IS_ACTION_DOWN)); 573 assertThat(events.get(1), allOf(isAtPoint(CONTINUED_LINE_MID1), IS_ACTION_MOVE)); 574 assertThat(events.get(2), allOf(isAtPoint(CONTINUED_LINE_MID1), IS_ACTION_CANCEL)); 575 } 576 577 @Test 578 public void testContinuedGesture_continuationArrivesFromOtherService_bothGesturesCanceled() 579 throws Exception { 580 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 581 IAccessibilityServiceClient otherService = mock(IAccessibilityServiceClient.class); 582 injectEventsSync(mContinuedLineList1, mServiceInterface, CONTINUED_LINE_SEQUENCE_1); 583 mMessageCapturingHandler.sendOneMessage(); // Send a motion events 584 injectEventsSync(mContinuedLineList2, otherService, CONTINUED_LINE_SEQUENCE_2); 585 mMessageCapturingHandler.sendAllMessages(); // Send all motion events 586 verify(mServiceInterface).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_1, false); 587 verify(otherService).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_2, false); 588 verify(next, times(2)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt()); 589 List<MotionEvent> events = mCaptor1.getAllValues(); 590 assertThat(events.get(0), allOf(isAtPoint(CONTINUED_LINE_START), IS_ACTION_DOWN)); 591 assertThat(events.get(1), IS_ACTION_CANCEL); 592 } 593 594 @Test 595 public void testContinuedGesture_realGestureArrivesInBetween_getsCanceled() 596 throws Exception { 597 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 598 injectEventsSync(mContinuedLineList1, mServiceInterface, CONTINUED_LINE_SEQUENCE_1); 599 mMessageCapturingHandler.sendAllMessages(); // Send all motion events 600 verify(mServiceInterface).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_1, true); 601 602 mMotionEventInjector.onMotionEvent(mClickDownEvent, mClickDownEvent, 0); 603 604 injectEventsSync(mContinuedLineList2, mServiceInterface, CONTINUED_LINE_SEQUENCE_2); 605 mMessageCapturingHandler.sendAllMessages(); // Send all motion events 606 verify(mServiceInterface).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_2, false); 607 verify(next, times(4)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt()); 608 List<MotionEvent> events = mCaptor1.getAllValues(); 609 assertThat(events.get(0), allOf(isAtPoint(CONTINUED_LINE_START), IS_ACTION_DOWN)); 610 assertThat(events.get(1), allOf(isAtPoint(CONTINUED_LINE_MID1), IS_ACTION_MOVE)); 611 assertThat(events.get(2), IS_ACTION_CANCEL); 612 assertThat(events.get(3), allOf(isAtPoint(CLICK_POINT), IS_ACTION_DOWN)); 613 } 614 615 @Test 616 public void testClearEvents_realGestureInProgress_shouldForgetAboutGesture() { 617 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 618 mMotionEventInjector.onMotionEvent(mClickDownEvent, mClickDownEvent, 0); 619 mMotionEventInjector.clearEvents(MOTION_EVENT_SOURCE); 620 injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE); 621 mMessageCapturingHandler.sendOneMessage(); // Send a motion event 622 623 verify(next, times(2)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt()); 624 assertThat(mCaptor1.getAllValues().get(0), mIsClickDown); 625 assertThat(mCaptor1.getAllValues().get(1), mIsLineStart); 626 } 627 628 @Test 629 public void testClearEventsOnOtherSource_realGestureInProgress_shouldNotForgetAboutGesture() { 630 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 631 mMotionEventInjector.onMotionEvent(mClickDownEvent, mClickDownEvent, 0); 632 mMotionEventInjector.clearEvents(OTHER_EVENT_SOURCE); 633 injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE); 634 mMessageCapturingHandler.sendOneMessage(); // Send a motion event 635 636 verify(next, times(3)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt()); 637 assertThat(mCaptor1.getAllValues().get(0), mIsClickDown); 638 assertThat(mCaptor1.getAllValues().get(1), IS_ACTION_CANCEL); 639 assertThat(mCaptor1.getAllValues().get(2), mIsLineStart); 640 } 641 642 @Test 643 public void testOnDestroy_shouldCancelGestures() throws RemoteException { 644 mMotionEventInjector.onDestroy(); 645 injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE); 646 verify(mServiceInterface).onPerformGestureResult(LINE_SEQUENCE, false); 647 } 648 649 @Test 650 public void testInjectEvents_withNoNext_shouldCancel() throws RemoteException { 651 injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE); 652 verify(mServiceInterface).onPerformGestureResult(LINE_SEQUENCE, false); 653 } 654 655 @Test 656 public void testOnMotionEvent_withNoNext_shouldNotCrash() { 657 mMotionEventInjector.onMotionEvent(mClickDownEvent, mClickDownEvent, 0); 658 } 659 660 @Test 661 public void testOnKeyEvent_shouldPassToNext() { 662 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 663 KeyEvent event = new KeyEvent(0, 0); 664 mMotionEventInjector.onKeyEvent(event, 0); 665 verify(next).onKeyEvent(event, 0); 666 } 667 668 @Test 669 public void testOnKeyEvent_withNoNext_shouldNotCrash() { 670 KeyEvent event = new KeyEvent(0, 0); 671 mMotionEventInjector.onKeyEvent(event, 0); 672 } 673 674 @Test 675 public void testOnAccessibilityEvent_shouldPassToNext() { 676 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 677 AccessibilityEvent event = AccessibilityEvent.obtain(); 678 mMotionEventInjector.onAccessibilityEvent(event); 679 verify(next).onAccessibilityEvent(event); 680 } 681 682 @Test 683 public void testOnAccessibilityEvent_withNoNext_shouldNotCrash() { 684 AccessibilityEvent event = AccessibilityEvent.obtain(); 685 mMotionEventInjector.onAccessibilityEvent(event); 686 } 687 688 private void injectEventsSync(List<GestureStep> gestureSteps, 689 IAccessibilityServiceClient serviceInterface, int sequence) { 690 mMotionEventInjector.injectEvents(gestureSteps, serviceInterface, sequence); 691 // Dispatch the message sent by the injector. Our simple handler doesn't guarantee stuff 692 // happens in order. 693 mMessageCapturingHandler.sendLastMessage(); 694 } 695 696 private List<GestureStep> createSimpleGestureFromPoints(int strokeId, int continuedStrokeId, 697 boolean continued, long interval, Point... points) { 698 List<GestureStep> gesture = new ArrayList<>(points.length); 699 TouchPoint[] touchPoints = new TouchPoint[1]; 700 touchPoints[0] = new TouchPoint(); 701 for (int i = 0; i < points.length; i++) { 702 touchPoints[0].mX = points[i].x; 703 touchPoints[0].mY = points[i].y; 704 touchPoints[0].mIsStartOfPath = ((i == 0) && (continuedStrokeId <= 0)); 705 touchPoints[0].mContinuedStrokeId = continuedStrokeId; 706 touchPoints[0].mStrokeId = strokeId; 707 touchPoints[0].mIsEndOfPath = ((i == points.length - 1) && !continued); 708 gesture.add(new GestureStep(interval * i, 1, touchPoints)); 709 } 710 return gesture; 711 } 712 713 List<GestureStep> combineGestureSteps(List<GestureStep> list1, List<GestureStep> list2) { 714 assertEquals(list1.size(), list2.size()); 715 List<GestureStep> gesture = new ArrayList<>(list1.size()); 716 for (int i = 0; i < list1.size(); i++) { 717 int numPoints1 = list1.get(i).numTouchPoints; 718 int numPoints2 = list2.get(i).numTouchPoints; 719 TouchPoint[] touchPoints = new TouchPoint[numPoints1 + numPoints2]; 720 for (int j = 0; j < numPoints1; j++) { 721 touchPoints[j] = new TouchPoint(); 722 touchPoints[j].copyFrom(list1.get(i).touchPoints[j]); 723 } 724 for (int j = 0; j < numPoints2; j++) { 725 touchPoints[numPoints1 + j] = new TouchPoint(); 726 touchPoints[numPoints1 + j].copyFrom(list2.get(i).touchPoints[j]); 727 } 728 gesture.add(new GestureStep(list1.get(i).timeSinceGestureStart, 729 numPoints1 + numPoints2, touchPoints)); 730 } 731 return gesture; 732 } 733 734 private EventStreamTransformation attachMockNext(MotionEventInjector motionEventInjector) { 735 EventStreamTransformation next = mock(EventStreamTransformation.class); 736 motionEventInjector.setNext(next); 737 return next; 738 } 739 740 static class MotionEventMatcher extends TypeSafeMatcher<MotionEvent> { 741 long mDownTime; 742 long mEventTime; 743 long mActionMasked; 744 int mX; 745 int mY; 746 747 MotionEventMatcher(long downTime, long eventTime, int actionMasked, int x, int y) { 748 mDownTime = downTime; 749 mEventTime = eventTime; 750 mActionMasked = actionMasked; 751 mX = x; 752 mY = y; 753 } 754 755 MotionEventMatcher(MotionEvent event) { 756 this(event.getDownTime(), event.getEventTime(), event.getActionMasked(), 757 (int) event.getX(), (int) event.getY()); 758 } 759 760 void offsetTimesBy(long timeOffset) { 761 mDownTime += timeOffset; 762 mEventTime += timeOffset; 763 } 764 765 @Override 766 public boolean matchesSafely(MotionEvent event) { 767 if ((event.getDownTime() == mDownTime) && (event.getEventTime() == mEventTime) 768 && (event.getActionMasked() == mActionMasked) && ((int) event.getX() == mX) 769 && ((int) event.getY() == mY)) { 770 return true; 771 } 772 Log.e(LOG_TAG, "MotionEvent match failed"); 773 Log.e(LOG_TAG, "event.getDownTime() = " + event.getDownTime() 774 + ", expected " + mDownTime); 775 Log.e(LOG_TAG, "event.getEventTime() = " + event.getEventTime() 776 + ", expected " + mEventTime); 777 Log.e(LOG_TAG, "event.getActionMasked() = " + event.getActionMasked() 778 + ", expected " + mActionMasked); 779 Log.e(LOG_TAG, "event.getX() = " + event.getX() + ", expected " + mX); 780 Log.e(LOG_TAG, "event.getY() = " + event.getY() + ", expected " + mY); 781 return false; 782 } 783 784 @Override 785 public void describeTo(Description description) { 786 description.appendText("Motion event matcher"); 787 } 788 } 789 790 private static class MotionEventActionMatcher extends TypeSafeMatcher<MotionEvent> { 791 int mAction; 792 793 MotionEventActionMatcher(int action) { 794 super(); 795 mAction = action; 796 } 797 798 @Override 799 protected boolean matchesSafely(MotionEvent motionEvent) { 800 return motionEvent.getActionMasked() == mAction; 801 } 802 803 @Override 804 public void describeTo(Description description) { 805 description.appendText("Matching to action " + mAction); 806 } 807 } 808 809 private static TypeSafeMatcher<MotionEvent> isAtPoint(final Point point) { 810 return new TypeSafeMatcher<MotionEvent>() { 811 @Override 812 protected boolean matchesSafely(MotionEvent event) { 813 return ((event.getX() == point.x) && (event.getY() == point.y)); 814 } 815 816 @Override 817 public void describeTo(Description description) { 818 description.appendText("Is at point " + point); 819 } 820 }; 821 } 822 823 private static TypeSafeMatcher<MotionEvent> containsPoints(final Point... points) { 824 return new TypeSafeMatcher<MotionEvent>() { 825 @Override 826 protected boolean matchesSafely(MotionEvent event) { 827 MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords(); 828 for (int i = 0; i < points.length; i++) { 829 boolean havePoint = false; 830 for (int j = 0; j < points.length; j++) { 831 event.getPointerCoords(j, coords); 832 if ((points[i].x == coords.x) && (points[i].y == coords.y)) { 833 havePoint = true; 834 } 835 } 836 if (!havePoint) { 837 return false; 838 } 839 } 840 return true; 841 } 842 843 @Override 844 public void describeTo(Description description) { 845 description.appendText("Contains points " + points); 846 } 847 }; 848 } 849 850 private static TypeSafeMatcher<MotionEvent> hasDownTime(final long downTime) { 851 return new TypeSafeMatcher<MotionEvent>() { 852 @Override 853 protected boolean matchesSafely(MotionEvent event) { 854 return event.getDownTime() == downTime; 855 } 856 857 @Override 858 public void describeTo(Description description) { 859 description.appendText("Down time = " + downTime); 860 } 861 }; 862 } 863 864 private static TypeSafeMatcher<MotionEvent> hasEventTime(final long eventTime) { 865 return new TypeSafeMatcher<MotionEvent>() { 866 @Override 867 protected boolean matchesSafely(MotionEvent event) { 868 return event.getEventTime() == eventTime; 869 } 870 871 @Override 872 public void describeTo(Description description) { 873 description.appendText("Event time = " + eventTime); 874 } 875 }; 876 } 877 878 private static TypeSafeMatcher<MotionEvent> hasTimeFromDown(final long timeFromDown) { 879 return new TypeSafeMatcher<MotionEvent>() { 880 @Override 881 protected boolean matchesSafely(MotionEvent event) { 882 return (event.getEventTime() - event.getDownTime()) == timeFromDown; 883 } 884 885 @Override 886 public void describeTo(Description description) { 887 description.appendText("Time from down to event times = " + timeFromDown); 888 } 889 }; 890 } 891 892 private static TypeSafeMatcher<MotionEvent> hasStandardInitialization() { 893 return new TypeSafeMatcher<MotionEvent>() { 894 @Override 895 protected boolean matchesSafely(MotionEvent event) { 896 return (0 == event.getActionIndex()) && (0 == event.getDeviceId()) 897 && (0 == event.getEdgeFlags()) && (0 == event.getFlags()) 898 && (0 == event.getMetaState()) && (0F == event.getOrientation()) 899 && (0F == event.getTouchMajor()) && (0F == event.getTouchMinor()) 900 && (1F == event.getXPrecision()) && (1F == event.getYPrecision()) 901 && (1 == event.getPointerCount()) && (1F == event.getPressure()) 902 && (InputDevice.SOURCE_TOUCHSCREEN == event.getSource()); 903 } 904 905 @Override 906 public void describeTo(Description description) { 907 description.appendText("Has standard values for all parameters"); 908 } 909 }; 910 } 911 } 912