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