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