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