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