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