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