1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package org.chromium.content.browser;
6
7import android.content.Context;
8import android.os.Build;
9import android.os.Bundle;
10import android.os.SystemClock;
11import android.test.InstrumentationTestCase;
12import android.test.suitebuilder.annotation.SmallTest;
13import android.util.Log;
14import android.view.MotionEvent;
15import android.view.MotionEvent.PointerCoords;
16import android.view.MotionEvent.PointerProperties;
17import android.view.ViewConfiguration;
18
19import org.chromium.base.test.util.Feature;
20import org.chromium.content.browser.ContentViewGestureHandler.MotionEventDelegate;
21import org.chromium.content.browser.third_party.GestureDetector;
22
23import java.util.ArrayList;
24import java.util.concurrent.CountDownLatch;
25
26/**
27 * Test suite for ContentViewGestureHandler.
28 */
29public class ContentViewGestureHandlerTest extends InstrumentationTestCase {
30    private static final int FAKE_COORD_X = 42;
31    private static final int FAKE_COORD_Y = 24;
32
33    private static final String TAG = "ContentViewGestureHandler";
34    private MockListener mMockListener;
35    private MockMotionEventDelegate mMockMotionEventDelegate;
36    private MockGestureDetector mMockGestureDetector;
37    private ContentViewGestureHandler mGestureHandler;
38    private LongPressDetector mLongPressDetector;
39
40    static class MockListener extends GestureDetector.SimpleOnGestureListener {
41        MotionEvent mLastLongPress;
42        MotionEvent mLastSingleTap;
43        MotionEvent mLastFling1;
44        CountDownLatch mLongPressCalled;
45
46        public MockListener() {
47            mLongPressCalled = new CountDownLatch(1);
48        }
49
50        @Override
51        public void onLongPress(MotionEvent e) {
52            mLastLongPress = MotionEvent.obtain(e);
53            mLongPressCalled.countDown();
54        }
55
56        @Override
57        public boolean onSingleTapConfirmed(MotionEvent e) {
58            mLastSingleTap = e;
59            return true;
60        }
61
62        @Override
63        public boolean onSingleTapUp(MotionEvent e) {
64            mLastSingleTap = e;
65            return true;
66        }
67
68        @Override
69        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
70            return true;
71        }
72
73        @Override
74        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
75            mLastFling1 = e1;
76            return true;
77        }
78    }
79
80    static class MockGestureDetector extends GestureDetector {
81        MotionEvent mLastEvent;
82        public MockGestureDetector(Context context, OnGestureListener listener) {
83            super(context, listener);
84        }
85
86        @Override
87        public boolean onTouchEvent(MotionEvent ev) {
88            mLastEvent = MotionEvent.obtain(ev);
89            return super.onTouchEvent(ev);
90        }
91    }
92
93    static class MockMotionEventDelegate implements MotionEventDelegate {
94        private ContentViewGestureHandler mSynchronousConfirmTarget;
95        private int mSynchronousConfirmAckResult;
96
97        public int mLastTouchAction;
98        public int mLastGestureType;
99
100        @Override
101        public boolean sendTouchEvent(long timeMs, int action, TouchPoint[] pts) {
102            mLastTouchAction = action;
103            if (mSynchronousConfirmTarget != null) {
104                mSynchronousConfirmTarget.confirmTouchEvent(mSynchronousConfirmAckResult);
105            }
106            return true;
107        }
108
109        @Override
110        public boolean sendGesture(int type, long timeMs, int x, int y,
111                boolean lastInputEventForVSync, Bundle extraParams) {
112            Log.i(TAG,"Gesture event received with type id " + type);
113            mLastGestureType = type;
114            return true;
115        }
116
117        @Override
118        public boolean didUIStealScroll(float x, float y) {
119            // Not implemented.
120            return false;
121        }
122
123        @Override
124        public void invokeZoomPicker() {
125            // Not implemented.
126        }
127
128        @Override
129        public boolean hasFixedPageScale() {
130            return false;
131        }
132
133        public void enableSynchronousConfirmTouchEvent(
134                ContentViewGestureHandler handler, int ackResult) {
135            mSynchronousConfirmTarget = handler;
136            mSynchronousConfirmAckResult = ackResult;
137        }
138
139        public void disableSynchronousConfirmTouchEvent() {
140            mSynchronousConfirmTarget = null;
141        }
142    }
143
144    static class MockZoomManager extends ZoomManager {
145        MockZoomManager(Context context, ContentViewCore contentViewCore) {
146            super(context, contentViewCore);
147        }
148
149        @Override
150        public boolean processTouchEvent(MotionEvent event) {
151            return false;
152        }
153    }
154
155    static private MotionEvent motionEvent(int action, long downTime, long eventTime) {
156        return MotionEvent.obtain(downTime, eventTime, action, FAKE_COORD_X, FAKE_COORD_Y, 0);
157    }
158
159    @Override
160    public void setUp() {
161        mMockListener = new MockListener();
162        mMockGestureDetector = new MockGestureDetector(
163                getInstrumentation().getTargetContext(), mMockListener);
164        mMockMotionEventDelegate = new MockMotionEventDelegate();
165        mGestureHandler = new ContentViewGestureHandler(
166                getInstrumentation().getTargetContext(), mMockMotionEventDelegate,
167                new MockZoomManager(getInstrumentation().getTargetContext(), null),
168                ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC);
169        mLongPressDetector = new LongPressDetector(
170                getInstrumentation().getTargetContext(), mGestureHandler);
171        mGestureHandler.setTestDependencies(
172                mLongPressDetector, mMockGestureDetector, mMockListener);
173        TouchPoint.initializeConstantsForTesting();
174    }
175
176    /**
177     * Verify that a DOWN followed shortly by an UP will trigger a single tap.
178     *
179     * @throws Exception
180     */
181    @SmallTest
182    @Feature({"Gestures"})
183    public void testGestureSingleClick() throws Exception {
184        final long downTime = SystemClock.uptimeMillis();
185        final long eventTime = SystemClock.uptimeMillis();
186
187        MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
188
189        assertFalse(mGestureHandler.onTouchEvent(event));
190        assertTrue("Should have a pending gesture", mMockGestureDetector.mLastEvent != null);
191        assertTrue("Should have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
192
193        event = motionEvent(MotionEvent.ACTION_UP, downTime, eventTime + 10);
194        mLongPressDetector.cancelLongPressIfNeeded(event);
195        assertTrue("Should not have a pending LONG_PRESS", !mLongPressDetector.hasPendingMessage());
196        assertTrue(mGestureHandler.onTouchEvent(event));
197        // Synchronous, no need to wait.
198        assertTrue("Should have a single tap", mMockListener.mLastSingleTap != null);
199    }
200
201    /**
202     * Verify that when a touch event handler is registered the touch events are queued
203     * and sent in order.
204     * @throws Exception
205     */
206    @SmallTest
207    @Feature({"Gestures"})
208    public void testFlingOnTouchHandler() throws Exception {
209        final long downTime = SystemClock.uptimeMillis();
210        final long eventTime = SystemClock.uptimeMillis();
211
212        MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
213
214        mGestureHandler.hasTouchEventHandlers(true);
215
216        assertTrue(mGestureHandler.onTouchEvent(event));
217        assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
218        assertTrue("Should not have a pending gesture", mMockGestureDetector.mLastEvent == null);
219        assertFalse("Should not have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
220
221        event = MotionEvent.obtain(
222                downTime, eventTime + 5, MotionEvent.ACTION_MOVE,
223                FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
224        assertTrue(mGestureHandler.onTouchEvent(event));
225        assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
226
227        event = MotionEvent.obtain(
228                downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
229                FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
230        assertTrue(mGestureHandler.onTouchEvent(event));
231        assertEquals("We should have coalesced move events into one"
232                , 2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
233
234        event = MotionEvent.obtain(
235                downTime, eventTime + 15, MotionEvent.ACTION_UP,
236                FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
237        assertTrue(mGestureHandler.onTouchEvent(event));
238        assertEquals(3, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
239
240        mGestureHandler.confirmTouchEvent(
241                ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
242        assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
243        assertEquals(MotionEvent.ACTION_MOVE,
244                mGestureHandler.peekFirstInPendingMotionEventsForTesting().getActionMasked());
245        assertFalse("Pending LONG_PRESS should have been canceled",
246                mLongPressDetector.hasPendingMessage());
247
248        mGestureHandler.confirmTouchEvent(ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_CONSUMED);
249        assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
250        assertEquals(MotionEvent.ACTION_UP,
251                mGestureHandler.peekFirstInPendingMotionEventsForTesting().getActionMasked());
252
253        mGestureHandler.confirmTouchEvent(ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_CONSUMED);
254        assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
255
256        // Synchronous, no need to wait.
257        assertTrue("Should not have a fling", mMockListener.mLastFling1 == null);
258        assertTrue("Should not have a long press", mMockListener.mLastLongPress == null);
259    }
260
261    /**
262     * Verify the behavior of touch event timeout handler.
263     * @throws Exception
264     */
265    @SmallTest
266    @Feature({"Gestures"})
267    public void testTouchEventTimeoutHandler() throws Exception {
268        final long downTime = SystemClock.uptimeMillis();
269        final long eventTime = SystemClock.uptimeMillis();
270
271        mGestureHandler.hasTouchEventHandlers(true);
272
273        MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
274        assertTrue(mGestureHandler.onTouchEvent(event));
275        assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
276
277        // Queue a touch move event.
278        event = MotionEvent.obtain(
279                downTime, eventTime + 50, MotionEvent.ACTION_MOVE,
280                FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
281        assertTrue(mGestureHandler.onTouchEvent(event));
282        assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
283
284        mGestureHandler.mockTouchEventTimeout();
285        // On timeout, the pending queue should be cleared.
286        assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
287
288        // No new touch events should be sent to the touch handler before the timed-out event
289        // gets ACK'ed.
290        event = MotionEvent.obtain(
291                downTime, eventTime + 200, MotionEvent.ACTION_MOVE,
292                FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
293        mGestureHandler.onTouchEvent(event);
294        assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
295
296        // When the timed-out event gets ACK'ed, a cancel event should be sent.
297        mGestureHandler.confirmTouchEvent(ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_CONSUMED);
298        assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
299        assertEquals(TouchPoint.TOUCH_EVENT_TYPE_CANCEL, mMockMotionEventDelegate.mLastTouchAction);
300
301        // No new touch events should be sent to the touch handler before the cancel event
302        // gets ACK'ed.
303        event = MotionEvent.obtain(
304                downTime, eventTime + 300, MotionEvent.ACTION_UP,
305                FAKE_COORD_X * 20, FAKE_COORD_Y * 20, 0);
306        mGestureHandler.onTouchEvent(event);
307        assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
308
309        mGestureHandler.confirmTouchEvent(
310                ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
311
312        // After cancel event is ACK'ed, the handler should return to normal state.
313        event = MotionEvent.obtain(
314                downTime + 400, eventTime + 400, MotionEvent.ACTION_DOWN,
315                FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
316        assertTrue(mGestureHandler.onTouchEvent(event));
317        assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
318        mGestureHandler.confirmTouchEvent(ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_CONSUMED);
319        assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
320    }
321
322    /**
323     * Verify that after a touch event handlers starts handling a gesture, even though some event
324     * in the middle of the gesture returns with NOT_CONSUMED, we don't send that to the gesture
325     * detector to avoid falling to a faulty state.
326     * @throws Exception
327     */
328    @SmallTest
329    @Feature({"Gestures"})
330    public void testFlingOnTouchHandlerWithOneEventNotConsumed() throws Exception {
331        final long downTime = SystemClock.uptimeMillis();
332        final long eventTime = SystemClock.uptimeMillis();
333
334        MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
335
336        mGestureHandler.hasTouchEventHandlers(true);
337
338        assertTrue(mGestureHandler.onTouchEvent(event));
339        assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
340        assertTrue("Should not have a pending gesture", mMockGestureDetector.mLastEvent == null);
341        assertFalse("Should not have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
342
343        event = MotionEvent.obtain(
344                downTime, eventTime + 5, MotionEvent.ACTION_MOVE,
345                FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
346        assertTrue(mGestureHandler.onTouchEvent(event));
347        assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
348
349        event = MotionEvent.obtain(
350                downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
351                FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
352        assertTrue(mGestureHandler.onTouchEvent(event));
353        assertEquals("We should have coalesced move events into one"
354                , 2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
355
356        event = MotionEvent.obtain(
357                downTime, eventTime + 15, MotionEvent.ACTION_UP,
358                FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
359        assertTrue(mGestureHandler.onTouchEvent(event));
360        assertEquals(3, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
361
362        mGestureHandler.confirmTouchEvent(
363                ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_CONSUMED);
364        assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
365        assertEquals(MotionEvent.ACTION_MOVE,
366                mGestureHandler.peekFirstInPendingMotionEventsForTesting().getActionMasked());
367
368        mGestureHandler.confirmTouchEvent(
369                ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
370        assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
371        assertEquals(MotionEvent.ACTION_UP,
372                mGestureHandler.peekFirstInPendingMotionEventsForTesting().getActionMasked());
373        assertTrue("Even though the last event was not consumed by JavaScript," +
374                "it shouldn't have been sent to the Gesture Detector",
375                        mMockGestureDetector.mLastEvent == null);
376
377        mGestureHandler.confirmTouchEvent(ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_CONSUMED);
378        assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
379
380        // Synchronous, no need to wait.
381        assertTrue("Should not have a fling", mMockListener.mLastFling1 == null);
382        assertTrue("Should not have a long press", mMockListener.mLastLongPress == null);
383    }
384
385    /**
386     * Verify that when a registered touch event handler return NO_CONSUMER_EXISTS for down event
387     * all queue is drained until next down.
388     * @throws Exception
389     */
390    @SmallTest
391    @Feature({"Gestures"})
392    public void testDrainWithFlingAndClickOutofTouchHandler() throws Exception {
393        final long downTime = SystemClock.uptimeMillis();
394        final long eventTime = SystemClock.uptimeMillis();
395
396        mGestureHandler = new ContentViewGestureHandler(
397                getInstrumentation().getTargetContext(), new MockMotionEventDelegate(),
398                new MockZoomManager(getInstrumentation().getTargetContext(), null),
399                ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC);
400        mLongPressDetector = new LongPressDetector(
401                getInstrumentation().getTargetContext(), mGestureHandler);
402
403        mGestureHandler.hasTouchEventHandlers(true);
404
405        MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
406        assertTrue(mGestureHandler.onTouchEvent(event));
407        assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
408        assertFalse("Should not have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
409
410        event = MotionEvent.obtain(
411                downTime, eventTime + 5, MotionEvent.ACTION_MOVE,
412                FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
413        assertTrue(mGestureHandler.onTouchEvent(event));
414        assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
415
416        event = MotionEvent.obtain(
417                downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
418                FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
419        assertTrue(mGestureHandler.onTouchEvent(event));
420        assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
421
422        event = MotionEvent.obtain(
423                downTime, eventTime + 15, MotionEvent.ACTION_UP,
424                FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
425        assertTrue(mGestureHandler.onTouchEvent(event));
426        assertEquals(3, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
427
428        event = motionEvent(MotionEvent.ACTION_DOWN, eventTime + 20, eventTime + 20);
429        assertTrue(mGestureHandler.onTouchEvent(event));
430        assertEquals(4, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
431
432        event = MotionEvent.obtain(
433                downTime, eventTime + 20, MotionEvent.ACTION_UP,
434                FAKE_COORD_X, FAKE_COORD_Y, 0);
435        assertTrue(mGestureHandler.onTouchEvent(event));
436        assertEquals(5, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
437
438        mGestureHandler.confirmTouchEvent(
439                ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
440        assertEquals("The queue should have been drained until first down since no consumer exists",
441                2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
442        assertEquals(MotionEvent.ACTION_DOWN,
443                mGestureHandler.peekFirstInPendingMotionEventsForTesting().getActionMasked());
444
445        mGestureHandler.confirmTouchEvent(
446                ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
447        assertEquals("The queue should have been drained",
448                0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
449    }
450
451    /**
452     * Verify that when a touch event handler is registered the touch events stop getting queued
453     * after we received INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS.
454     * @throws Exception
455     */
456    @SmallTest
457    @Feature({"Gestures"})
458    public void testFlingOutOfTouchHandler() throws Exception {
459        final long downTime = SystemClock.uptimeMillis();
460        final long eventTime = SystemClock.uptimeMillis();
461
462        MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
463
464        mGestureHandler.hasTouchEventHandlers(true);
465
466        assertTrue(mGestureHandler.onTouchEvent(event));
467        assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
468        assertTrue("Should not have a pending gesture", mMockGestureDetector.mLastEvent == null);
469        assertFalse("Should not have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
470
471        mGestureHandler.confirmTouchEvent(
472                ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
473        assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
474        assertEquals("The down touch event should have been sent to the Gesture Detector",
475                event.getEventTime(), mMockGestureDetector.mLastEvent.getEventTime());
476        assertEquals("The down touch event should have been sent to the Gesture Detector",
477                MotionEvent.ACTION_DOWN, mMockGestureDetector.mLastEvent.getActionMasked());
478
479        event = MotionEvent.obtain(
480                downTime, eventTime + 5, MotionEvent.ACTION_MOVE,
481                FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
482        assertTrue(mGestureHandler.onTouchEvent(event));
483        assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
484        assertEquals("Motion events should be going to the Gesture Detector directly",
485                event.getEventTime(), mMockGestureDetector.mLastEvent.getEventTime());
486        assertEquals("Motion events should be going to the Gesture Detector directly",
487                MotionEvent.ACTION_MOVE, mMockGestureDetector.mLastEvent.getActionMasked());
488
489        event = MotionEvent.obtain(
490                downTime, eventTime + 10, MotionEvent.ACTION_UP,
491                FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
492        assertTrue(mGestureHandler.onTouchEvent(event));
493        assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
494        assertEquals("Motion events should be going to the Gesture Detector directly",
495                event.getEventTime(), mMockGestureDetector.mLastEvent.getEventTime());
496        assertEquals("Motion events should be going to the Gesture Detector directly",
497                MotionEvent.ACTION_UP, mMockGestureDetector.mLastEvent.getActionMasked());
498
499        // Synchronous, no need to wait.
500        assertTrue("Should have a fling", mMockListener.mLastFling1 != null);
501        assertTrue("Should not have a long press", mMockListener.mLastLongPress == null);
502    }
503
504    /**
505     * Verifies that a single tap doesn't cause a long press event to be sent.
506     * @throws Exception
507     */
508    @SmallTest
509    @Feature({"Gestures"})
510    public void testNoLongPressIsSentForSingleTapOutOfTouchHandler() throws Exception {
511        final long downTime = SystemClock.uptimeMillis();
512        final long eventTime = SystemClock.uptimeMillis();
513
514        MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
515
516        mGestureHandler.hasTouchEventHandlers(true);
517
518        assertTrue(mGestureHandler.onTouchEvent(event));
519        assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
520        assertTrue("Should not have a pending gesture", mMockGestureDetector.mLastEvent == null);
521        assertFalse("Should not have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
522
523        event = motionEvent(MotionEvent.ACTION_UP, downTime, eventTime + 5);
524        assertTrue(mGestureHandler.onTouchEvent(event));
525        assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
526
527        mGestureHandler.confirmTouchEvent(
528                ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
529
530        assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
531        assertEquals("The down touch event should have been sent to the Gesture Detector",
532                event.getDownTime(), mMockGestureDetector.mLastEvent.getEventTime());
533        assertEquals("The next event should be ACTION_UP",
534                MotionEvent.ACTION_UP,
535                mGestureHandler.peekFirstInPendingMotionEventsForTesting().getActionMasked());
536
537        mGestureHandler.confirmTouchEvent(
538                ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
539
540        assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
541        assertEquals("The up touch event should have been sent to the Gesture Detector",
542                event.getEventTime(), mMockGestureDetector.mLastEvent.getEventTime());
543
544        assertFalse("Should not have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
545    }
546
547    /**
548     * Verify that a DOWN followed by a MOVE will trigger fling (but not LONG).
549     * @throws Exception
550     */
551    @SmallTest
552    @Feature({"Gestures"})
553    public void testGestureFlingAndCancelLongClick() throws Exception {
554        final long downTime = SystemClock.uptimeMillis();
555        final long eventTime = SystemClock.uptimeMillis();
556
557        MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
558
559        assertFalse(mGestureHandler.onTouchEvent(event));
560        assertTrue("Should have a pending gesture", mMockGestureDetector.mLastEvent != null);
561        assertTrue("Should have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
562
563        event = MotionEvent.obtain(
564                downTime, eventTime + 5, MotionEvent.ACTION_MOVE,
565                FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
566        mLongPressDetector.cancelLongPressIfNeeded(event);
567        assertTrue("Should not have a pending LONG_PRESS", !mLongPressDetector.hasPendingMessage());
568        assertTrue(mGestureHandler.onTouchEvent(event));
569
570        event = MotionEvent.obtain(
571                downTime, eventTime + 10, MotionEvent.ACTION_UP,
572                FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
573        assertTrue(mGestureHandler.onTouchEvent(event));
574
575        // Synchronous, no need to wait.
576        assertTrue("Should have a fling", mMockListener.mLastFling1 != null);
577        assertTrue("Should not have a long press", mMockListener.mLastLongPress == null);
578    }
579
580    /**
581     * Verify that for a normal fling (fling after scroll) the following events are sent:
582     * - GESTURE_SCROLL_BEGIN
583     * - GESTURE_FLING_START
584     * and GESTURE_FLING_CANCEL is sent on the next touch.
585     * @throws Exception
586     */
587    @SmallTest
588    @Feature({"Gestures"})
589    public void testFlingEventSequence() throws Exception {
590        final long downTime = SystemClock.uptimeMillis();
591        final long eventTime = SystemClock.uptimeMillis();
592
593        GestureRecordingMotionEventDelegate mockDelegate =
594                new GestureRecordingMotionEventDelegate();
595        mGestureHandler = new ContentViewGestureHandler(
596                getInstrumentation().getTargetContext(), mockDelegate,
597                new MockZoomManager(getInstrumentation().getTargetContext(), null),
598                ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC);
599
600        MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
601
602        assertTrue(mGestureHandler.onTouchEvent(event));
603
604        event = MotionEvent.obtain(
605                downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
606                FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
607        assertTrue(mGestureHandler.onTouchEvent(event));
608        assertTrue(mGestureHandler.isNativeScrolling());
609        assertTrue("A scrollStart event should have been sent",
610                mockDelegate.mGestureTypeList.contains(
611                        ContentViewGestureHandler.GESTURE_SCROLL_START));
612        assertEquals("We should have started scrolling",
613                ContentViewGestureHandler.GESTURE_SCROLL_BY,
614                mockDelegate.mMostRecentGestureEvent.mType);
615        assertEquals("Only scrollBegin and scrollBy should have been sent",
616                2, mockDelegate.mGestureTypeList.size());
617
618        event = MotionEvent.obtain(
619                downTime, eventTime + 15, MotionEvent.ACTION_UP,
620                FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
621        assertTrue(mGestureHandler.onTouchEvent(event));
622        assertFalse(mGestureHandler.isNativeScrolling());
623        assertEquals("We should have started flinging",
624                ContentViewGestureHandler.GESTURE_FLING_START,
625                        mockDelegate.mMostRecentGestureEvent.mType);
626        assertTrue("A scroll end event should not have been sent",
627                !mockDelegate.mGestureTypeList.contains(
628                        ContentViewGestureHandler.GESTURE_SCROLL_END));
629        assertEquals("The last up should have caused flingStart to be sent",
630                3, mockDelegate.mGestureTypeList.size());
631
632        event = motionEvent(MotionEvent.ACTION_DOWN, downTime + 50, downTime + 50);
633        assertTrue(mGestureHandler.onTouchEvent(event));
634        assertTrue("A flingCancel should have been sent",
635                mockDelegate.mGestureTypeList.contains(
636                        ContentViewGestureHandler.GESTURE_FLING_CANCEL));
637        assertEquals("Only flingCancel should have been sent",
638                4, mockDelegate.mGestureTypeList.size());
639    }
640
641    /**
642     * Verify that a show pressed state gesture followed by a long press followed by
643     * the focus
644     * loss in the window due to context menu cancels show pressed.
645     * @throws Exception
646     */
647    @SmallTest
648    @Feature({"Gestures"})
649    public void testShowPressCancelOnWindowFocusLost() throws Exception {
650        final long time = SystemClock.uptimeMillis();
651        GestureRecordingMotionEventDelegate mockDelegate =
652                new GestureRecordingMotionEventDelegate();
653        mGestureHandler = new ContentViewGestureHandler(
654                getInstrumentation().getTargetContext(), mockDelegate,
655                new MockZoomManager(getInstrumentation().getTargetContext(), null),
656                ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC);
657        mLongPressDetector = new LongPressDetector(
658                getInstrumentation().getTargetContext(), mGestureHandler);
659        mGestureHandler.setTestDependencies(mLongPressDetector, null, null);
660
661        MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, time, time);
662        mGestureHandler.onTouchEvent(event);
663
664        mGestureHandler.sendShowPressedStateGestureForTesting();
665        assertEquals("A show pressed state event should have been sent",
666                ContentViewGestureHandler.GESTURE_SHOW_PRESSED_STATE,
667                        mockDelegate.mMostRecentGestureEvent.mType);
668        assertEquals("Only showPressedState should have been sent",
669                1, mockDelegate.mGestureTypeList.size());
670
671        mLongPressDetector.startLongPressTimerIfNeeded(event);
672        mLongPressDetector.sendLongPressGestureForTest();
673
674        assertEquals("Only should have sent only LONG_PRESS event",
675                2, mockDelegate.mGestureTypeList.size());
676        assertEquals("Should have a long press event next",
677                ContentViewGestureHandler.GESTURE_LONG_PRESS,
678                mockDelegate.mGestureTypeList.get(1).intValue());
679
680        // The long press triggers window focus loss by opening a context menu
681        mGestureHandler.onWindowFocusLost();
682
683        assertEquals("Only should have sent only GESTURE_SHOW_PRESS_CANCEL event",
684                3, mockDelegate.mGestureTypeList.size());
685        assertEquals("Should have a long press event next",
686                ContentViewGestureHandler.GESTURE_SHOW_PRESS_CANCEL,
687                mockDelegate.mGestureTypeList.get(2).intValue());
688    }
689
690    /**
691     * Verify that a recent show pressed state gesture is canceled when scrolling begins.
692     * @throws Exception
693     */
694    @SmallTest
695    @Feature({"Gestures"})
696    public void testShowPressCancelWhenScrollBegins() throws Exception {
697        final long downTime = SystemClock.uptimeMillis();
698        final long eventTime = SystemClock.uptimeMillis();
699
700        GestureRecordingMotionEventDelegate mockDelegate =
701                new GestureRecordingMotionEventDelegate();
702        mGestureHandler = new ContentViewGestureHandler(
703                getInstrumentation().getTargetContext(), mockDelegate,
704                new MockZoomManager(getInstrumentation().getTargetContext(), null),
705                ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC);
706        mLongPressDetector = new LongPressDetector(
707                getInstrumentation().getTargetContext(), mGestureHandler);
708
709        MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
710
711        assertTrue(mGestureHandler.onTouchEvent(event));
712        assertFalse("Should not have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
713
714        mGestureHandler.sendShowPressedStateGestureForTesting();
715
716        assertEquals("A show pressed state event should have been sent",
717                ContentViewGestureHandler.GESTURE_SHOW_PRESSED_STATE,
718                        mockDelegate.mMostRecentGestureEvent.mType);
719        assertEquals("Only showPressedState should have been sent",
720                1, mockDelegate.mGestureTypeList.size());
721
722        event = MotionEvent.obtain(
723                downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
724                FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
725        assertTrue(mGestureHandler.onTouchEvent(event));
726
727        assertEquals("We should have started scrolling",
728                ContentViewGestureHandler.GESTURE_SCROLL_BY,
729                        mockDelegate.mMostRecentGestureEvent.mType);
730        assertTrue("A show press cancel event should have been sent",
731                mockDelegate.mGestureTypeList.contains(
732                        ContentViewGestureHandler.GESTURE_SHOW_PRESS_CANCEL));
733        assertEquals("Only showPressedState, showPressCancel, scrollBegin and scrollBy" +
734                " should have been sent",
735                4, mockDelegate.mGestureTypeList.size());
736
737        event = MotionEvent.obtain(
738                downTime, eventTime + 15, MotionEvent.ACTION_UP,
739                FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
740        assertTrue(mGestureHandler.onTouchEvent(event));
741        assertEquals("We should have started flinging",
742                ContentViewGestureHandler.GESTURE_FLING_START,
743                        mockDelegate.mMostRecentGestureEvent.mType);
744        assertTrue("A scroll end event should not have been sent",
745                !mockDelegate.mGestureTypeList.contains(
746                        ContentViewGestureHandler.GESTURE_SCROLL_END));
747        assertEquals("The last up should have caused flingStart to be sent",
748                5, mockDelegate.mGestureTypeList.size());
749    }
750
751    /**
752     * Verify that double tap is correctly handled including the recent show pressed state gesture
753     * cancellation.
754     * @throws Exception
755     */
756    @SmallTest
757    @Feature({"Gestures"})
758    public void testDoubleTap() throws Exception {
759        final long downTime = SystemClock.uptimeMillis();
760        final long eventTime = SystemClock.uptimeMillis();
761
762        GestureRecordingMotionEventDelegate mockDelegate =
763                new GestureRecordingMotionEventDelegate();
764        mGestureHandler = new ContentViewGestureHandler(
765                getInstrumentation().getTargetContext(), mockDelegate,
766                new MockZoomManager(getInstrumentation().getTargetContext(), null),
767                ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC);
768        mLongPressDetector = new LongPressDetector(
769                getInstrumentation().getTargetContext(), mGestureHandler);
770
771        MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
772        assertTrue(mGestureHandler.onTouchEvent(event));
773        assertFalse("Should not have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
774
775        mGestureHandler.sendShowPressedStateGestureForTesting();
776        assertEquals("GESTURE_SHOW_PRESSED_STATE should have been sent",
777                ContentViewGestureHandler.GESTURE_SHOW_PRESSED_STATE,
778                mockDelegate.mMostRecentGestureEvent.mType);
779        assertEquals("Only GESTURE_SHOW_PRESSED_STATE should have been sent",
780                1, mockDelegate.mGestureTypeList.size());
781
782        event = MotionEvent.obtain(
783                downTime, eventTime + 5, MotionEvent.ACTION_UP,
784                FAKE_COORD_X, FAKE_COORD_Y, 0);
785        mGestureHandler.onTouchEvent(event);
786        assertEquals("A GESTURE_SINGLE_TAP_UNCONFIRMED event should have been sent",
787                ContentViewGestureHandler.GESTURE_SINGLE_TAP_UNCONFIRMED,
788                mockDelegate.mMostRecentGestureEvent.mType);
789        assertEquals("Only GESTURE_SHOW_PRESSED_STATE and " +
790                "GESTURE_SINGLE_TAP_UNCONFIRMED should have been sent",
791                2, mockDelegate.mGestureTypeList.size());
792
793        event = MotionEvent.obtain(
794                eventTime + 10, eventTime + 10, MotionEvent.ACTION_DOWN,
795                FAKE_COORD_X, FAKE_COORD_Y, 0);
796        assertTrue(mGestureHandler.onTouchEvent(event));
797        assertEquals("A GESTURE_SINGLE_TAP_UNCONFIRMED event should have been sent ",
798                ContentViewGestureHandler.GESTURE_SHOW_PRESS_CANCEL,
799                mockDelegate.mMostRecentGestureEvent.mType);
800        assertEquals("Only GESTURE_SHOW_PRESSED_STATE, " +
801                "GESTURE_SINGLE_TAP_UNCONFIRMED, and " +
802                "GESTURE_SHOW_PRESS_CANCEL should have been sent",
803                3, mockDelegate.mGestureTypeList.size());
804
805        // Moving a very small amount of distance should not trigger the double tap drag zoom mode.
806        event = MotionEvent.obtain(
807                eventTime + 10, eventTime + 10, MotionEvent.ACTION_MOVE,
808                FAKE_COORD_X, FAKE_COORD_Y + 1, 0);
809        assertTrue(mGestureHandler.onTouchEvent(event));
810        assertEquals("Only GESTURE_SHOW_PRESSED_STATE, " +
811                "GESTURE_SINGLE_TAP_UNCONFIRMED, and " +
812                "GESTURE_SHOW_PRESS_CANCEL should have been sent",
813                3, mockDelegate.mGestureTypeList.size());
814
815        event = MotionEvent.obtain(
816                eventTime + 10, eventTime + 15, MotionEvent.ACTION_UP,
817                FAKE_COORD_X, FAKE_COORD_Y + 1, 0);
818        assertTrue(mGestureHandler.onTouchEvent(event));
819        assertEquals("A double tap should have occurred",
820                ContentViewGestureHandler.GESTURE_DOUBLE_TAP,
821                mockDelegate.mMostRecentGestureEvent.mType);
822        assertEquals("Only GESTURE_SHOW_PRESSED_STATE, " +
823                "GESTURE_SINGLE_TAP_UNCONFIRMED, " +
824                "GESTURE_SHOW_PRESS_CANCEL, and " +
825                "GESTURE_DOUBLE_TAP should have been sent",
826                4, mockDelegate.mGestureTypeList.size());
827    }
828
829    /**
830     * Verify that double tap drag zoom feature is correctly executed.
831     * @throws Exception
832     */
833    @SmallTest
834    @Feature({"Gestures"})
835    public void testDoubleTapDragZoom() throws Exception {
836        final long downTime1 = SystemClock.uptimeMillis();
837        final long downTime2 = downTime1 + 100;
838
839        GestureRecordingMotionEventDelegate mockDelegate =
840                new GestureRecordingMotionEventDelegate();
841        mGestureHandler = new ContentViewGestureHandler(
842                getInstrumentation().getTargetContext(), mockDelegate,
843                new MockZoomManager(getInstrumentation().getTargetContext(), null),
844                ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC);
845        mLongPressDetector = new LongPressDetector(
846                getInstrumentation().getTargetContext(), mGestureHandler);
847
848        MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime1, downTime1);
849        assertTrue(mGestureHandler.onTouchEvent(event));
850        assertFalse("Should not have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
851
852        mGestureHandler.sendShowPressedStateGestureForTesting();
853        assertEquals("GESTURE_SHOW_PRESSED_STATE should have been sent",
854                ContentViewGestureHandler.GESTURE_SHOW_PRESSED_STATE,
855                        mockDelegate.mMostRecentGestureEvent.mType);
856        assertEquals("Only GESTURE_SHOW_PRESSED_STATE should have been sent",
857                1, mockDelegate.mGestureTypeList.size());
858
859        event = MotionEvent.obtain(
860                downTime1, downTime1 + 5, MotionEvent.ACTION_UP,
861                FAKE_COORD_X, FAKE_COORD_Y, 0);
862        mGestureHandler.onTouchEvent(event);
863        assertEquals("A GESTURE_SINGLE_TAP_UNCONFIRMED event should have been sent",
864                ContentViewGestureHandler.GESTURE_SINGLE_TAP_UNCONFIRMED,
865                mockDelegate.mMostRecentGestureEvent.mType);
866        assertEquals("Only GESTURE_SHOW_PRESSED_STATE and " +
867                "GESTURE_TAB_UNCONFIRMED " +
868                "should have been sent",
869                2, mockDelegate.mGestureTypeList.size());
870
871        event = MotionEvent.obtain(
872                downTime2, downTime2, MotionEvent.ACTION_DOWN,
873                FAKE_COORD_X, FAKE_COORD_Y, 0);
874        assertTrue(mGestureHandler.onTouchEvent(event));
875        assertEquals("GESTURE_SHOW_PRESS_CANCEL should have been sent",
876                ContentViewGestureHandler.GESTURE_SHOW_PRESS_CANCEL,
877                mockDelegate.mMostRecentGestureEvent.mType);
878        assertEquals("Only GESTURE_SHOW_PRESSED_STATE, " +
879                "GESTURE_TAB_UNCONFIRMED, and " +
880                "GESTURE_SHOW_PRESS_CANCEL should have been sent",
881                3, mockDelegate.mGestureTypeList.size());
882
883        event = MotionEvent.obtain(
884                downTime2, downTime2 + 5, MotionEvent.ACTION_MOVE,
885                FAKE_COORD_X, FAKE_COORD_Y + 100, 0);
886        assertTrue(mGestureHandler.onTouchEvent(event));
887        assertTrue("GESTURE_SCROLL_START should have been sent",
888                mockDelegate.mGestureTypeList.contains(
889                        ContentViewGestureHandler.GESTURE_SCROLL_START));
890        assertEquals("GESTURE_PINCH_BEGIN should have been sent",
891                ContentViewGestureHandler.GESTURE_PINCH_BEGIN,
892                mockDelegate.mMostRecentGestureEvent.mType);
893        assertEquals("Only GESTURE_SHOW_PRESSED_STATE, " +
894                "GESTURE_TAB_UNCONFIRMED," +
895                "GESTURE_SHOW_PRESS_CANCEL, " +
896                "GESTURE_SCROLL_START, and " +
897                "GESTURE_PINCH_BEGIN should have been sent",
898                5, mockDelegate.mGestureTypeList.size());
899
900        event = MotionEvent.obtain(
901                downTime2, downTime2 + 10, MotionEvent.ACTION_MOVE,
902                FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
903        assertTrue(mGestureHandler.onTouchEvent(event));
904        assertTrue("GESTURE_SCROLL_BY should have been sent",
905                mockDelegate.mGestureTypeList.contains(
906                        ContentViewGestureHandler.GESTURE_SCROLL_BY));
907        assertEquals("GESTURE_PINCH_BY should have been sent",
908                ContentViewGestureHandler.GESTURE_PINCH_BY,
909                mockDelegate.mMostRecentGestureEvent.mType);
910        assertEquals("Only GESTURE_SHOW_PRESSED_STATE, " +
911                "GESTURE_TAB_UNCONFIRMED," +
912                "GESTURE_SHOW_PRESS_CANCEL, " +
913                "GESTURE_SCROLL_START," +
914                "GESTURE_PINCH_BEGIN, " +
915                "GESTURE_SCROLL_BY, and " +
916                "GESTURE_PINCH_BY should have been sent",
917                7, mockDelegate.mGestureTypeList.size());
918
919        event = MotionEvent.obtain(
920                downTime2, downTime2 + 15, MotionEvent.ACTION_UP,
921                FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
922        assertTrue(mGestureHandler.onTouchEvent(event));
923        assertTrue("GESTURE_PINCH_END should have been sent",
924                mockDelegate.mGestureTypeList.contains(
925                        ContentViewGestureHandler.GESTURE_PINCH_END));
926        assertEquals("GESTURE_SCROLL_END should have been sent",
927                ContentViewGestureHandler.GESTURE_SCROLL_END,
928                mockDelegate.mMostRecentGestureEvent.mType);
929        assertEquals("Only GESTURE_SHOW_PRESSED_STATE, " +
930                "GESTURE_TAB_UNCONFIRMED," +
931                "GESTURE_SHOW_PRESS_CANCEL, " +
932                "GESTURE_SCROLL_START," +
933                "GESTURE_PINCH_BEGIN, " +
934                "GESTURE_SCROLL_BY," +
935                "GESTURE_PINCH_BY, " +
936                "GESTURE_PINCH_END, and " +
937                "GESTURE_SCROLL_END should have been sent",
938                9, mockDelegate.mGestureTypeList.size());
939    }
940
941    /**
942     * Verify that double tap drag zoom feature is not invoked
943     * when it is disabled..
944     * @throws Exception
945     */
946    @SmallTest
947    @Feature({"Gestures"})
948    public void testDoubleTapDragZoomNothingWhenDisabled() throws Exception {
949        final long downTime1 = SystemClock.uptimeMillis();
950        final long downTime2 = downTime1 + 100;
951
952        GestureRecordingMotionEventDelegate mockDelegate =
953                new GestureRecordingMotionEventDelegate();
954        mGestureHandler = new ContentViewGestureHandler(
955                getInstrumentation().getTargetContext(), mockDelegate,
956                new MockZoomManager(getInstrumentation().getTargetContext(), null),
957                ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC);
958
959        mGestureHandler.updateDoubleTapDragSupport(false);
960
961        MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime1, downTime1);
962        assertTrue(mGestureHandler.onTouchEvent(event));
963
964        event = MotionEvent.obtain(
965                downTime1, downTime1 + 5, MotionEvent.ACTION_UP,
966                FAKE_COORD_X, FAKE_COORD_Y, 0);
967        mGestureHandler.onTouchEvent(event);
968
969        event = MotionEvent.obtain(
970                downTime2, downTime2, MotionEvent.ACTION_DOWN,
971                FAKE_COORD_X, FAKE_COORD_Y, 0);
972        assertTrue(mGestureHandler.onTouchEvent(event));
973
974        event = MotionEvent.obtain(
975                downTime2, downTime2 + 5, MotionEvent.ACTION_MOVE,
976                FAKE_COORD_X, FAKE_COORD_Y + 100, 0);
977        // As double tap and drag to zoom is disabled, we won't handle
978        // the move event.
979        assertFalse(mGestureHandler.onTouchEvent(event));
980
981        assertFalse("No GESTURE_SCROLL_START should have been sent",
982                mockDelegate.mGestureTypeList.contains(
983                        ContentViewGestureHandler.GESTURE_SCROLL_START));
984        assertTrue("No GESTURE_PINCH_BEGIN should have been sent",
985                ContentViewGestureHandler.GESTURE_PINCH_BEGIN !=
986                mockDelegate.mMostRecentGestureEvent.mType);
987
988        event = MotionEvent.obtain(
989                downTime2, downTime2 + 10, MotionEvent.ACTION_MOVE,
990                FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
991        assertFalse(mGestureHandler.onTouchEvent(event));
992        assertFalse("No GESTURE_SCROLL_BY should have been sent",
993                mockDelegate.mGestureTypeList.contains(
994                        ContentViewGestureHandler.GESTURE_SCROLL_BY));
995        assertTrue("No GESTURE_PINCH_BY should have been sent",
996                ContentViewGestureHandler.GESTURE_PINCH_BY !=
997                mockDelegate.mMostRecentGestureEvent.mType);
998
999        event = MotionEvent.obtain(
1000                downTime2, downTime2 + 15, MotionEvent.ACTION_UP,
1001                FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
1002        assertFalse(mGestureHandler.onTouchEvent(event));
1003        assertFalse("No GESTURE_PINCH_END should have been sent",
1004                mockDelegate.mGestureTypeList.contains(
1005                        ContentViewGestureHandler.GESTURE_PINCH_END));
1006    }
1007
1008    /**
1009     * Mock MotionEventDelegate that remembers the most recent gesture event.
1010     */
1011    static class GestureRecordingMotionEventDelegate implements MotionEventDelegate {
1012        static class GestureEvent {
1013            private final int mType;
1014            private final long mTimeMs;
1015            private final int mX;
1016            private final int mY;
1017            private final Bundle mExtraParams;
1018
1019            public GestureEvent(int type, long timeMs, int x, int y, Bundle extraParams) {
1020                mType = type;
1021                mTimeMs = timeMs;
1022                mX = x;
1023                mY = y;
1024                mExtraParams = extraParams;
1025            }
1026
1027            public int getType() {
1028                return mType;
1029            }
1030
1031            public long getTimeMs() {
1032                return mTimeMs;
1033            }
1034
1035            public int getX() {
1036                return mX;
1037            }
1038
1039            public int getY() {
1040                return mY;
1041            }
1042
1043            public Bundle getExtraParams() {
1044                return mExtraParams;
1045            }
1046        };
1047        private GestureEvent mMostRecentGestureEvent;
1048        private boolean mMostRecentGestureEventWasLastForVSync;
1049        private final ArrayList<Integer> mGestureTypeList = new ArrayList<Integer>();
1050
1051        @Override
1052        public boolean sendTouchEvent(long timeMs, int action, TouchPoint[] pts) {
1053            // Not implemented.
1054            return false;
1055        }
1056
1057        @Override
1058        public boolean sendGesture(int type, long timeMs, int x, int y,
1059                boolean lastInputEventForVSync, Bundle extraParams) {
1060            mMostRecentGestureEvent = new GestureEvent(type, timeMs, x, y, extraParams);
1061            mMostRecentGestureEventWasLastForVSync = lastInputEventForVSync;
1062            mGestureTypeList.add(mMostRecentGestureEvent.mType);
1063            return true;
1064        }
1065
1066        @Override
1067        public boolean didUIStealScroll(float x, float y) {
1068            // Not implemented.
1069            return false;
1070        }
1071
1072        @Override
1073        public void invokeZoomPicker() {
1074            // Not implemented.
1075        }
1076
1077        @Override
1078        public boolean hasFixedPageScale() {
1079            return false;
1080        }
1081
1082        public GestureEvent getMostRecentGestureEvent() {
1083            return mMostRecentGestureEvent;
1084        }
1085
1086        public boolean mostRecentGestureEventForLastForVSync() {
1087            return mMostRecentGestureEventWasLastForVSync;
1088        }
1089    }
1090
1091    /**
1092     * Verify that the first event sent while the page is scrolling will be
1093     * converted to a touchcancel. The touchcancel event should stay in the
1094     * pending queue. Acking the touchcancel event will consume all the touch
1095     * events of the current session.
1096     */
1097    @SmallTest
1098    @Feature({"Gestures"})
1099    public void testTouchEventsCanceledWhileScrolling() {
1100        final int deltaY = 84;
1101        final long downTime = SystemClock.uptimeMillis();
1102
1103        MockMotionEventDelegate delegate = new MockMotionEventDelegate();
1104        ContentViewGestureHandler gestureHandler = new ContentViewGestureHandler(
1105                getInstrumentation().getTargetContext(), delegate,
1106                new MockZoomManager(getInstrumentation().getTargetContext(), null),
1107                ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC);
1108        gestureHandler.hasTouchEventHandlers(true);
1109        MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
1110        assertTrue(gestureHandler.onTouchEvent(event));
1111        gestureHandler.confirmTouchEvent(
1112                ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
1113
1114        event = MotionEvent.obtain(
1115                downTime, downTime + 5, MotionEvent.ACTION_MOVE,
1116                FAKE_COORD_X, FAKE_COORD_Y - deltaY / 2, 0);
1117        assertTrue(gestureHandler.onTouchEvent(event));
1118        gestureHandler.confirmTouchEvent(
1119                ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
1120
1121        // This event will be converted to touchcancel and put into the pending
1122        // queue.
1123        event = MotionEvent.obtain(
1124                downTime, downTime + 10, MotionEvent.ACTION_MOVE,
1125                FAKE_COORD_X, FAKE_COORD_Y - deltaY, 0);
1126        assertTrue(gestureHandler.onTouchEvent(event));
1127        assertEquals(1, gestureHandler.getNumberOfPendingMotionEventsForTesting());
1128        assertTrue(gestureHandler.isEventCancelledForTesting(
1129                gestureHandler.peekFirstInPendingMotionEventsForTesting()));
1130        MotionEvent canceledEvent = gestureHandler.peekFirstInPendingMotionEventsForTesting();
1131
1132        event = motionEvent(MotionEvent.ACTION_POINTER_DOWN, downTime + 15, downTime + 15);
1133        assertTrue(gestureHandler.onTouchEvent(event));
1134        assertEquals(2, gestureHandler.getNumberOfPendingMotionEventsForTesting());
1135
1136        // Acking the touchcancel will drain all the events, and clear the event previously marked
1137        // for cancellation.
1138        gestureHandler.confirmTouchEvent(
1139                ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
1140        assertEquals(0, gestureHandler.getNumberOfPendingMotionEventsForTesting());
1141        assertFalse(gestureHandler.isEventCancelledForTesting(canceledEvent));
1142
1143        // Note: This check relies on an implementation detail of MotionEvent, namely, that the last
1144        //       event recycled is the the first returned by MotionEvent.obtain(). Should
1145        //       MotioEvent's implementation change, this assumption will need to be rebased.
1146        MotionEvent recycledCanceledEvent =
1147                motionEvent(MotionEvent.ACTION_DOWN, downTime + 20 , downTime + 20);
1148        assertSame("The canceled event should have been recycled.",
1149                canceledEvent, recycledCanceledEvent);
1150    }
1151
1152    /**
1153     * Generate a scroll gesture and verify that the resulting scroll motion event has both absolute
1154     * and relative position information.
1155     */
1156    @SmallTest
1157    @Feature({"Gestures"})
1158    public void testScrollUpdateCoordinates() {
1159        final int deltaX = 16;
1160        final int deltaY = 84;
1161        final long downTime = SystemClock.uptimeMillis();
1162
1163        GestureRecordingMotionEventDelegate delegate = new GestureRecordingMotionEventDelegate();
1164        ContentViewGestureHandler gestureHandler = new ContentViewGestureHandler(
1165                getInstrumentation().getTargetContext(), delegate,
1166                new MockZoomManager(getInstrumentation().getTargetContext(), null),
1167                ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC);
1168        MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
1169        assertTrue(gestureHandler.onTouchEvent(event));
1170
1171        // Move twice, because the first move gesture is discarded.
1172        event = MotionEvent.obtain(
1173                downTime, downTime + 5, MotionEvent.ACTION_MOVE,
1174                FAKE_COORD_X - deltaX / 2, FAKE_COORD_Y - deltaY / 2, 0);
1175        assertTrue(gestureHandler.onTouchEvent(event));
1176
1177        event = MotionEvent.obtain(
1178                downTime, downTime + 10, MotionEvent.ACTION_MOVE,
1179                FAKE_COORD_X - deltaX, FAKE_COORD_Y - deltaY, 0);
1180        assertTrue(gestureHandler.onTouchEvent(event));
1181
1182        // Make sure the reported gesture event has all the expected data.
1183        GestureRecordingMotionEventDelegate.GestureEvent gestureEvent =
1184                delegate.getMostRecentGestureEvent();
1185        assertNotNull(gestureEvent);
1186        assertEquals(ContentViewGestureHandler.GESTURE_SCROLL_BY, gestureEvent.getType());
1187        assertEquals(downTime + 10, gestureEvent.getTimeMs());
1188        assertEquals(FAKE_COORD_X - deltaX, gestureEvent.getX());
1189        assertEquals(FAKE_COORD_Y - deltaY, gestureEvent.getY());
1190
1191        Bundle extraParams = gestureEvent.getExtraParams();
1192        assertNotNull(extraParams);
1193        // No horizontal delta because of snapping.
1194        assertEquals(0, extraParams.getInt(ContentViewGestureHandler.DISTANCE_X));
1195        assertEquals(deltaY / 2, extraParams.getInt(ContentViewGestureHandler.DISTANCE_Y));
1196    }
1197
1198    /**
1199     * Verify that the timer of LONG_PRESS will be cancelled when scrolling begins so
1200     * LONG_PRESS and LONG_TAP won't be triggered.
1201     *
1202     * @throws Exception
1203     */
1204    @SmallTest
1205    @Feature({"Gestures"})
1206    public void testLongPressAndTapCancelWhenScrollBegins() throws Exception {
1207        final long downTime = SystemClock.uptimeMillis();
1208        final long eventTime = SystemClock.uptimeMillis();
1209
1210        GestureRecordingMotionEventDelegate mockDelegate =
1211                new GestureRecordingMotionEventDelegate();
1212        mGestureHandler = new ContentViewGestureHandler(
1213                getInstrumentation().getTargetContext(), mockDelegate,
1214                new MockZoomManager(getInstrumentation().getTargetContext(), null),
1215                ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC);
1216        mLongPressDetector = mGestureHandler.getLongPressDetector();
1217
1218        MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
1219        assertTrue(mGestureHandler.onTouchEvent(event));
1220        assertTrue("Should have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
1221        event = MotionEvent.obtain(
1222                downTime, eventTime + 5, MotionEvent.ACTION_MOVE,
1223                FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
1224        assertTrue(mGestureHandler.onTouchEvent(event));
1225        event = MotionEvent.obtain(
1226                downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
1227                FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
1228        assertTrue(mGestureHandler.onTouchEvent(event));
1229        assertTrue("Should not have a pending LONG_PRESS", !mLongPressDetector.hasPendingMessage());
1230
1231        // No LONG_TAP because LONG_PRESS timer is cancelled.
1232        assertFalse("No LONG_PRESS should be sent",
1233                mockDelegate.mGestureTypeList.contains(
1234                        ContentViewGestureHandler.GESTURE_LONG_PRESS));
1235        assertFalse("No LONG_TAP should be sent",
1236                mockDelegate.mGestureTypeList.contains(
1237                        ContentViewGestureHandler.GESTURE_LONG_TAP));
1238    }
1239
1240    /**
1241     * Verifies that when hasTouchEventHandlers changes while in a gesture, that the pending
1242     * queue does not grow continually.
1243     */
1244    @SmallTest
1245    @Feature({"Gestures"})
1246    public void testHasTouchEventHandlersChangesInGesture() {
1247        final long downTime = SystemClock.uptimeMillis();
1248        final long eventTime = SystemClock.uptimeMillis();
1249
1250        MockMotionEventDelegate eventDelegate = new MockMotionEventDelegate() {
1251            @Override
1252            public boolean sendTouchEvent(long timeMs, int action, TouchPoint[] pts) {
1253                if (action == TouchPoint.TOUCH_EVENT_TYPE_CANCEL) {
1254                    // Ensure the event to be cancelled is already in the pending queue.
1255                    MotionEvent canceledEvent =
1256                            mGestureHandler.peekFirstInPendingMotionEventsForTesting();
1257                    assertTrue(mGestureHandler.isEventCancelledForTesting(canceledEvent));
1258
1259                    mGestureHandler.confirmTouchEvent(
1260                            ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
1261
1262                    // Ensure that the canceled event has been removed from the event queue and is
1263                    // no longer scheduled for cancellation.
1264                    assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1265                    assertFalse(mGestureHandler.isEventCancelledForTesting(canceledEvent));
1266                }
1267                return super.sendTouchEvent(timeMs, action, pts);
1268            }
1269        };
1270        mGestureHandler = new ContentViewGestureHandler(
1271                getInstrumentation().getTargetContext(), eventDelegate,
1272                new MockZoomManager(getInstrumentation().getTargetContext(), null),
1273                ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC);
1274        mLongPressDetector = new LongPressDetector(
1275                getInstrumentation().getTargetContext(), mGestureHandler);
1276
1277        MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
1278        assertTrue(mGestureHandler.onTouchEvent(event));
1279        assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1280        assertFalse("Should not have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
1281
1282        event = MotionEvent.obtain(
1283                downTime, eventTime + 5, MotionEvent.ACTION_MOVE,
1284                FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
1285        assertTrue(mGestureHandler.onTouchEvent(event));
1286        assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1287
1288        mGestureHandler.hasTouchEventHandlers(true);
1289
1290        event = MotionEvent.obtain(
1291                downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
1292                FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
1293        assertTrue(mGestureHandler.onTouchEvent(event));
1294        assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1295
1296        event = MotionEvent.obtain(
1297                downTime, eventTime + 15, MotionEvent.ACTION_MOVE,
1298                FAKE_COORD_X * 15, FAKE_COORD_Y * 15, 0);
1299        assertTrue(mGestureHandler.onTouchEvent(event));
1300        assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1301    }
1302
1303    /**
1304     * Verify that LONG_TAP is triggered after LongPress followed by an UP.
1305     *
1306     * @throws Exception
1307     */
1308    @SmallTest
1309    @Feature({"Gestures"})
1310    public void testGestureLongTap() throws Exception {
1311        final long downTime = SystemClock.uptimeMillis();
1312        final long eventTime = SystemClock.uptimeMillis();
1313
1314        GestureRecordingMotionEventDelegate mockDelegate =
1315                new GestureRecordingMotionEventDelegate();
1316        mGestureHandler = new ContentViewGestureHandler(
1317                getInstrumentation().getTargetContext(), mockDelegate,
1318                new MockZoomManager(getInstrumentation().getTargetContext(), null),
1319                ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC);
1320        mLongPressDetector = mGestureHandler.getLongPressDetector();
1321
1322        MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
1323        assertTrue(mGestureHandler.onTouchEvent(event));
1324        assertTrue("Should have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
1325
1326        mLongPressDetector.sendLongPressGestureForTest();
1327
1328        assertEquals("A LONG_PRESS gesture should have been sent",
1329                ContentViewGestureHandler.GESTURE_LONG_PRESS,
1330                        mockDelegate.mMostRecentGestureEvent.mType);
1331
1332        event = motionEvent(MotionEvent.ACTION_UP, downTime, eventTime + 1000);
1333        assertTrue(mGestureHandler.onTouchEvent(event));
1334        assertEquals("A LONG_TAP gesture should have been sent",
1335                ContentViewGestureHandler.GESTURE_LONG_TAP,
1336                        mockDelegate.mMostRecentGestureEvent.mType);
1337    }
1338
1339    /**
1340     * Verify that the touch slop region is removed from the first scroll delta to avoid a jump when
1341     * starting to scroll.
1342     * @throws Exception
1343     */
1344    @SmallTest
1345    @Feature({"Gestures"})
1346    public void testTouchSlopRemovedFromScroll() throws Exception {
1347        Context context = getInstrumentation().getTargetContext();
1348        final long downTime = SystemClock.uptimeMillis();
1349        final long eventTime = SystemClock.uptimeMillis();
1350        final int scaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
1351        final int scrollDelta = 5;
1352
1353        GestureRecordingMotionEventDelegate mockDelegate =
1354                new GestureRecordingMotionEventDelegate();
1355        mGestureHandler = new ContentViewGestureHandler(
1356                context, mockDelegate,
1357                new MockZoomManager(context, null),
1358                ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC);
1359
1360        MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
1361        assertTrue(mGestureHandler.onTouchEvent(event));
1362
1363        event = MotionEvent.obtain(
1364                downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
1365                FAKE_COORD_X, FAKE_COORD_Y + scaledTouchSlop + scrollDelta, 0);
1366        assertTrue(mGestureHandler.onTouchEvent(event));
1367
1368        assertEquals("We should have started scrolling",
1369                ContentViewGestureHandler.GESTURE_SCROLL_BY,
1370                mockDelegate.mMostRecentGestureEvent.mType);
1371
1372        GestureRecordingMotionEventDelegate.GestureEvent gestureEvent =
1373                mockDelegate.getMostRecentGestureEvent();
1374        assertNotNull(gestureEvent);
1375        Bundle extraParams = gestureEvent.getExtraParams();
1376        assertEquals(0, extraParams.getInt(ContentViewGestureHandler.DISTANCE_X));
1377        assertEquals(-scrollDelta, extraParams.getInt(ContentViewGestureHandler.DISTANCE_Y));
1378    }
1379
1380    static private void sendLastScrollByEvent(ContentViewGestureHandler handler) {
1381        final long downTime = SystemClock.uptimeMillis();
1382        final long eventTime = SystemClock.uptimeMillis();
1383        MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
1384        assertTrue(handler.onTouchEvent(event));
1385        event = MotionEvent.obtain(
1386                downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
1387                FAKE_COORD_X, FAKE_COORD_Y + 30, 0);
1388        assertTrue(handler.onTouchEvent(event));
1389    }
1390
1391    static private void sendLastPinchEvent(ContentViewGestureHandler handler) {
1392        final long downTime = SystemClock.uptimeMillis();
1393        final long eventTime = SystemClock.uptimeMillis();
1394        handler.pinchBegin(downTime, FAKE_COORD_X, FAKE_COORD_Y);
1395        handler.pinchBy(eventTime + 10, FAKE_COORD_X, FAKE_COORD_Y, 2);
1396    }
1397
1398    /**
1399     * Verify that certain gesture events are sent with the "last for this vsync" flag set.
1400     * @throws Exception
1401     */
1402    @SmallTest
1403    @Feature({"Gestures"})
1404    public void testFinalInputEventsForVSyncInterval() throws Exception {
1405        Context context = getInstrumentation().getTargetContext();
1406        final boolean inputEventsDeliveredAtVSync =
1407                Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
1408
1409        GestureRecordingMotionEventDelegate mockDelegate =
1410                new GestureRecordingMotionEventDelegate();
1411        mGestureHandler = new ContentViewGestureHandler(
1412                context, mockDelegate,
1413                new MockZoomManager(context, null),
1414                inputEventsDeliveredAtVSync ? ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC :
1415                                              ContentViewCore.INPUT_EVENTS_DELIVERED_IMMEDIATELY);
1416
1417        sendLastScrollByEvent(mGestureHandler);
1418        assertEquals("We should have started scrolling",
1419                ContentViewGestureHandler.GESTURE_SCROLL_BY,
1420                mockDelegate.mMostRecentGestureEvent.mType);
1421        if (inputEventsDeliveredAtVSync) {
1422            assertEquals("Gesture should be last for vsync",
1423                    true,
1424                    mockDelegate.mostRecentGestureEventForLastForVSync());
1425        } else {
1426            assertEquals("Gesture should not be last for vsync",
1427                    false,
1428                    mockDelegate.mostRecentGestureEventForLastForVSync());
1429        }
1430
1431        sendLastPinchEvent(mGestureHandler);
1432        assertEquals("We should have started pinch-zooming",
1433                ContentViewGestureHandler.GESTURE_PINCH_BY,
1434                mockDelegate.mMostRecentGestureEvent.mType);
1435        if (inputEventsDeliveredAtVSync) {
1436            assertEquals("Gesture should be last for vsync",
1437                    true,
1438                    mockDelegate.mostRecentGestureEventForLastForVSync());
1439        } else {
1440            assertEquals("Gesture should not be last for vsync",
1441                    false,
1442                    mockDelegate.mostRecentGestureEventForLastForVSync());
1443        }
1444    }
1445
1446    /**
1447     * Verify that no gesture is set with "last for this vsync" flag if vsync is not enabled for
1448     * gesture handler
1449     * @throws Exception
1450     */
1451    @SmallTest
1452    @Feature({"Gestures"})
1453    public void testFinalInputEventsForVSyncIntervalWithVsyncDisabled() throws Exception {
1454        Context context = getInstrumentation().getTargetContext();
1455        final boolean inputEventsDeliveredAtVSync =
1456                Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
1457
1458        // Nothing to test on OS version that does not have input batched before vsync.
1459        if (!inputEventsDeliveredAtVSync) {
1460            return;
1461        }
1462
1463        GestureRecordingMotionEventDelegate mockDelegate =
1464                new GestureRecordingMotionEventDelegate();
1465        mGestureHandler = new ContentViewGestureHandler(
1466                context, mockDelegate,
1467                new MockZoomManager(context, null),
1468                ContentViewCore.INPUT_EVENTS_DELIVERED_IMMEDIATELY);
1469
1470        sendLastScrollByEvent(mGestureHandler);
1471        assertEquals("Gesture should not be last for vsync",
1472                false,
1473                mockDelegate.mostRecentGestureEventForLastForVSync());
1474
1475        sendLastPinchEvent(mGestureHandler);
1476        assertEquals("Gesture should not be last for vsync",
1477                false,
1478                mockDelegate.mostRecentGestureEventForLastForVSync());
1479    }
1480
1481    /**
1482     * Verify that a DOWN followed shortly by an UP will trigger
1483     * a GESTURE_SINGLE_TAP_UNCONFIRMED event immediately.
1484     *
1485     * @throws Exception
1486     */
1487    @SmallTest
1488    @Feature({"Gestures"})
1489    public void testGestureEventsSingleTapUnconfirmed() throws Exception {
1490        final long downTime = SystemClock.uptimeMillis();
1491        final long eventTime = SystemClock.uptimeMillis();
1492
1493        GestureRecordingMotionEventDelegate mockDelegate =
1494                new GestureRecordingMotionEventDelegate();
1495        mGestureHandler = new ContentViewGestureHandler(
1496                getInstrumentation().getTargetContext(), mockDelegate,
1497                new MockZoomManager(getInstrumentation().getTargetContext(), null),
1498                ContentViewCore.INPUT_EVENTS_DELIVERED_IMMEDIATELY);
1499
1500        MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
1501        assertTrue(mGestureHandler.onTouchEvent(event));
1502        assertNull(mockDelegate.getMostRecentGestureEvent());
1503
1504        event = motionEvent(MotionEvent.ACTION_UP, downTime, eventTime + 10);
1505        assertFalse(mGestureHandler.onTouchEvent(event));
1506        assertEquals("A GESTURE_SINGLE_TAP_UNCONFIRMED gesture should have been sent",
1507                ContentViewGestureHandler.GESTURE_SINGLE_TAP_UNCONFIRMED,
1508                        mockDelegate.mMostRecentGestureEvent.mType);
1509
1510        assertTrue("Should not have confirmed a single tap yet",
1511                mMockListener.mLastSingleTap == null);
1512    }
1513
1514    /**
1515     * Verify that touch move events are properly coalesced.
1516     * @throws Exception
1517     */
1518    @SmallTest
1519    @Feature({"Gestures"})
1520    public void testTouchMoveCoalescing() throws Exception {
1521        final long downTime = SystemClock.uptimeMillis();
1522        final long eventTime = SystemClock.uptimeMillis();
1523
1524        mGestureHandler.hasTouchEventHandlers(true);
1525
1526        MotionEvent event = MotionEvent.obtain(
1527                downTime, eventTime + 5, MotionEvent.ACTION_MOVE,
1528                FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
1529        assertTrue(mGestureHandler.onTouchEvent(event));
1530        assertFalse("Should not have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
1531        assertEquals("Initial move events should offered to javascript and added to the queue",
1532                1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1533        assertEquals(TouchPoint.TOUCH_EVENT_TYPE_MOVE, mMockMotionEventDelegate.mLastTouchAction);
1534
1535        event = MotionEvent.obtain(
1536                downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
1537                FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
1538        assertTrue(mGestureHandler.onTouchEvent(event));
1539        assertEquals("Move events already sent to javascript should not be coalesced",
1540                2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1541
1542        event = MotionEvent.obtain(
1543                downTime, eventTime + 15, MotionEvent.ACTION_MOVE,
1544                FAKE_COORD_X * 15, FAKE_COORD_Y * 15, 0);
1545        assertTrue(mGestureHandler.onTouchEvent(event));
1546        assertEquals("Similar pending move events should be coalesced",
1547                2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1548
1549        PointerProperties pp1 = new PointerProperties();
1550        pp1.id = 0;
1551        pp1.toolType = MotionEvent.TOOL_TYPE_FINGER;
1552        PointerProperties pp2 = new PointerProperties();
1553        pp2.id = 1;
1554        pp2.toolType = MotionEvent.TOOL_TYPE_FINGER;
1555        PointerProperties[] properties = new PointerProperties[] { pp1, pp2 };
1556
1557        PointerCoords pc1 = new PointerCoords();
1558        pc1.x = FAKE_COORD_X * 10;
1559        pc1.y = FAKE_COORD_Y * 10;
1560        pc1.pressure = 1;
1561        pc1.size = 1;
1562        PointerCoords pc2 = new PointerCoords();
1563        pc2.x = FAKE_COORD_X * 15;
1564        pc2.y = FAKE_COORD_Y * 15;
1565        pc2.pressure = 1;
1566        pc2.size = 1;
1567        PointerCoords[] coords = new PointerCoords[] { pc1, pc2 };
1568
1569        event = MotionEvent.obtain(
1570                downTime, eventTime + 20, MotionEvent.ACTION_MOVE,
1571                2, properties, coords, 0, 0, 1.0f, 1.0f, 0, 0, 0, 0);
1572        assertTrue(mGestureHandler.onTouchEvent(event));
1573        assertEquals("Move events with different pointer counts should not be coalesced",
1574                3, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1575
1576        event = MotionEvent.obtain(
1577                downTime, eventTime + 25, MotionEvent.ACTION_MOVE,
1578                2, properties, coords, 0, 0, 1.0f, 1.0f, 0, 0, 0, 0);
1579        assertTrue(mGestureHandler.onTouchEvent(event));
1580        assertEquals("Move events with similar pointer counts should be coalesced",
1581                3, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1582
1583        event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
1584        assertTrue(mGestureHandler.onTouchEvent(event));
1585        assertEquals("Move events should not be coalesced with other events",
1586                4, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1587    }
1588
1589    /**
1590     * Verify that synchronous confirmTouchEvent() calls made from the MotionEventDelegate behave
1591     * properly.
1592     * @throws Exception
1593     */
1594    @SmallTest
1595    @Feature({"Gestures"})
1596    public void testSynchronousConfirmTouchEvent() throws Exception {
1597        final long downTime = SystemClock.uptimeMillis();
1598        final long eventTime = SystemClock.uptimeMillis();
1599
1600        mGestureHandler.hasTouchEventHandlers(true);
1601
1602        mMockMotionEventDelegate.disableSynchronousConfirmTouchEvent();
1603
1604        // Queue an asynchronously handled event; this should schedule a touch timeout.
1605        MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
1606        assertTrue(mGestureHandler.onTouchEvent(event));
1607        assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1608        assertTrue(mGestureHandler.hasScheduledTouchTimeoutEventForTesting());
1609
1610        // Queue another event; this will remain in the queue until the first event is confirmed.
1611        event = MotionEvent.obtain(
1612                downTime, eventTime + 5, MotionEvent.ACTION_MOVE,
1613                FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
1614        assertTrue(mGestureHandler.onTouchEvent(event));
1615        assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1616
1617        // Enable synchronous event confirmation upon dispatch.
1618        mMockMotionEventDelegate.enableSynchronousConfirmTouchEvent(
1619                mGestureHandler, ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_CONSUMED);
1620
1621        // Confirm the original event; this should dispatch the second event and confirm it
1622        // synchronously, without scheduling a touch timeout.
1623        mGestureHandler.confirmTouchEvent(
1624                ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_CONSUMED);
1625        assertTrue(mGestureHandler.onTouchEvent(event));
1626        assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1627        assertFalse(mGestureHandler.hasScheduledTouchTimeoutEventForTesting());
1628        assertEquals(TouchPoint.TOUCH_EVENT_TYPE_MOVE, mMockMotionEventDelegate.mLastTouchAction);
1629
1630        // Adding events to any empty queue will trigger synchronous dispatch and confirmation.
1631        event = MotionEvent.obtain(
1632                downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
1633                FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
1634        assertTrue(mGestureHandler.onTouchEvent(event));
1635        assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1636        assertFalse(mGestureHandler.hasScheduledTouchTimeoutEventForTesting());
1637   }
1638}
1639