e18171b0ac5478c611c03b299824dcb5a00c5982
[platform/framework/web/crosswalk.git] / src / content / public / android / javatests / src / org / chromium / content / browser / ContentViewGestureHandlerTest.java
1 // Copyright 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
5 package org.chromium.content.browser;
6
7 import android.content.Context;
8 import android.os.Bundle;
9 import android.os.SystemClock;
10 import android.test.InstrumentationTestCase;
11 import android.test.suitebuilder.annotation.SmallTest;
12 import android.util.Log;
13 import android.view.MotionEvent;
14 import android.view.MotionEvent.PointerCoords;
15 import android.view.MotionEvent.PointerProperties;
16 import android.view.ViewConfiguration;
17
18 import org.chromium.base.test.util.Feature;
19 import org.chromium.base.test.util.ScalableTimeout;
20 import org.chromium.content.browser.ContentViewGestureHandler.MotionEventDelegate;
21 import org.chromium.content.browser.third_party.GestureDetector;
22
23 import java.util.ArrayList;
24 import java.util.concurrent.CountDownLatch;
25 import java.util.concurrent.TimeUnit;
26
27 /**
28  * Test suite for ContentViewGestureHandler.
29  */
30 public class ContentViewGestureHandlerTest extends InstrumentationTestCase {
31     private static final int FAKE_COORD_X = 42;
32     private static final int FAKE_COORD_Y = 24;
33
34     private static final String TAG = "ContentViewGestureHandler";
35     private MockListener mMockListener;
36     private MockMotionEventDelegate mMockMotionEventDelegate;
37     private MockGestureDetector mMockGestureDetector;
38     private MockZoomManager mMockZoomManager;
39     private ContentViewGestureHandler mGestureHandler;
40     private LongPressDetector mLongPressDetector;
41
42     static class MockListener extends GestureDetector.SimpleOnGestureListener {
43         MotionEvent mLastLongPress;
44         MotionEvent mLastShowPress;
45         MotionEvent mLastSingleTap;
46         MotionEvent mLastFling1;
47         CountDownLatch mLongPressCalled;
48         CountDownLatch mShowPressCalled;
49
50         public MockListener() {
51             mLongPressCalled = new CountDownLatch(1);
52             mShowPressCalled = new CountDownLatch(1);
53         }
54
55         @Override
56         public void onLongPress(MotionEvent e) {
57             mLastLongPress = MotionEvent.obtain(e);
58             mLongPressCalled.countDown();
59         }
60
61         @Override
62         public void onShowPress(MotionEvent e) {
63             mLastShowPress = MotionEvent.obtain(e);
64             mShowPressCalled.countDown();
65             Log.e("Overscroll", "OnShowPress");
66         }
67
68         @Override
69         public boolean onSingleTapConfirmed(MotionEvent e) {
70             mLastSingleTap = e;
71             return true;
72         }
73
74         @Override
75         public boolean onSingleTapUp(MotionEvent e) {
76             mLastSingleTap = e;
77             return true;
78         }
79
80         @Override
81         public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
82             return true;
83         }
84
85         @Override
86         public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
87             mLastFling1 = e1;
88             return true;
89         }
90     }
91
92     static class MockGestureDetector extends GestureDetector {
93         MotionEvent mLastEvent;
94         public MockGestureDetector(Context context, OnGestureListener listener) {
95             super(context, listener);
96         }
97
98         @Override
99         public boolean onTouchEvent(MotionEvent ev) {
100             mLastEvent = MotionEvent.obtain(ev);
101             return super.onTouchEvent(ev);
102         }
103     }
104
105     static class MockMotionEventDelegate implements MotionEventDelegate {
106         private ContentViewGestureHandler mSynchronousConfirmTarget;
107         private int mSynchronousConfirmAckResult;
108
109         public int mLastTouchAction;
110         public int mLastGestureType;
111         public int mTotalSentGestureCount;
112
113         @Override
114         public boolean sendTouchEvent(long timeMs, int action, TouchPoint[] pts) {
115             mLastTouchAction = action;
116             if (mSynchronousConfirmTarget != null) {
117                 mSynchronousConfirmTarget.confirmTouchEvent(mSynchronousConfirmAckResult);
118             }
119             return true;
120         }
121
122         @Override
123         public boolean sendGesture(int type, long timeMs, int x, int y, Bundle extraParams) {
124             Log.i(TAG,"Gesture event received with type id " + type);
125             mLastGestureType = type;
126             mTotalSentGestureCount++;
127             return true;
128         }
129
130         @Override
131         public void sendSingleTapUMA(int type) {
132             // Not implemented.
133         }
134
135         @Override
136         public void sendActionAfterDoubleTapUMA(int type,
137                 boolean clickDelayEnabled) {
138             // Not implemented.
139         }
140
141         @Override
142         public void invokeZoomPicker() {
143             // Not implemented.
144         }
145
146         public void enableSynchronousConfirmTouchEvent(
147                 ContentViewGestureHandler handler, int ackResult) {
148             mSynchronousConfirmTarget = handler;
149             mSynchronousConfirmAckResult = ackResult;
150         }
151
152         public void disableSynchronousConfirmTouchEvent() {
153             mSynchronousConfirmTarget = null;
154         }
155     }
156
157     static class MockZoomManager extends ZoomManager {
158         private ContentViewGestureHandler mHandlerForMoveEvents;
159
160         MockZoomManager(Context context, ContentViewCore contentViewCore) {
161             super(context, contentViewCore);
162         }
163
164         public void pinchOnMoveEvents(ContentViewGestureHandler handler) {
165             mHandlerForMoveEvents = handler;
166         }
167
168         @Override
169         public boolean processTouchEvent(MotionEvent event) {
170             if (event.getActionMasked() == MotionEvent.ACTION_MOVE
171                     && mHandlerForMoveEvents != null) {
172                 mHandlerForMoveEvents.pinchBy(event.getEventTime(), 1, 1, 1.1f);
173                 return true;
174             }
175             return false;
176         }
177     }
178
179     private static MotionEvent motionEvent(int action, long downTime, long eventTime) {
180         return MotionEvent.obtain(downTime, eventTime, action, FAKE_COORD_X, FAKE_COORD_Y, 0);
181     }
182
183     @Override
184     public void setUp() {
185         mMockListener = new MockListener();
186         mMockGestureDetector = new MockGestureDetector(
187                 getInstrumentation().getTargetContext(), mMockListener);
188         mMockMotionEventDelegate = new MockMotionEventDelegate();
189         mMockZoomManager = new MockZoomManager(getInstrumentation().getTargetContext(), null);
190         mGestureHandler = new ContentViewGestureHandler(
191                 getInstrumentation().getTargetContext(), mMockMotionEventDelegate,
192                 mMockZoomManager);
193         mLongPressDetector = new LongPressDetector(
194                 getInstrumentation().getTargetContext(), mGestureHandler);
195         mGestureHandler.setTestDependencies(
196                 mLongPressDetector, mMockGestureDetector, mMockListener);
197         TouchPoint.initializeConstantsForTesting();
198     }
199
200     /**
201      * Verify that a DOWN followed shortly by an UP will trigger a single tap.
202      *
203      * @throws Exception
204      */
205     @SmallTest
206     @Feature({"Gestures"})
207     public void testGestureSingleClick() throws Exception {
208         final long downTime = SystemClock.uptimeMillis();
209         final long eventTime = SystemClock.uptimeMillis();
210
211         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
212
213         assertFalse(mGestureHandler.onTouchEvent(event));
214         assertTrue("Should have a pending gesture", mMockGestureDetector.mLastEvent != null);
215         assertTrue("Should have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
216
217         event = motionEvent(MotionEvent.ACTION_UP, downTime, eventTime + 10);
218         mLongPressDetector.cancelLongPressIfNeeded(event);
219         assertTrue("Should not have a pending LONG_PRESS", !mLongPressDetector.hasPendingMessage());
220         assertTrue(mGestureHandler.onTouchEvent(event));
221         // Synchronous, no need to wait.
222         assertTrue("Should have a single tap", mMockListener.mLastSingleTap != null);
223     }
224
225     /**
226      * Verify that when a touch event handler is registered the touch events are queued
227      * and sent in order.
228      * @throws Exception
229      */
230     @SmallTest
231     @Feature({"Gestures"})
232     public void testFlingOnTouchHandler() throws Exception {
233         final long downTime = SystemClock.uptimeMillis();
234         final long eventTime = SystemClock.uptimeMillis();
235
236         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
237
238         mGestureHandler.hasTouchEventHandlers(true);
239
240         assertTrue(mGestureHandler.onTouchEvent(event));
241         assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
242         assertTrue("Should not have a pending gesture", mMockGestureDetector.mLastEvent == null);
243         assertFalse("Should not have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
244
245         event = MotionEvent.obtain(
246                 downTime, eventTime + 5, MotionEvent.ACTION_MOVE,
247                 FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
248         assertTrue(mGestureHandler.onTouchEvent(event));
249         assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
250
251         event = MotionEvent.obtain(
252                 downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
253                 FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
254         assertTrue(mGestureHandler.onTouchEvent(event));
255         assertEquals("We should have coalesced move events into one"
256                 , 2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
257
258         event = MotionEvent.obtain(
259                 downTime, eventTime + 15, MotionEvent.ACTION_UP,
260                 FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
261         assertTrue(mGestureHandler.onTouchEvent(event));
262         assertEquals(3, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
263
264         mGestureHandler.confirmTouchEvent(
265                 ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
266         assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
267         assertEquals(MotionEvent.ACTION_MOVE,
268                 mGestureHandler.peekFirstInPendingMotionEventsForTesting().getActionMasked());
269         assertFalse("Pending LONG_PRESS should have been canceled",
270                 mLongPressDetector.hasPendingMessage());
271
272         mGestureHandler.confirmTouchEvent(ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_CONSUMED);
273         assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
274         assertEquals(MotionEvent.ACTION_UP,
275                 mGestureHandler.peekFirstInPendingMotionEventsForTesting().getActionMasked());
276
277         mGestureHandler.confirmTouchEvent(ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_CONSUMED);
278         assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
279
280         // Synchronous, no need to wait.
281         assertTrue("Should not have a fling", mMockListener.mLastFling1 == null);
282         assertTrue("Should not have a long press", mMockListener.mLastLongPress == null);
283     }
284
285     /**
286      * Verify that after a touch event handlers starts handling a gesture, even though some event
287      * in the middle of the gesture returns with NOT_CONSUMED, we don't send that to the gesture
288      * detector to avoid falling to a faulty state.
289      * @throws Exception
290      */
291     @SmallTest
292     @Feature({"Gestures"})
293     public void testFlingOnTouchHandlerWithOneEventNotConsumed() throws Exception {
294         final long downTime = SystemClock.uptimeMillis();
295         final long eventTime = SystemClock.uptimeMillis();
296
297         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
298
299         mGestureHandler.hasTouchEventHandlers(true);
300
301         assertTrue(mGestureHandler.onTouchEvent(event));
302         assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
303         assertTrue("Should not have a pending gesture", mMockGestureDetector.mLastEvent == null);
304         assertFalse("Should not have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
305
306         event = MotionEvent.obtain(
307                 downTime, eventTime + 5, MotionEvent.ACTION_MOVE,
308                 FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
309         assertTrue(mGestureHandler.onTouchEvent(event));
310         assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
311
312         event = MotionEvent.obtain(
313                 downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
314                 FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
315         assertTrue(mGestureHandler.onTouchEvent(event));
316         assertEquals("We should have coalesced move events into one"
317                 , 2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
318
319         event = MotionEvent.obtain(
320                 downTime, eventTime + 15, MotionEvent.ACTION_UP,
321                 FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
322         assertTrue(mGestureHandler.onTouchEvent(event));
323         assertEquals(3, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
324
325         mGestureHandler.confirmTouchEvent(
326                 ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_CONSUMED);
327         assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
328         assertEquals(MotionEvent.ACTION_MOVE,
329                 mGestureHandler.peekFirstInPendingMotionEventsForTesting().getActionMasked());
330
331         mGestureHandler.confirmTouchEvent(
332                 ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
333         assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
334         assertEquals(MotionEvent.ACTION_UP,
335                 mGestureHandler.peekFirstInPendingMotionEventsForTesting().getActionMasked());
336         assertTrue("Even though the last event was not consumed by JavaScript," +
337                 "it shouldn't have been sent to the Gesture Detector",
338                         mMockGestureDetector.mLastEvent == null);
339
340         mGestureHandler.confirmTouchEvent(ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_CONSUMED);
341         assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
342
343         // Synchronous, no need to wait.
344         assertTrue("Should not have a fling", mMockListener.mLastFling1 == null);
345         assertTrue("Should not have a long press", mMockListener.mLastLongPress == null);
346     }
347
348     /**
349      * Verify that when a registered touch event handler return NO_CONSUMER_EXISTS for down event
350      * all queue is drained until next down.
351      * @throws Exception
352      */
353     @SmallTest
354     @Feature({"Gestures"})
355     public void testDrainWithFlingAndClickOutofTouchHandler() throws Exception {
356         final long downTime = SystemClock.uptimeMillis();
357         final long eventTime = SystemClock.uptimeMillis();
358
359         mGestureHandler = new ContentViewGestureHandler(
360                 getInstrumentation().getTargetContext(), new MockMotionEventDelegate(),
361                 mMockZoomManager);
362         mLongPressDetector = new LongPressDetector(
363                 getInstrumentation().getTargetContext(), mGestureHandler);
364
365         mGestureHandler.hasTouchEventHandlers(true);
366
367         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
368         assertTrue(mGestureHandler.onTouchEvent(event));
369         assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
370         assertFalse("Should not have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
371
372         event = MotionEvent.obtain(
373                 downTime, eventTime + 5, MotionEvent.ACTION_MOVE,
374                 FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
375         assertTrue(mGestureHandler.onTouchEvent(event));
376         assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
377
378         event = MotionEvent.obtain(
379                 downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
380                 FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
381         assertTrue(mGestureHandler.onTouchEvent(event));
382         assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
383
384         event = MotionEvent.obtain(
385                 downTime, eventTime + 15, MotionEvent.ACTION_UP,
386                 FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
387         assertTrue(mGestureHandler.onTouchEvent(event));
388         assertEquals(3, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
389
390         event = motionEvent(MotionEvent.ACTION_DOWN, eventTime + 20, eventTime + 20);
391         assertTrue(mGestureHandler.onTouchEvent(event));
392         assertEquals(4, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
393
394         event = MotionEvent.obtain(
395                 downTime, eventTime + 20, MotionEvent.ACTION_UP,
396                 FAKE_COORD_X, FAKE_COORD_Y, 0);
397         assertTrue(mGestureHandler.onTouchEvent(event));
398         assertEquals(5, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
399
400         mGestureHandler.confirmTouchEvent(
401                 ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
402         assertEquals("The queue should have been drained until first down since no consumer exists",
403                 2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
404         assertEquals(MotionEvent.ACTION_DOWN,
405                 mGestureHandler.peekFirstInPendingMotionEventsForTesting().getActionMasked());
406
407         mGestureHandler.confirmTouchEvent(
408                 ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
409         assertEquals("The queue should have been drained",
410                 0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
411     }
412
413     /**
414      * Verify that when a touch event handler is registered the touch events stop getting queued
415      * after we received INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS.
416      * @throws Exception
417      */
418     @SmallTest
419     @Feature({"Gestures"})
420     public void testFlingOutOfTouchHandler() throws Exception {
421         final long downTime = SystemClock.uptimeMillis();
422         final long eventTime = SystemClock.uptimeMillis();
423
424         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
425
426         mGestureHandler.hasTouchEventHandlers(true);
427
428         assertTrue(mGestureHandler.onTouchEvent(event));
429         assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
430         assertTrue("Should not have a pending gesture", mMockGestureDetector.mLastEvent == null);
431         assertFalse("Should not have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
432
433         mGestureHandler.confirmTouchEvent(
434                 ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
435         assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
436         assertEquals("The down touch event should have been sent to the Gesture Detector",
437                 event.getEventTime(), mMockGestureDetector.mLastEvent.getEventTime());
438         assertEquals("The down touch event should have been sent to the Gesture Detector",
439                 MotionEvent.ACTION_DOWN, mMockGestureDetector.mLastEvent.getActionMasked());
440
441         event = MotionEvent.obtain(
442                 downTime, eventTime + 5, MotionEvent.ACTION_MOVE,
443                 FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
444         assertTrue(mGestureHandler.onTouchEvent(event));
445         assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
446         assertEquals("Motion events should be going to the Gesture Detector directly",
447                 event.getEventTime(), mMockGestureDetector.mLastEvent.getEventTime());
448         assertEquals("Motion events should be going to the Gesture Detector directly",
449                 MotionEvent.ACTION_MOVE, mMockGestureDetector.mLastEvent.getActionMasked());
450
451         event = MotionEvent.obtain(
452                 downTime, eventTime + 10, MotionEvent.ACTION_UP,
453                 FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
454         assertTrue(mGestureHandler.onTouchEvent(event));
455         assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
456         assertEquals("Motion events should be going to the Gesture Detector directly",
457                 event.getEventTime(), mMockGestureDetector.mLastEvent.getEventTime());
458         assertEquals("Motion events should be going to the Gesture Detector directly",
459                 MotionEvent.ACTION_UP, mMockGestureDetector.mLastEvent.getActionMasked());
460
461         // Synchronous, no need to wait.
462         assertTrue("Should have a fling", mMockListener.mLastFling1 != null);
463         assertTrue("Should not have a long press", mMockListener.mLastLongPress == null);
464     }
465
466     /**
467      * Verifies that a single tap doesn't cause a long press event to be sent.
468      * @throws Exception
469      */
470     @SmallTest
471     @Feature({"Gestures"})
472     public void testNoLongPressIsSentForSingleTapOutOfTouchHandler() throws Exception {
473         final long downTime = SystemClock.uptimeMillis();
474         final long eventTime = SystemClock.uptimeMillis();
475
476         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
477
478         mGestureHandler.hasTouchEventHandlers(true);
479
480         assertTrue(mGestureHandler.onTouchEvent(event));
481         assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
482         assertTrue("Should not have a pending gesture", mMockGestureDetector.mLastEvent == null);
483         assertFalse("Should not have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
484
485         event = motionEvent(MotionEvent.ACTION_UP, downTime, eventTime + 5);
486         assertTrue(mGestureHandler.onTouchEvent(event));
487         assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
488
489         mGestureHandler.confirmTouchEvent(
490                 ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
491
492         assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
493         assertEquals("The down touch event should have been sent to the Gesture Detector",
494                 event.getDownTime(), mMockGestureDetector.mLastEvent.getEventTime());
495         assertEquals("The next event should be ACTION_UP",
496                 MotionEvent.ACTION_UP,
497                 mGestureHandler.peekFirstInPendingMotionEventsForTesting().getActionMasked());
498
499         mGestureHandler.confirmTouchEvent(
500                 ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
501
502         assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
503         assertEquals("The up touch event should have been sent to the Gesture Detector",
504                 event.getEventTime(), mMockGestureDetector.mLastEvent.getEventTime());
505
506         assertFalse("Should not have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
507     }
508
509     /**
510      * Verify that a DOWN followed by a MOVE will trigger fling (but not LONG).
511      * @throws Exception
512      */
513     @SmallTest
514     @Feature({"Gestures"})
515     public void testGestureFlingAndCancelLongClick() throws Exception {
516         final long downTime = SystemClock.uptimeMillis();
517         final long eventTime = SystemClock.uptimeMillis();
518
519         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
520
521         assertFalse(mGestureHandler.onTouchEvent(event));
522         assertTrue("Should have a pending gesture", mMockGestureDetector.mLastEvent != null);
523         assertTrue("Should have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
524
525         event = MotionEvent.obtain(
526                 downTime, eventTime + 5, MotionEvent.ACTION_MOVE,
527                 FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
528         mLongPressDetector.cancelLongPressIfNeeded(event);
529         assertTrue("Should not have a pending LONG_PRESS", !mLongPressDetector.hasPendingMessage());
530         assertTrue(mGestureHandler.onTouchEvent(event));
531
532         event = MotionEvent.obtain(
533                 downTime, eventTime + 10, MotionEvent.ACTION_UP,
534                 FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
535         assertTrue(mGestureHandler.onTouchEvent(event));
536
537         // Synchronous, no need to wait.
538         assertTrue("Should have a fling", mMockListener.mLastFling1 != null);
539         assertTrue("Should not have a long press", mMockListener.mLastLongPress == null);
540     }
541
542     /**
543      * Verify that for a normal scroll the following events are sent:
544      * - GESTURE_SCROLL_START
545      * - GESTURE_SCROLL_BY
546      * - GESTURE_SCROLL_END
547      * @throws Exception
548      */
549     @SmallTest
550     @Feature({"Gestures"})
551     public void testScrollEventActionUpSequence() throws Exception {
552         checkScrollEventSequenceForEndActionType(MotionEvent.ACTION_UP);
553     }
554
555     /**
556      * Verify that for a cancelled scroll the following events are sent:
557      * - GESTURE_SCROLL_START
558      * - GESTURE_SCROLL_BY
559      * - GESTURE_SCROLL_END
560      * @throws Exception
561      */
562     @SmallTest
563     @Feature({"Gestures"})
564     public void testScrollEventActionCancelSequence() throws Exception {
565         checkScrollEventSequenceForEndActionType(MotionEvent.ACTION_CANCEL);
566     }
567
568     private void checkScrollEventSequenceForEndActionType(int endActionType) throws Exception {
569         final long downTime = SystemClock.uptimeMillis();
570         final long eventTime = SystemClock.uptimeMillis();
571         final int scrollToX = FAKE_COORD_X + 100;
572         final int scrollToY = FAKE_COORD_Y + 100;
573
574         GestureRecordingMotionEventDelegate mockDelegate =
575                 new GestureRecordingMotionEventDelegate();
576         mGestureHandler = new ContentViewGestureHandler(
577                 getInstrumentation().getTargetContext(), mockDelegate, mMockZoomManager);
578
579         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
580
581         assertTrue(mGestureHandler.onTouchEvent(event));
582
583         event = MotionEvent.obtain(
584                 downTime, eventTime + 1000, MotionEvent.ACTION_MOVE, scrollToX, scrollToY, 0);
585         assertTrue(mGestureHandler.onTouchEvent(event));
586         assertTrue(mGestureHandler.isNativeScrolling());
587         assertTrue("A scrollStart event should have been sent",
588                 mockDelegate.mGestureTypeList.contains(
589                         ContentViewGestureHandler.GESTURE_SCROLL_START));
590         assertEquals("We should have started scrolling",
591                 ContentViewGestureHandler.GESTURE_SCROLL_BY,
592                 mockDelegate.mMostRecentGestureEvent.mType);
593         assertEquals("Only tapDown, tapCancel, scrollBegin and scrollBy should have been sent",
594                 4, mockDelegate.mGestureTypeList.size());
595         assertEquals("scrollBegin should be sent before scrollBy",
596                 ContentViewGestureHandler.GESTURE_SCROLL_START,
597                 (int) mockDelegate.mGestureTypeList.get(2));
598         assertEquals("scrollBegin should have the time of the ACTION_MOVE",
599                 eventTime + 1000, (long) mockDelegate.mGestureTimeList.get(2));
600
601         event = MotionEvent.obtain(
602                 downTime, eventTime + 1000, endActionType, scrollToX, scrollToY, 0);
603         mGestureHandler.onTouchEvent(event);
604         assertFalse(mGestureHandler.isNativeScrolling());
605         assertTrue("A scrollEnd event should have been sent",
606                 mockDelegate.mGestureTypeList.contains(
607                         ContentViewGestureHandler.GESTURE_SCROLL_END));
608         assertEquals("We should have stopped scrolling",
609                 ContentViewGestureHandler.GESTURE_SCROLL_END,
610                 mockDelegate.mMostRecentGestureEvent.mType);
611         assertEquals("Only tapDown, scrollBegin and scrollBy and scrollEnd should have been sent",
612                 5, mockDelegate.mGestureTypeList.size());
613     }
614
615     /**
616      * Verify that for a normal fling (fling after scroll) the following events are sent:
617      * - GESTURE_SCROLL_BEGIN
618      * - GESTURE_FLING_START
619      * and GESTURE_FLING_CANCEL is sent on the next touch.
620      * @throws Exception
621      */
622     @SmallTest
623     @Feature({"Gestures"})
624     public void testFlingEventSequence() throws Exception {
625         final long downTime = SystemClock.uptimeMillis();
626         final long eventTime = SystemClock.uptimeMillis();
627
628         GestureRecordingMotionEventDelegate mockDelegate =
629                 new GestureRecordingMotionEventDelegate();
630         mGestureHandler = new ContentViewGestureHandler(
631                 getInstrumentation().getTargetContext(), mockDelegate, mMockZoomManager);
632
633         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
634
635         assertTrue(mGestureHandler.onTouchEvent(event));
636
637         event = MotionEvent.obtain(
638                 downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
639                 FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
640         assertTrue(mGestureHandler.onTouchEvent(event));
641         assertTrue(mGestureHandler.isNativeScrolling());
642         assertTrue("A scrollStart event should have been sent",
643                 mockDelegate.mGestureTypeList.contains(
644                         ContentViewGestureHandler.GESTURE_SCROLL_START));
645         assertEquals("We should have started scrolling",
646                 ContentViewGestureHandler.GESTURE_SCROLL_BY,
647                 mockDelegate.mMostRecentGestureEvent.mType);
648         assertEquals("Only tapDown, tapCancel, scrollBegin and scrollBy should have been sent",
649                 4, mockDelegate.mGestureTypeList.size());
650         assertEquals("scrollBegin should be sent before scrollBy",
651                 ContentViewGestureHandler.GESTURE_SCROLL_START,
652                 (int) mockDelegate.mGestureTypeList.get(2));
653         GestureRecordingMotionEventDelegate.GestureEvent startEvent =
654                 mockDelegate.getActiveScrollStartEvent();
655         assertNotNull(startEvent);
656         assertEquals("scrollBegin should have the time of the ACTION_MOVE",
657                 eventTime + 10, (long) startEvent.getTimeMs());
658         int hintX = startEvent.getExtraParams().getInt(ContentViewGestureHandler.DELTA_HINT_X);
659         int hintY = startEvent.getExtraParams().getInt(ContentViewGestureHandler.DELTA_HINT_Y);
660         // We don't want to take a dependency here on exactly how hints are calculated for a
661         // fling (eg. may depend on velocity), so just validate the direction.
662         assertTrue("scrollBegin hint should be in positive X axis",
663                 hintX > 0 && hintY > 0 && hintX > hintY);
664
665         event = MotionEvent.obtain(
666                 downTime, eventTime + 15, MotionEvent.ACTION_UP,
667                 FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
668         assertTrue(mGestureHandler.onTouchEvent(event));
669         assertFalse(mGestureHandler.isNativeScrolling());
670         assertEquals("We should have started flinging",
671                 ContentViewGestureHandler.GESTURE_FLING_START,
672                         mockDelegate.mMostRecentGestureEvent.mType);
673         assertTrue("A scroll end event should not have been sent",
674                 !mockDelegate.mGestureTypeList.contains(
675                         ContentViewGestureHandler.GESTURE_SCROLL_END));
676         assertEquals("The last up should have caused flingStart to be sent",
677                 5, mockDelegate.mGestureTypeList.size());
678         assertEquals("flingStart should have the time of the ACTION_UP",
679                 eventTime + 15, (long) mockDelegate.mGestureTimeList.get(4));
680
681         event = motionEvent(MotionEvent.ACTION_DOWN, downTime + 50, downTime + 50);
682         assertTrue(mGestureHandler.onTouchEvent(event));
683         assertTrue("A flingCancel should have been sent",
684                 mockDelegate.mGestureTypeList.contains(
685                         ContentViewGestureHandler.GESTURE_FLING_CANCEL));
686         assertEquals("Only tapDown and flingCancel should have been sent",
687                 7, mockDelegate.mGestureTypeList.size());
688     }
689
690     /**
691      * Verify that a zero-velocity fling is never forwarded, and cancels any
692      * previous fling or scroll sequence.
693      * @throws Exception
694      */
695     @SmallTest
696     @Feature({"Gestures"})
697     public void testZeroVelocityFling() throws Exception {
698         final long downTime = SystemClock.uptimeMillis();
699         final long eventTime = SystemClock.uptimeMillis();
700
701         GestureRecordingMotionEventDelegate mockDelegate =
702                 new GestureRecordingMotionEventDelegate();
703         mGestureHandler = new ContentViewGestureHandler(
704                 getInstrumentation().getTargetContext(), mockDelegate, mMockZoomManager);
705
706         mGestureHandler.fling(eventTime, 5, 5, 0, 0);
707         assertEquals("A zero-velocity fling should not be forwrded",
708                 null, mockDelegate.mMostRecentGestureEvent);
709
710         mGestureHandler.fling(eventTime, 5, 5, 5, 0);
711         assertEquals("Subsequent flings should work properly",
712                 ContentViewGestureHandler.GESTURE_FLING_START,
713                         mockDelegate.mMostRecentGestureEvent.mType);
714
715         mGestureHandler.fling(eventTime, 5, 5, 0, 0);
716         assertEquals("A zero-velocity fling should cancel any outstanding fling",
717                 ContentViewGestureHandler.GESTURE_FLING_CANCEL,
718                         mockDelegate.mMostRecentGestureEvent.mType);
719
720         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
721         assertTrue(mGestureHandler.onTouchEvent(event));
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         assertTrue(mGestureHandler.isNativeScrolling());
727         assertTrue("A scrollStart event should have been sent",
728                 mockDelegate.mGestureTypeList.contains(
729                         ContentViewGestureHandler.GESTURE_SCROLL_START));
730         assertEquals("We should have started scrolling",
731                 ContentViewGestureHandler.GESTURE_SCROLL_BY,
732                 mockDelegate.mMostRecentGestureEvent.mType);
733
734         mGestureHandler.fling(eventTime, 5, 5, 0, 0);
735         assertEquals("A zero-velicty fling should end the current scroll sequence",
736                 ContentViewGestureHandler.GESTURE_SCROLL_END,
737                         mockDelegate.mMostRecentGestureEvent.mType);
738     }
739
740     /**
741      * Verify that a show pressed state gesture followed by a long press followed by the focus
742      * loss in the window due to context menu cancels show pressed.
743      * @throws Exception
744      */
745     @SmallTest
746     @Feature({"Gestures"})
747     public void testShowPressCancelOnWindowFocusLost() throws Exception {
748         final long time = SystemClock.uptimeMillis();
749         GestureRecordingMotionEventDelegate mockDelegate =
750                 new GestureRecordingMotionEventDelegate();
751         mGestureHandler = new ContentViewGestureHandler(
752                 getInstrumentation().getTargetContext(), mockDelegate, mMockZoomManager);
753         mLongPressDetector = new LongPressDetector(
754                 getInstrumentation().getTargetContext(), mGestureHandler);
755         mGestureHandler.setTestDependencies(mLongPressDetector, null, null);
756
757         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, time, time);
758         mGestureHandler.onTouchEvent(event);
759
760         mGestureHandler.sendShowPressedStateGestureForTesting();
761         assertEquals("A show pressed state event should have been sent",
762                 ContentViewGestureHandler.GESTURE_SHOW_PRESSED_STATE,
763                         mockDelegate.mMostRecentGestureEvent.mType);
764         assertEquals("Only showPressedState and tapDown should have been sent",
765                 2, mockDelegate.mGestureTypeList.size());
766
767         mLongPressDetector.startLongPressTimerIfNeeded(event);
768         mLongPressDetector.sendLongPressGestureForTest();
769
770         assertEquals("Only should have sent only LONG_PRESS event",
771                 3, mockDelegate.mGestureTypeList.size());
772         assertEquals("Should have a long press event next",
773                 ContentViewGestureHandler.GESTURE_LONG_PRESS,
774                 mockDelegate.mGestureTypeList.get(2).intValue());
775
776         // The long press triggers window focus loss by opening a context menu
777         mGestureHandler.onWindowFocusLost();
778
779         assertEquals("Only should have sent only GESTURE_TAP_CANCEL event",
780                 4, mockDelegate.mGestureTypeList.size());
781         assertEquals("Should have a gesture show press cancel event next",
782                 ContentViewGestureHandler.GESTURE_TAP_CANCEL,
783                 mockDelegate.mGestureTypeList.get(3).intValue());
784     }
785
786     /**
787      * Verify that a recent show pressed state gesture is canceled when scrolling begins.
788      * @throws Exception
789      */
790     @SmallTest
791     @Feature({"Gestures"})
792     public void testShowPressCancelWhenScrollBegins() throws Exception {
793         final long downTime = SystemClock.uptimeMillis();
794         final long eventTime = SystemClock.uptimeMillis();
795
796         GestureRecordingMotionEventDelegate mockDelegate =
797                 new GestureRecordingMotionEventDelegate();
798         mGestureHandler = new ContentViewGestureHandler(
799                 getInstrumentation().getTargetContext(), mockDelegate, mMockZoomManager);
800         mLongPressDetector = new LongPressDetector(
801                 getInstrumentation().getTargetContext(), mGestureHandler);
802
803         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
804
805         assertTrue(mGestureHandler.onTouchEvent(event));
806         assertFalse("Should not have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
807
808         mGestureHandler.sendShowPressedStateGestureForTesting();
809
810         assertEquals("A show pressed state event should have been sent",
811                 ContentViewGestureHandler.GESTURE_SHOW_PRESSED_STATE,
812                         mockDelegate.mMostRecentGestureEvent.mType);
813         assertEquals("Only tapDown and showPressedState should have been sent",
814                 2, mockDelegate.mGestureTypeList.size());
815
816         event = MotionEvent.obtain(
817                 downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
818                 FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
819         assertTrue(mGestureHandler.onTouchEvent(event));
820
821         assertEquals("We should have started scrolling",
822                 ContentViewGestureHandler.GESTURE_SCROLL_BY,
823                         mockDelegate.mMostRecentGestureEvent.mType);
824         assertTrue("A show press cancel event should have been sent",
825                 mockDelegate.mGestureTypeList.contains(
826                         ContentViewGestureHandler.GESTURE_TAP_CANCEL));
827         assertEquals("Only tapDown, showPressedState, showPressCancel, scrollBegin and scrollBy" +
828                 " should have been sent",
829                 5, mockDelegate.mGestureTypeList.size());
830
831         event = MotionEvent.obtain(
832                 downTime, eventTime + 15, MotionEvent.ACTION_UP,
833                 FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
834         assertTrue(mGestureHandler.onTouchEvent(event));
835         assertEquals("We should have started flinging",
836                 ContentViewGestureHandler.GESTURE_FLING_START,
837                         mockDelegate.mMostRecentGestureEvent.mType);
838         assertTrue("A scroll end event should not have been sent",
839                 !mockDelegate.mGestureTypeList.contains(
840                         ContentViewGestureHandler.GESTURE_SCROLL_END));
841         assertEquals("The last up should have caused flingStart to be sent",
842                 6, mockDelegate.mGestureTypeList.size());
843     }
844
845     /**
846      * Verify that double tap is correctly handled including the recent show pressed state gesture
847      * cancellation.
848      * @throws Exception
849      */
850     @SmallTest
851     @Feature({"Gestures"})
852     public void testDoubleTap() throws Exception {
853         final long downTime = SystemClock.uptimeMillis();
854         final long eventTime = SystemClock.uptimeMillis();
855
856         GestureRecordingMotionEventDelegate mockDelegate =
857                 new GestureRecordingMotionEventDelegate();
858         mGestureHandler = new ContentViewGestureHandler(
859                 getInstrumentation().getTargetContext(), mockDelegate, mMockZoomManager);
860         mLongPressDetector = new LongPressDetector(
861                 getInstrumentation().getTargetContext(), mGestureHandler);
862
863         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
864         assertTrue(mGestureHandler.onTouchEvent(event));
865         assertFalse("Should not have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
866
867         mGestureHandler.sendShowPressedStateGestureForTesting();
868         assertEquals("GESTURE_SHOW_PRESSED_STATE should have been sent",
869                 ContentViewGestureHandler.GESTURE_SHOW_PRESSED_STATE,
870                 mockDelegate.mMostRecentGestureEvent.mType);
871         assertEquals("Only GESTURE_TAP_DOWN and GESTURE_SHOW_PRESSED_STATE should have been sent",
872                 2, mockDelegate.mGestureTypeList.size());
873
874         event = MotionEvent.obtain(
875                 downTime, eventTime + 5, MotionEvent.ACTION_UP,
876                 FAKE_COORD_X, FAKE_COORD_Y, 0);
877         mGestureHandler.onTouchEvent(event);
878         assertEquals("A GESTURE_SINGLE_TAP_UNCONFIRMED event should have been sent",
879                 ContentViewGestureHandler.GESTURE_SINGLE_TAP_UNCONFIRMED,
880                 mockDelegate.mMostRecentGestureEvent.mType);
881         assertEquals("Only GESTURE_TAP_DOWN, " +
882                 "GESTURE_SHOW_PRESSED_STATE and " +
883                 "GESTURE_SINGLE_TAP_UNCONFIRMED should have been sent",
884                 3, mockDelegate.mGestureTypeList.size());
885
886         event = MotionEvent.obtain(
887                 eventTime + 10, eventTime + 10, MotionEvent.ACTION_DOWN,
888                 FAKE_COORD_X, FAKE_COORD_Y, 0);
889         assertTrue(mGestureHandler.onTouchEvent(event));
890         assertEquals("A GESTURE_TAP_DOWN event should have been sent ",
891                 ContentViewGestureHandler.GESTURE_TAP_DOWN,
892                 mockDelegate.mMostRecentGestureEvent.mType);
893         assertEquals("Only GESTURE_TAP_DOWN, " +
894                 "GESTURE_SHOW_PRESSED_STATE, " +
895                 "GESTURE_SINGLE_TAP_UNCONFIRMED," +
896                 "GESTURE_TAP_CANCEL and" +
897                 "GESTURE_TAP_DOWN should have been sent",
898                 5, mockDelegate.mGestureTypeList.size());
899
900         // Moving a very small amount of distance should not trigger the double tap drag zoom mode.
901         event = MotionEvent.obtain(
902                 eventTime + 10, eventTime + 10, MotionEvent.ACTION_MOVE,
903                 FAKE_COORD_X, FAKE_COORD_Y + 1, 0);
904         assertTrue(mGestureHandler.onTouchEvent(event));
905         assertEquals("Only GESTURE_TAP_DOWN, " +
906                 "GESTURE_SHOW_PRESSED_STATE, " +
907                 "GESTURE_SINGLE_TAP_UNCONFIRMED and" +
908                 "GESTURE_TAP_CANCEL and" +
909                 "GESTURE_TAP_DOWN should have been sent",
910                 5, mockDelegate.mGestureTypeList.size());
911
912         event = MotionEvent.obtain(
913                 eventTime + 10, eventTime + 15, MotionEvent.ACTION_UP,
914                 FAKE_COORD_X, FAKE_COORD_Y + 1, 0);
915         assertTrue(mGestureHandler.onTouchEvent(event));
916         assertEquals("A double tap should have occurred",
917                 ContentViewGestureHandler.GESTURE_DOUBLE_TAP,
918                 mockDelegate.mMostRecentGestureEvent.mType);
919         assertEquals("Only GESTURE_TAP_DOWN, " +
920                 "GESTURE_SHOW_PRESSED_STATE, " +
921                 "GESTURE_SINGLE_TAP_UNCONFIRMED, " +
922                 "GESTURE_TAP_CANCEL, " +
923                 "GESTURE_TAP_DOWN, " +
924                 "GESTURE_DOUBLE_TAP should have been sent",
925                 6, mockDelegate.mGestureTypeList.size());
926     }
927
928     /**
929      * Verify that double tap drag zoom feature is correctly executed.
930      * @throws Exception
931      */
932     @SmallTest
933     @Feature({"Gestures"})
934     public void testDoubleTapDragZoom() throws Exception {
935         final long downTime1 = SystemClock.uptimeMillis();
936         final long downTime2 = downTime1 + 100;
937
938         GestureRecordingMotionEventDelegate mockDelegate =
939                 new GestureRecordingMotionEventDelegate();
940         mGestureHandler = new ContentViewGestureHandler(
941                 getInstrumentation().getTargetContext(), mockDelegate, mMockZoomManager);
942         mLongPressDetector = new LongPressDetector(
943                 getInstrumentation().getTargetContext(), mGestureHandler);
944
945         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime1, downTime1);
946         assertTrue(mGestureHandler.onTouchEvent(event));
947         assertFalse("Should not have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
948
949         mGestureHandler.sendShowPressedStateGestureForTesting();
950         assertEquals("GESTURE_SHOW_PRESSED_STATE should have been sent",
951                 ContentViewGestureHandler.GESTURE_SHOW_PRESSED_STATE,
952                         mockDelegate.mMostRecentGestureEvent.mType);
953         assertEquals("Only GESTURE_TAP_DOWN and GESTURE_SHOW_PRESSED_STATE should have been sent",
954                 2, mockDelegate.mGestureTypeList.size());
955
956
957         event = MotionEvent.obtain(
958                 downTime1, downTime1 + 5, MotionEvent.ACTION_UP,
959                 FAKE_COORD_X, FAKE_COORD_Y, 0);
960         mGestureHandler.onTouchEvent(event);
961         assertEquals("A GESTURE_SINGLE_TAP_UNCONFIRMED event should have been sent",
962                 ContentViewGestureHandler.GESTURE_SINGLE_TAP_UNCONFIRMED,
963                 mockDelegate.mMostRecentGestureEvent.mType);
964         assertEquals("Only GESTURE_TAP_DOWN, " +
965                 "GESTURE_SHOW_PRESSED_STATE and " +
966                 "GESTURE_TAP_UNCONFIRMED " +
967                 "should have been sent",
968                 3, mockDelegate.mGestureTypeList.size());
969
970         event = MotionEvent.obtain(
971                 downTime2, downTime2, MotionEvent.ACTION_DOWN,
972                 FAKE_COORD_X, FAKE_COORD_Y, 0);
973         assertTrue(mGestureHandler.onTouchEvent(event));
974         assertEquals("GESTURE_TAP_DOWN should have been sent",
975                 ContentViewGestureHandler.GESTURE_TAP_DOWN,
976                 mockDelegate.mMostRecentGestureEvent.mType);
977         assertEquals("Only GESTURE_TAP_DOWN, " +
978                 "GESTURE_SHOW_PRESSED_STATE, " +
979                 "GESTURE_TAP_UNCONFIRMED," +
980                 "GESTURE_TAP_CANCEL and" +
981                 "GESTURE_TAP_DOWN should have been sent",
982                 5, mockDelegate.mGestureTypeList.size());
983
984         event = MotionEvent.obtain(
985                 downTime2, downTime2 + 5, MotionEvent.ACTION_MOVE,
986                 FAKE_COORD_X, FAKE_COORD_Y + 100, 0);
987         assertTrue(mGestureHandler.onTouchEvent(event));
988         assertTrue("GESTURE_SCROLL_START should have been sent",
989                 mockDelegate.mGestureTypeList.contains(
990                         ContentViewGestureHandler.GESTURE_SCROLL_START));
991         GestureRecordingMotionEventDelegate.GestureEvent startEvent =
992                 mockDelegate.getActiveScrollStartEvent();
993         assertEquals(FAKE_COORD_X, startEvent.getX());
994         assertEquals(FAKE_COORD_Y + 100, startEvent.getY());
995         Bundle extraParams = startEvent.getExtraParams();
996         assertNotNull(extraParams);
997         assertEquals("GESTURE_SCROLL_START should have an X hint equal to the distance traveled",
998                 0, extraParams.getInt(ContentViewGestureHandler.DELTA_HINT_X));
999         assertEquals("GESTURE_SCROLL_START should have an X hint equal to the distance traveled",
1000                 100, extraParams.getInt(ContentViewGestureHandler.DELTA_HINT_Y));
1001
1002         assertEquals("GESTURE_PINCH_BEGIN should have been sent",
1003                 ContentViewGestureHandler.GESTURE_PINCH_BEGIN,
1004                 mockDelegate.mMostRecentGestureEvent.mType);
1005         assertEquals("Only GESTURE_TAP_DOWN, " +
1006                 "GESTURE_SHOW_PRESSED_STATE, " +
1007                 "GESTURE_TAP_UNCONFIRMED," +
1008                 "GESTURE_TAP_CANCEL, " +
1009                 "GESTURE_TAP_DOWN, " +
1010                 "GESTURE_TAP_CANCEL, " +
1011                 "GESTURE_SCROLL_START, and " +
1012                 "GESTURE_PINCH_BEGIN should have been sent",
1013                 8, mockDelegate.mGestureTypeList.size());
1014
1015         event = MotionEvent.obtain(
1016                 downTime2, downTime2 + 10, MotionEvent.ACTION_MOVE,
1017                 FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
1018         assertTrue(mGestureHandler.onTouchEvent(event));
1019         assertTrue("GESTURE_SCROLL_BY should have been sent",
1020                 mockDelegate.mGestureTypeList.contains(
1021                         ContentViewGestureHandler.GESTURE_SCROLL_BY));
1022         assertEquals("GESTURE_PINCH_BY should have been sent",
1023                 ContentViewGestureHandler.GESTURE_PINCH_BY,
1024                 mockDelegate.mMostRecentGestureEvent.mType);
1025         assertEquals("Only GESTURE_TAP_DOWN, " +
1026                 "GESTURE_SHOW_PRESSED_STATE, " +
1027                 "GESTURE_TAP_UNCONFIRMED," +
1028                 "GESTURE_TAP_CANCEL, " +
1029                 "GESTURE_TAP_DOWN, " +
1030                 "GESTURE_TAP_CANCEL, " +
1031                 "GESTURE_SCROLL_START," +
1032                 "GESTURE_PINCH_BEGIN, " +
1033                 "GESTURE_SCROLL_BY, and " +
1034                 "GESTURE_PINCH_BY should have been sent",
1035                 10, mockDelegate.mGestureTypeList.size());
1036
1037         event = MotionEvent.obtain(
1038                 downTime2, downTime2 + 15, MotionEvent.ACTION_UP,
1039                 FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
1040         assertTrue(mGestureHandler.onTouchEvent(event));
1041         assertTrue("GESTURE_PINCH_END should have been sent",
1042                 mockDelegate.mGestureTypeList.contains(
1043                         ContentViewGestureHandler.GESTURE_PINCH_END));
1044         assertEquals("GESTURE_SCROLL_END should have been sent",
1045                 ContentViewGestureHandler.GESTURE_SCROLL_END,
1046                 mockDelegate.mMostRecentGestureEvent.mType);
1047         assertEquals("Only GESTURE_TAP_DOWN, " +
1048                 "GESTURE_SHOW_PRESSED_STATE, " +
1049                 "GESTURE_TAP_UNCONFIRMED," +
1050                 "GESTURE_TAP_CANCEL, " +
1051                 "GESTURE_TAP_DOWN, " +
1052                 "GESTURE_TAP_CANCEL, " +
1053                 "GESTURE_SCROLL_START," +
1054                 "GESTURE_PINCH_BEGIN, " +
1055                 "GESTURE_SCROLL_BY," +
1056                 "GESTURE_PINCH_BY, " +
1057                 "GESTURE_PINCH_END, and " +
1058                 "GESTURE_SCROLL_END should have been sent",
1059                 12, mockDelegate.mGestureTypeList.size());
1060     }
1061
1062
1063     /**
1064      * Verify that double tap drag zoom is cancelled if the user presses a
1065      * secondary pointer.
1066      * @throws Exception
1067      */
1068     @SmallTest
1069     @Feature({"Gestures"})
1070     public void testDoubleTapDragZoomCancelledOnSecondaryPointerDown() throws Exception {
1071         final long downTime1 = SystemClock.uptimeMillis();
1072         final long downTime2 = downTime1 + 100;
1073
1074         GestureRecordingMotionEventDelegate mockDelegate =
1075                 new GestureRecordingMotionEventDelegate();
1076         mGestureHandler = new ContentViewGestureHandler(
1077                 getInstrumentation().getTargetContext(), mockDelegate, mMockZoomManager);
1078         mLongPressDetector = new LongPressDetector(
1079                 getInstrumentation().getTargetContext(), mGestureHandler);
1080
1081         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime1, downTime1);
1082         assertTrue(mGestureHandler.onTouchEvent(event));
1083
1084         mGestureHandler.sendShowPressedStateGestureForTesting();
1085         assertEquals("GESTURE_SHOW_PRESSED_STATE should have been sent",
1086                 ContentViewGestureHandler.GESTURE_SHOW_PRESSED_STATE,
1087                         mockDelegate.mMostRecentGestureEvent.mType);
1088         assertEquals("Only GESTURE_TAP_DOWN and GESTURE_SHOW_PRESSED_STATE should have been sent",
1089                 2, mockDelegate.mGestureTypeList.size());
1090
1091         event = MotionEvent.obtain(
1092                 downTime1, downTime1 + 5, MotionEvent.ACTION_UP,
1093                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1094         mGestureHandler.onTouchEvent(event);
1095         assertEquals("A GESTURE_SINGLE_TAP_UNCONFIRMED event should have been sent",
1096                 ContentViewGestureHandler.GESTURE_SINGLE_TAP_UNCONFIRMED,
1097                 mockDelegate.mMostRecentGestureEvent.mType);
1098         assertEquals("Only GESTURE_TAP_DOWN, " +
1099                 "GESTURE_SHOW_PRESSED_STATE and " +
1100                 "GESTURE_TAP_UNCONFIRMED " +
1101                 "should have been sent",
1102                 3, mockDelegate.mGestureTypeList.size());
1103
1104         event = MotionEvent.obtain(
1105                 downTime2, downTime2, MotionEvent.ACTION_DOWN,
1106                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1107         assertTrue(mGestureHandler.onTouchEvent(event));
1108         assertEquals("GESTURE_TAP_DOWN should have been sent",
1109                 ContentViewGestureHandler.GESTURE_TAP_DOWN,
1110                 mockDelegate.mMostRecentGestureEvent.mType);
1111         assertEquals("Only GESTURE_TAP_DOWN, " +
1112                 "GESTURE_SHOW_PRESSED_STATE, " +
1113                 "GESTURE_TAP_UNCONFIRMED," +
1114                 "GESTURE_TAP_CANCEL and" +
1115                 "GESTURE_TAP_DOWN should have been sent",
1116                 5, mockDelegate.mGestureTypeList.size());
1117
1118         event = MotionEvent.obtain(
1119                 downTime2, downTime2 + 5, MotionEvent.ACTION_MOVE,
1120                 FAKE_COORD_X, FAKE_COORD_Y + 100, 0);
1121         assertTrue(mGestureHandler.onTouchEvent(event));
1122         assertTrue("GESTURE_SCROLL_START should have been sent",
1123                 mockDelegate.mGestureTypeList.contains(
1124                         ContentViewGestureHandler.GESTURE_SCROLL_START));
1125
1126         assertEquals("GESTURE_PINCH_BEGIN should have been sent",
1127                 ContentViewGestureHandler.GESTURE_PINCH_BEGIN,
1128                 mockDelegate.mMostRecentGestureEvent.mType);
1129         assertEquals("Only GESTURE_TAP_DOWN, " +
1130                 "GESTURE_SHOW_PRESSED_STATE, " +
1131                 "GESTURE_TAP_UNCONFIRMED," +
1132                 "GESTURE_TAP_CANCEL, " +
1133                 "GESTURE_TAP_DOWN, " +
1134                 "GESTURE_TAP_CANCEL, " +
1135                 "GESTURE_SCROLL_START, and " +
1136                 "GESTURE_PINCH_BEGIN should have been sent",
1137                 8, mockDelegate.mGestureTypeList.size());
1138
1139         event = MotionEvent.obtain(
1140                 downTime2, downTime2 + 10, MotionEvent.ACTION_POINTER_DOWN,
1141                 FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
1142         mGestureHandler.onTouchEvent(event);
1143         assertTrue("GESTURE_PINCH_END should have been sent",
1144                 mockDelegate.mGestureTypeList.contains(
1145                         ContentViewGestureHandler.GESTURE_PINCH_END));
1146         assertEquals("GESTURE_SCROLL_END should have been sent",
1147                 ContentViewGestureHandler.GESTURE_SCROLL_END,
1148                 mockDelegate.mMostRecentGestureEvent.mType);
1149         assertEquals("Only GESTURE_TAP_DOWN, " +
1150                 "GESTURE_SHOW_PRESSED_STATE, " +
1151                 "GESTURE_TAP_UNCONFIRMED," +
1152                 "GESTURE_TAP_CANCEL, " +
1153                 "GESTURE_TAP_DOWN, " +
1154                 "GESTURE_TAP_CANCEL, " +
1155                 "GESTURE_SCROLL_START," +
1156                 "GESTURE_PINCH_BEGIN, " +
1157                 "GESTURE_PINCH_END, and " +
1158                 "GESTURE_SCROLL_END should have been sent",
1159                 10, mockDelegate.mGestureTypeList.size());
1160
1161         event = MotionEvent.obtain(
1162                 downTime2, downTime2 + 15, MotionEvent.ACTION_POINTER_UP,
1163                 FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
1164         mGestureHandler.onTouchEvent(event);
1165         assertEquals("No new gestures should have been sent",
1166                 10, mockDelegate.mGestureTypeList.size());
1167
1168         event = MotionEvent.obtain(
1169                 downTime2, downTime2 + 20, MotionEvent.ACTION_UP,
1170                 FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
1171         mGestureHandler.onTouchEvent(event);
1172         assertEquals("No new gestures should have been sent",
1173                 10, mockDelegate.mGestureTypeList.size());
1174     }
1175
1176     /**
1177      * Verify that double tap drag zoom feature is not invoked
1178      * when it is disabled..
1179      * @throws Exception
1180      */
1181     @SmallTest
1182     @Feature({"Gestures"})
1183     public void testDoubleTapDragZoomNothingWhenDisabled() throws Exception {
1184         final long downTime1 = SystemClock.uptimeMillis();
1185         final long downTime2 = downTime1 + 100;
1186
1187         GestureRecordingMotionEventDelegate mockDelegate =
1188                 new GestureRecordingMotionEventDelegate();
1189         mGestureHandler = new ContentViewGestureHandler(
1190                 getInstrumentation().getTargetContext(), mockDelegate, mMockZoomManager);
1191         mGestureHandler.updateDoubleTapSupport(false);
1192
1193         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime1, downTime1);
1194         assertTrue(mGestureHandler.onTouchEvent(event));
1195
1196         event = MotionEvent.obtain(
1197                 downTime1, downTime1 + 5, MotionEvent.ACTION_UP,
1198                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1199         mGestureHandler.onTouchEvent(event);
1200
1201         event = MotionEvent.obtain(
1202                 downTime2, downTime2, MotionEvent.ACTION_DOWN,
1203                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1204         assertTrue(mGestureHandler.onTouchEvent(event));
1205
1206         event = MotionEvent.obtain(
1207                 downTime2, downTime2 + 5, MotionEvent.ACTION_MOVE,
1208                 FAKE_COORD_X, FAKE_COORD_Y + 100, 0);
1209
1210         // The move should become a scroll, as double tap and drag to zoom is
1211         // disabled.
1212         assertTrue(mGestureHandler.onTouchEvent(event));
1213         assertTrue("GESTURE_SCROLL_START should have been sent",
1214                 mockDelegate.mGestureTypeList.contains(
1215                         ContentViewGestureHandler.GESTURE_SCROLL_START));
1216         assertFalse("No GESTURE_PINCH_BEGIN should have been sent",
1217                 mockDelegate.mGestureTypeList.contains(
1218                         ContentViewGestureHandler.GESTURE_PINCH_BEGIN));
1219
1220         event = MotionEvent.obtain(
1221                 downTime2, downTime2 + 10, MotionEvent.ACTION_MOVE,
1222                 FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
1223         assertTrue(mGestureHandler.onTouchEvent(event));
1224         assertEquals("GESTURE_SCROLL_BY should have been sent",
1225                 ContentViewGestureHandler.GESTURE_SCROLL_BY,
1226                 mockDelegate.mMostRecentGestureEvent.mType);
1227         assertEquals("GESTURE_SCROLL_BY should have been sent",
1228                 event.getEventTime(),
1229                 mockDelegate.mMostRecentGestureEvent.getTimeMs());
1230         assertTrue("No GESTURE_PINCH_BY should have been sent",
1231                 ContentViewGestureHandler.GESTURE_PINCH_BY !=
1232                 mockDelegate.mMostRecentGestureEvent.mType);
1233
1234         event = MotionEvent.obtain(
1235                 downTime2, downTime2 + 15, MotionEvent.ACTION_UP,
1236                 FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
1237         assertTrue(mGestureHandler.onTouchEvent(event));
1238         assertFalse("No GESTURE_PINCH_END should have been sent",
1239                 mockDelegate.mGestureTypeList.contains(
1240                         ContentViewGestureHandler.GESTURE_PINCH_END));
1241     }
1242
1243     /**
1244      * Mock MotionEventDelegate that remembers the most recent gesture event and any
1245      * currently active scroll start event.
1246      */
1247     static class GestureRecordingMotionEventDelegate implements MotionEventDelegate {
1248         static class GestureEvent {
1249             private final int mType;
1250             private final long mTimeMs;
1251             private final int mX;
1252             private final int mY;
1253             private final Bundle mExtraParams;
1254
1255             public GestureEvent(int type, long timeMs, int x, int y, Bundle extraParams) {
1256                 mType = type;
1257                 mTimeMs = timeMs;
1258                 mX = x;
1259                 mY = y;
1260                 mExtraParams = extraParams;
1261             }
1262
1263             public int getType() {
1264                 return mType;
1265             }
1266
1267             public long getTimeMs() {
1268                 return mTimeMs;
1269             }
1270
1271             public int getX() {
1272                 return mX;
1273             }
1274
1275             public int getY() {
1276                 return mY;
1277             }
1278
1279             public Bundle getExtraParams() {
1280                 return mExtraParams;
1281             }
1282         }
1283         private GestureEvent mMostRecentGestureEvent;
1284         private GestureEvent mActiveScrollStartEvent;
1285         private final ArrayList<Integer> mGestureTypeList = new ArrayList<Integer>();
1286         private final ArrayList<Long> mGestureTimeList = new ArrayList<Long>();
1287
1288         @Override
1289         public boolean sendTouchEvent(long timeMs, int action, TouchPoint[] pts) {
1290             return true;
1291         }
1292
1293         @Override
1294         public boolean sendGesture(int type, long timeMs, int x, int y, Bundle extraParams) {
1295             Log.i(TAG, "Gesture event received with type id " + type);
1296             mMostRecentGestureEvent = new GestureEvent(type, timeMs, x, y, extraParams);
1297             mGestureTypeList.add(mMostRecentGestureEvent.mType);
1298             mGestureTimeList.add(timeMs);
1299             if (type == ContentViewGestureHandler.GESTURE_SCROLL_START)
1300                 mActiveScrollStartEvent = mMostRecentGestureEvent;
1301             else if (type == ContentViewGestureHandler.GESTURE_SCROLL_END ||
1302                          type == ContentViewGestureHandler.GESTURE_FLING_CANCEL)
1303                 mActiveScrollStartEvent = null;
1304             return true;
1305         }
1306
1307         @Override
1308         public void sendSingleTapUMA(int type) {
1309             // Not implemented.
1310         }
1311
1312         @Override
1313         public void sendActionAfterDoubleTapUMA(int type,
1314                 boolean clickDelayEnabled) {
1315             // Not implemented.
1316         }
1317
1318         @Override
1319         public void invokeZoomPicker() {
1320             // Not implemented.
1321         }
1322
1323         public GestureEvent getMostRecentGestureEvent() {
1324             return mMostRecentGestureEvent;
1325         }
1326
1327         public GestureEvent getActiveScrollStartEvent() {
1328             return mActiveScrollStartEvent;
1329         }
1330     }
1331
1332     /**
1333      * Generate a scroll gesture and verify that the resulting scroll motion event has both absolute
1334      * and relative position information.
1335      */
1336     @SmallTest
1337     @Feature({"Gestures"})
1338     public void testScrollUpdateCoordinates() {
1339         final int deltaX = 16;
1340         final int deltaY = 84;
1341         final long downTime = SystemClock.uptimeMillis();
1342
1343         GestureRecordingMotionEventDelegate delegate = new GestureRecordingMotionEventDelegate();
1344         ContentViewGestureHandler gestureHandler = new ContentViewGestureHandler(
1345                 getInstrumentation().getTargetContext(), delegate, mMockZoomManager);
1346         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
1347         assertTrue(gestureHandler.onTouchEvent(event));
1348
1349         // Move twice so that we get two GESTURE_SCROLL_BY events and can compare
1350         // the relative and absolute coordinates.
1351         event = MotionEvent.obtain(
1352                 downTime, downTime + 5, MotionEvent.ACTION_MOVE,
1353                 FAKE_COORD_X - deltaX / 2, FAKE_COORD_Y - deltaY / 2, 0);
1354         assertTrue(gestureHandler.onTouchEvent(event));
1355
1356         event = MotionEvent.obtain(
1357                 downTime, downTime + 10, MotionEvent.ACTION_MOVE,
1358                 FAKE_COORD_X - deltaX, FAKE_COORD_Y - deltaY, 0);
1359         assertTrue(gestureHandler.onTouchEvent(event));
1360
1361         // Make sure the reported gesture event has all the expected data.
1362         GestureRecordingMotionEventDelegate.GestureEvent gestureEvent =
1363                 delegate.getMostRecentGestureEvent();
1364         assertNotNull(gestureEvent);
1365         assertEquals(ContentViewGestureHandler.GESTURE_SCROLL_BY, gestureEvent.getType());
1366         assertEquals(downTime + 10, gestureEvent.getTimeMs());
1367         assertEquals(FAKE_COORD_X - deltaX, gestureEvent.getX());
1368         assertEquals(FAKE_COORD_Y - deltaY, gestureEvent.getY());
1369
1370         Bundle extraParams = gestureEvent.getExtraParams();
1371         assertNotNull(extraParams);
1372         // No horizontal delta because of snapping.
1373         assertEquals(0, extraParams.getInt(ContentViewGestureHandler.DISTANCE_X));
1374         assertEquals(deltaY / 2, extraParams.getInt(ContentViewGestureHandler.DISTANCE_Y));
1375     }
1376
1377     /**
1378      * Generate a scroll gesture and verify that the resulting scroll start event
1379      * has the expected hint values.
1380      */
1381     @SmallTest
1382     @Feature({"Gestures"})
1383     public void testScrollStartValues() {
1384         final int deltaX = 13;
1385         final int deltaY = 89;
1386         final long downTime = SystemClock.uptimeMillis();
1387
1388         GestureRecordingMotionEventDelegate delegate = new GestureRecordingMotionEventDelegate();
1389         ContentViewGestureHandler gestureHandler = new ContentViewGestureHandler(
1390                 getInstrumentation().getTargetContext(), delegate, mMockZoomManager);
1391         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
1392         assertTrue(gestureHandler.onTouchEvent(event));
1393
1394         // Move twice such that the first event isn't sufficient to start
1395         // scrolling on it's own.
1396         event = MotionEvent.obtain(
1397                 downTime, downTime + 5, MotionEvent.ACTION_MOVE,
1398                 FAKE_COORD_X + 2, FAKE_COORD_Y + 1, 0);
1399         assertFalse(gestureHandler.onTouchEvent(event));
1400         assertNull("Expect scrolling hasn't yet started",
1401                 delegate.getActiveScrollStartEvent());
1402
1403         event = MotionEvent.obtain(
1404                 downTime, downTime + 10, MotionEvent.ACTION_MOVE,
1405                 FAKE_COORD_X + deltaX, FAKE_COORD_Y + deltaY, 0);
1406         assertTrue(gestureHandler.onTouchEvent(event));
1407
1408         GestureRecordingMotionEventDelegate.GestureEvent startEvent =
1409                 delegate.getActiveScrollStartEvent();
1410         assertNotNull(startEvent);
1411         assertEquals(ContentViewGestureHandler.GESTURE_SCROLL_START, startEvent.getType());
1412         assertEquals(downTime + 10, startEvent.getTimeMs());
1413         assertEquals(FAKE_COORD_X, startEvent.getX());
1414         assertEquals(FAKE_COORD_Y, startEvent.getY());
1415
1416         Bundle extraParams = startEvent.getExtraParams();
1417         assertNotNull(extraParams);
1418         assertEquals(deltaX, extraParams.getInt(ContentViewGestureHandler.DELTA_HINT_X));
1419         assertEquals(deltaY, extraParams.getInt(ContentViewGestureHandler.DELTA_HINT_Y));
1420     }
1421
1422     /**
1423      * Verify that the timer of LONG_PRESS will be cancelled when scrolling begins so
1424      * LONG_PRESS and LONG_TAP won't be triggered.
1425      *
1426      * @throws Exception
1427      */
1428     @SmallTest
1429     @Feature({"Gestures"})
1430     public void testLongPressAndTapCancelWhenScrollBegins() throws Exception {
1431         final long downTime = SystemClock.uptimeMillis();
1432         final long eventTime = SystemClock.uptimeMillis();
1433
1434         GestureRecordingMotionEventDelegate mockDelegate =
1435                 new GestureRecordingMotionEventDelegate();
1436         mGestureHandler = new ContentViewGestureHandler(
1437                 getInstrumentation().getTargetContext(), mockDelegate, mMockZoomManager);
1438         mLongPressDetector = mGestureHandler.getLongPressDetector();
1439
1440         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
1441         assertTrue(mGestureHandler.onTouchEvent(event));
1442         assertTrue("Should have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
1443         event = MotionEvent.obtain(
1444                 downTime, eventTime + 5, MotionEvent.ACTION_MOVE,
1445                 FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
1446         assertTrue(mGestureHandler.onTouchEvent(event));
1447         event = MotionEvent.obtain(
1448                 downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
1449                 FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
1450         assertTrue(mGestureHandler.onTouchEvent(event));
1451         assertTrue("Should not have a pending LONG_PRESS", !mLongPressDetector.hasPendingMessage());
1452
1453         // No LONG_TAP because LONG_PRESS timer is cancelled.
1454         assertFalse("No LONG_PRESS should be sent",
1455                 mockDelegate.mGestureTypeList.contains(
1456                         ContentViewGestureHandler.GESTURE_LONG_PRESS));
1457         assertFalse("No LONG_TAP should be sent",
1458                 mockDelegate.mGestureTypeList.contains(
1459                         ContentViewGestureHandler.GESTURE_LONG_TAP));
1460     }
1461
1462     /**
1463      * Verifies that when hasTouchEventHandlers changes while in a gesture, that the pending
1464      * queue does not grow continually.
1465      */
1466     @SmallTest
1467     @Feature({"Gestures"})
1468     public void testHasTouchEventHandlersChangesInGesture() {
1469         final long downTime = SystemClock.uptimeMillis();
1470         final long eventTime = SystemClock.uptimeMillis();
1471
1472         mGestureHandler = new ContentViewGestureHandler(
1473                 getInstrumentation().getTargetContext(), new MockMotionEventDelegate(),
1474                 mMockZoomManager);
1475         mLongPressDetector = new LongPressDetector(
1476                 getInstrumentation().getTargetContext(), mGestureHandler);
1477
1478         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
1479         assertTrue(mGestureHandler.onTouchEvent(event));
1480         assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1481         assertFalse("Should not have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
1482
1483         event = MotionEvent.obtain(
1484                 downTime, eventTime + 5, MotionEvent.ACTION_MOVE,
1485                 FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
1486         assertTrue(mGestureHandler.onTouchEvent(event));
1487         assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1488
1489         mGestureHandler.hasTouchEventHandlers(true);
1490
1491         event = MotionEvent.obtain(
1492                 downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
1493                 FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
1494         assertTrue(mGestureHandler.onTouchEvent(event));
1495         assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1496
1497         event = MotionEvent.obtain(
1498                 downTime, eventTime + 15, MotionEvent.ACTION_MOVE,
1499                 FAKE_COORD_X * 15, FAKE_COORD_Y * 15, 0);
1500         assertTrue(mGestureHandler.onTouchEvent(event));
1501         assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1502     }
1503
1504     /**
1505      * Verify that LONG_TAP is triggered after LongPress followed by an UP.
1506      *
1507      * @throws Exception
1508      */
1509     @SmallTest
1510     @Feature({"Gestures"})
1511     public void testGestureLongTap() throws Exception {
1512         final long downTime = SystemClock.uptimeMillis();
1513         final long eventTime = SystemClock.uptimeMillis();
1514
1515         GestureRecordingMotionEventDelegate mockDelegate =
1516                 new GestureRecordingMotionEventDelegate();
1517         mGestureHandler = new ContentViewGestureHandler(
1518                 getInstrumentation().getTargetContext(), mockDelegate, mMockZoomManager);
1519         mLongPressDetector = mGestureHandler.getLongPressDetector();
1520
1521         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
1522         assertTrue(mGestureHandler.onTouchEvent(event));
1523         assertTrue("Should have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
1524
1525         mLongPressDetector.sendLongPressGestureForTest();
1526
1527         assertEquals("A LONG_PRESS gesture should have been sent",
1528                 ContentViewGestureHandler.GESTURE_LONG_PRESS,
1529                         mockDelegate.mMostRecentGestureEvent.mType);
1530
1531         event = motionEvent(MotionEvent.ACTION_UP, downTime, eventTime + 1000);
1532         assertTrue(mGestureHandler.onTouchEvent(event));
1533         assertEquals("A LONG_TAP gesture should have been sent",
1534                 ContentViewGestureHandler.GESTURE_LONG_TAP,
1535                         mockDelegate.mMostRecentGestureEvent.mType);
1536     }
1537
1538     /**
1539      * Verify that the touch slop region is removed from the first scroll delta to avoid a jump when
1540      * starting to scroll.
1541      * @throws Exception
1542      */
1543     @SmallTest
1544     @Feature({"Gestures"})
1545     public void testTouchSlopRemovedFromScroll() throws Exception {
1546         Context context = getInstrumentation().getTargetContext();
1547         final long downTime = SystemClock.uptimeMillis();
1548         final long eventTime = SystemClock.uptimeMillis();
1549         final int scaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
1550         final int scrollDelta = 5;
1551
1552         GestureRecordingMotionEventDelegate mockDelegate =
1553                 new GestureRecordingMotionEventDelegate();
1554         mGestureHandler = new ContentViewGestureHandler(
1555                 context, mockDelegate, mMockZoomManager);
1556
1557         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
1558         assertTrue(mGestureHandler.onTouchEvent(event));
1559
1560         event = MotionEvent.obtain(
1561                 downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
1562                 FAKE_COORD_X, FAKE_COORD_Y + scaledTouchSlop + scrollDelta, 0);
1563         assertTrue(mGestureHandler.onTouchEvent(event));
1564
1565         assertEquals("We should have started scrolling",
1566                 ContentViewGestureHandler.GESTURE_SCROLL_BY,
1567                 mockDelegate.mMostRecentGestureEvent.mType);
1568
1569         GestureRecordingMotionEventDelegate.GestureEvent gestureEvent =
1570                 mockDelegate.getMostRecentGestureEvent();
1571         assertNotNull(gestureEvent);
1572         Bundle extraParams = gestureEvent.getExtraParams();
1573         assertEquals(0, extraParams.getInt(ContentViewGestureHandler.DISTANCE_X));
1574         assertEquals(-scrollDelta, extraParams.getInt(ContentViewGestureHandler.DISTANCE_Y));
1575     }
1576
1577     /**
1578      * Verify that touch moves are deferred if they are within the touch slop region
1579      * and the touch sequence is not being consumed.
1580      * @throws Exception
1581      */
1582     @SmallTest
1583     @Feature({"Gestures"})
1584     public void testTouchMoveWithinTouchSlopDeferred() throws Exception {
1585         Context context = getInstrumentation().getTargetContext();
1586         final long downTime = SystemClock.uptimeMillis();
1587         final long eventTime = SystemClock.uptimeMillis();
1588         final int scaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
1589         final int lessThanSlopScrollDelta = scaledTouchSlop / 2;
1590         final int greaterThanSlopScrollDelta = scaledTouchSlop * 2;
1591
1592         mGestureHandler.hasTouchEventHandlers(true);
1593
1594         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
1595         assertTrue(mGestureHandler.onTouchEvent(event));
1596         assertEquals("The touch down should have been forwarded",
1597                 TouchPoint.TOUCH_EVENT_TYPE_START, mMockMotionEventDelegate.mLastTouchAction);
1598         assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1599
1600         event = MotionEvent.obtain(
1601                 downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
1602                 FAKE_COORD_X, FAKE_COORD_Y + lessThanSlopScrollDelta, 0);
1603         assertTrue(mGestureHandler.onTouchEvent(event));
1604         assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1605
1606         mGestureHandler.confirmTouchEvent(
1607                 ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
1608         assertEquals("The less-than-slop touch move should not have been forwarded",
1609                 TouchPoint.TOUCH_EVENT_TYPE_START, mMockMotionEventDelegate.mLastTouchAction);
1610         assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1611
1612         event = MotionEvent.obtain(
1613                 downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
1614                 FAKE_COORD_X, FAKE_COORD_Y + greaterThanSlopScrollDelta, 0);
1615         assertTrue(mGestureHandler.onTouchEvent(event));
1616         assertEquals("The touch move should have been forwarded",
1617                 TouchPoint.TOUCH_EVENT_TYPE_MOVE, mMockMotionEventDelegate.mLastTouchAction);
1618         mGestureHandler.confirmTouchEvent(
1619                 ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
1620         assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1621     }
1622
1623     /**
1624      * Verify that touch moves are not deferred even if they are within the touch slop region
1625      * when the touch sequence is being consumed.
1626      * @throws Exception
1627      */
1628     @SmallTest
1629     @Feature({"Gestures"})
1630     public void testTouchMoveWithinTouchSlopNotDeferredIfJavascriptConsumingGesture()
1631             throws Exception {
1632         Context context = getInstrumentation().getTargetContext();
1633         final long downTime = SystemClock.uptimeMillis();
1634         final long eventTime = SystemClock.uptimeMillis();
1635         final int scaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
1636         final int lessThanSlopScrollDelta = scaledTouchSlop / 2;
1637
1638         mGestureHandler.hasTouchEventHandlers(true);
1639
1640         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
1641         assertTrue(mGestureHandler.onTouchEvent(event));
1642         assertEquals("The touch down should have been forwarded",
1643                 TouchPoint.TOUCH_EVENT_TYPE_START, mMockMotionEventDelegate.mLastTouchAction);
1644         assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1645
1646         event = MotionEvent.obtain(
1647                 downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
1648                 FAKE_COORD_X, FAKE_COORD_Y + lessThanSlopScrollDelta, 0);
1649         assertTrue(mGestureHandler.onTouchEvent(event));
1650         assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1651
1652         mGestureHandler.confirmTouchEvent(
1653                 ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_CONSUMED);
1654         assertEquals("The less-than-slop touch move should have been forwarded",
1655                 TouchPoint.TOUCH_EVENT_TYPE_MOVE, mMockMotionEventDelegate.mLastTouchAction);
1656     }
1657
1658
1659     /**
1660      * Verify that touch moves are not deferred when the MotionEvent has multiple active pointers.
1661      * @throws Exception
1662      */
1663     @SmallTest
1664     @Feature({"Gestures"})
1665     public void testTouchMoveNotDeferredWithMultiplePointers()
1666             throws Exception {
1667         Context context = getInstrumentation().getTargetContext();
1668         final long downTime = SystemClock.uptimeMillis();
1669         final long eventTime = SystemClock.uptimeMillis();
1670         final int scaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
1671         final int lessThanSlopScrollDelta = scaledTouchSlop / 2;
1672
1673         mGestureHandler.hasTouchEventHandlers(true);
1674
1675         final int secondaryCoordX = FAKE_COORD_X + 10 * scaledTouchSlop;
1676         final int secondaryCoordY = FAKE_COORD_Y + 10 * scaledTouchSlop;
1677
1678         PointerProperties pp0 = new PointerProperties();
1679         pp0.id = 0;
1680         pp0.toolType = MotionEvent.TOOL_TYPE_FINGER;
1681         PointerProperties pp1 = new PointerProperties();
1682         pp1.id = 1;
1683         pp1.toolType = MotionEvent.TOOL_TYPE_FINGER;
1684
1685         PointerCoords pc0 = new PointerCoords();
1686         pc0.x = FAKE_COORD_X;
1687         pc0.y = FAKE_COORD_Y;
1688         pc0.pressure = 1;
1689         pc0.size = 1;
1690         PointerCoords pc1 = new PointerCoords();
1691         pc1.x = secondaryCoordX;
1692         pc1.y = secondaryCoordY;
1693         pc1.pressure = 1;
1694         pc1.size = 1;
1695
1696         MotionEvent event = MotionEvent.obtain(
1697                 eventTime, eventTime, MotionEvent.ACTION_DOWN,
1698                 1, new PointerProperties[] { pp0 }, new PointerCoords[] { pc0 },
1699                 0, 0, 1.0f, 1.0f, 0, 0, 0, 0);
1700         assertTrue(mGestureHandler.onTouchEvent(event));
1701         assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1702         assertEquals("The touch down should have been forwarded",
1703                 TouchPoint.TOUCH_EVENT_TYPE_START, mMockMotionEventDelegate.mLastTouchAction);
1704
1705         event = MotionEvent.obtain(
1706                 eventTime, eventTime, MotionEvent.ACTION_POINTER_DOWN,
1707                 2, new PointerProperties[] { pp0, pp1 }, new PointerCoords[] { pc0, pc1 },
1708                 0, 0, 1.0f, 1.0f, 0, 0, 0, 0);
1709         assertTrue(mGestureHandler.onTouchEvent(event));
1710         assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1711
1712         mGestureHandler.confirmTouchEvent(
1713                 ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
1714         assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1715         assertEquals("The secondary touch down should have been forwarded",
1716                 TouchPoint.TOUCH_EVENT_TYPE_START, mMockMotionEventDelegate.mLastTouchAction);
1717
1718         pc1.x = secondaryCoordX + lessThanSlopScrollDelta;
1719         pc1.y = secondaryCoordY + lessThanSlopScrollDelta;
1720
1721         event = MotionEvent.obtain(
1722                 eventTime, eventTime, MotionEvent.ACTION_MOVE,
1723                 2, new PointerProperties[] { pp0, pp1 }, new PointerCoords[] { pc0, pc1 },
1724                 0, 0, 1.0f, 1.0f, 0, 0, 0, 0);
1725         assertTrue(mGestureHandler.onTouchEvent(event));
1726         assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1727
1728         mGestureHandler.confirmTouchEvent(
1729                 ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
1730         assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1731         assertEquals("The secondary touch move should have been forwarded",
1732                 TouchPoint.TOUCH_EVENT_TYPE_MOVE, mMockMotionEventDelegate.mLastTouchAction);
1733     }
1734
1735     private static void sendLastScrollByEvent(ContentViewGestureHandler handler) {
1736         final long downTime = SystemClock.uptimeMillis();
1737         final long eventTime = SystemClock.uptimeMillis();
1738         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
1739         assertTrue(handler.onTouchEvent(event));
1740         event = MotionEvent.obtain(
1741                 downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
1742                 FAKE_COORD_X, FAKE_COORD_Y + 30, 0);
1743         assertTrue(handler.onTouchEvent(event));
1744     }
1745
1746     private static void sendLastZoomEvent(
1747             ContentViewGestureHandler handler, MockZoomManager zoomManager) {
1748         zoomManager.pinchOnMoveEvents(handler);
1749         final long downTime = SystemClock.uptimeMillis();
1750         final long eventTime = SystemClock.uptimeMillis();
1751         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
1752         assertTrue(handler.onTouchEvent(event));
1753         event = MotionEvent.obtain(
1754                 downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
1755                 FAKE_COORD_X, FAKE_COORD_Y + 30, 0);
1756         assertTrue(handler.onTouchEvent(event));
1757     }
1758
1759     private static void sendLastPinchEvent(ContentViewGestureHandler handler) {
1760         final long downTime = SystemClock.uptimeMillis();
1761         final long eventTime = SystemClock.uptimeMillis();
1762         handler.pinchBegin(downTime, FAKE_COORD_X, FAKE_COORD_Y);
1763         handler.pinchBy(eventTime + 10, FAKE_COORD_X, FAKE_COORD_Y, 2);
1764     }
1765
1766     /**
1767      * Verify that a DOWN followed shortly by an UP will trigger
1768      * a GESTURE_SINGLE_TAP_UNCONFIRMED event immediately.
1769      *
1770      * @throws Exception
1771      */
1772     @SmallTest
1773     @Feature({"Gestures"})
1774     public void testGestureEventsSingleTapUnconfirmed() throws Exception {
1775         final long downTime = SystemClock.uptimeMillis();
1776         final long eventTime = SystemClock.uptimeMillis();
1777
1778         GestureRecordingMotionEventDelegate mockDelegate =
1779                 new GestureRecordingMotionEventDelegate();
1780         mGestureHandler = new ContentViewGestureHandler(
1781                 getInstrumentation().getTargetContext(), mockDelegate, mMockZoomManager);
1782
1783         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
1784         assertTrue(mGestureHandler.onTouchEvent(event));
1785         assertEquals("A TAP_DOWN gesture should have been sent",
1786                 ContentViewGestureHandler.GESTURE_TAP_DOWN,
1787                         mockDelegate.mMostRecentGestureEvent.mType);
1788
1789         event = motionEvent(MotionEvent.ACTION_UP, downTime, eventTime + 10);
1790         assertFalse(mGestureHandler.onTouchEvent(event));
1791         assertEquals("A GESTURE_SINGLE_TAP_UNCONFIRMED gesture should have been sent",
1792                 ContentViewGestureHandler.GESTURE_SINGLE_TAP_UNCONFIRMED,
1793                         mockDelegate.mMostRecentGestureEvent.mType);
1794
1795         assertTrue("Should not have confirmed a single tap yet",
1796                 mMockListener.mLastSingleTap == null);
1797     }
1798
1799     /**
1800      * Verify that a tap-ending event will follow a TAP_DOWN event.
1801      *
1802      * @throws Exception
1803      */
1804     @SmallTest
1805     @Feature({"Gestures"})
1806     public void testTapDownFollowedByTapEndingEvent() throws Exception {
1807         long downTime = SystemClock.uptimeMillis();
1808         long eventTime = SystemClock.uptimeMillis();
1809
1810         GestureRecordingMotionEventDelegate mockDelegate =
1811                 new GestureRecordingMotionEventDelegate();
1812         mGestureHandler = new ContentViewGestureHandler(
1813                 getInstrumentation().getTargetContext(), mockDelegate, mMockZoomManager);
1814
1815         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
1816         assertTrue(mGestureHandler.onTouchEvent(event));
1817         assertEquals(ContentViewGestureHandler.GESTURE_TAP_DOWN,
1818                 mockDelegate.mMostRecentGestureEvent.mType);
1819         assertTrue(mGestureHandler.needsTapEndingEventForTesting());
1820         event = motionEvent(MotionEvent.ACTION_UP, downTime, eventTime + 5);
1821         assertFalse(mGestureHandler.onTouchEvent(event));
1822         assertEquals(ContentViewGestureHandler.GESTURE_SINGLE_TAP_UNCONFIRMED,
1823                 mockDelegate.mMostRecentGestureEvent.mType);
1824         assertTrue("An unconfirmed tap does not terminate the tap down.",
1825                 mGestureHandler.needsTapEndingEventForTesting());
1826
1827         // A confirmed tap is a tap-ending event.
1828         downTime += 20;
1829         eventTime += 20;
1830         mockDelegate.mGestureTypeList.clear();
1831         mGestureHandler.updateShouldDisableDoubleTap(true);
1832         event = MotionEvent.obtain(
1833                 downTime, downTime, MotionEvent.ACTION_DOWN,
1834                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1835         assertTrue(mGestureHandler.onTouchEvent(event));
1836         assertTrue(mGestureHandler.needsTapEndingEventForTesting());
1837         event = MotionEvent.obtain(
1838                 downTime, eventTime + 5, MotionEvent.ACTION_UP,
1839                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1840         assertTrue(mGestureHandler.onTouchEvent(event));
1841         assertEquals(ContentViewGestureHandler.GESTURE_SINGLE_TAP_CONFIRMED,
1842                 mockDelegate.mMostRecentGestureEvent.mType);
1843         assertFalse("A confirmed single tap should terminate the tap down.",
1844                 mGestureHandler.needsTapEndingEventForTesting());
1845
1846         // A double tap gesture is a tap-ending event.
1847         downTime += 20;
1848         eventTime += 20;
1849         mockDelegate.mGestureTypeList.clear();
1850         mGestureHandler.updateShouldDisableDoubleTap(false);
1851         event = MotionEvent.obtain(
1852                 downTime, downTime, MotionEvent.ACTION_DOWN,
1853                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1854         assertTrue(mGestureHandler.onTouchEvent(event));
1855         assertTrue(mGestureHandler.needsTapEndingEventForTesting());
1856         event = MotionEvent.obtain(
1857                 downTime, eventTime + 5, MotionEvent.ACTION_UP,
1858                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1859         event = MotionEvent.obtain(
1860                 eventTime + 10, eventTime + 10, MotionEvent.ACTION_DOWN,
1861                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1862         assertTrue(mGestureHandler.onTouchEvent(event));
1863         assertTrue(mGestureHandler.needsTapEndingEventForTesting());
1864         event = MotionEvent.obtain(
1865                 eventTime + 10, eventTime + 15, MotionEvent.ACTION_UP,
1866                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1867         assertTrue(mGestureHandler.onTouchEvent(event));
1868         assertEquals(ContentViewGestureHandler.GESTURE_DOUBLE_TAP,
1869                 mockDelegate.mMostRecentGestureEvent.mType);
1870         assertFalse("A double tap should terminate the tap down.",
1871                 mGestureHandler.needsTapEndingEventForTesting());
1872
1873         // A double tap drag gesture will trigger a tap-ending event.
1874         downTime += 20;
1875         eventTime += 20;
1876         mockDelegate.mGestureTypeList.clear();
1877         mGestureHandler.updateShouldDisableDoubleTap(false);
1878         event = MotionEvent.obtain(
1879                 downTime, downTime, MotionEvent.ACTION_DOWN,
1880                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1881         assertTrue(mGestureHandler.onTouchEvent(event));
1882         assertTrue(mGestureHandler.needsTapEndingEventForTesting());
1883         event = MotionEvent.obtain(
1884                 downTime, eventTime + 5, MotionEvent.ACTION_UP,
1885                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1886         event = MotionEvent.obtain(
1887                 eventTime + 10, eventTime + 10, MotionEvent.ACTION_DOWN,
1888                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1889         assertTrue(mGestureHandler.onTouchEvent(event));
1890         assertTrue(mGestureHandler.needsTapEndingEventForTesting());
1891         event = MotionEvent.obtain(
1892                 eventTime + 10, eventTime + 15, MotionEvent.ACTION_MOVE,
1893                 FAKE_COORD_X, FAKE_COORD_Y + 100, 0);
1894         assertTrue(mGestureHandler.onTouchEvent(event));
1895         assertFalse("A double tap drag should terminate the tap down.",
1896                 mGestureHandler.needsTapEndingEventForTesting());
1897         assertTrue(mockDelegate.mGestureTypeList.contains(
1898                 ContentViewGestureHandler.GESTURE_SCROLL_START));
1899         assertTrue(mockDelegate.mGestureTypeList.contains(
1900                 ContentViewGestureHandler.GESTURE_TAP_CANCEL));
1901         event = MotionEvent.obtain(
1902                 eventTime + 10, eventTime + 20, MotionEvent.ACTION_UP,
1903                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1904         assertTrue(mGestureHandler.onTouchEvent(event));
1905         assertFalse(mGestureHandler.needsTapEndingEventForTesting());
1906
1907         // A scroll event will trigger a tap-ending (cancel) event.
1908         downTime += 25;
1909         eventTime += 25;
1910         mockDelegate.mGestureTypeList.clear();
1911         event = MotionEvent.obtain(
1912                 downTime, downTime, MotionEvent.ACTION_DOWN,
1913                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1914         assertTrue(mGestureHandler.onTouchEvent(event));
1915         assertTrue(mGestureHandler.needsTapEndingEventForTesting());
1916         event = MotionEvent.obtain(
1917                 downTime, eventTime + 5, MotionEvent.ACTION_MOVE,
1918                 FAKE_COORD_X, FAKE_COORD_Y + 100, 0);
1919         assertTrue(mGestureHandler.onTouchEvent(event));
1920         assertTrue(mockDelegate.mGestureTypeList.contains(
1921                 ContentViewGestureHandler.GESTURE_SCROLL_START));
1922         assertTrue(mockDelegate.mGestureTypeList.contains(
1923                 ContentViewGestureHandler.GESTURE_TAP_CANCEL));
1924         assertFalse("A scroll should terminate the tap down.",
1925                    mGestureHandler.needsTapEndingEventForTesting());
1926         assertFalse(mGestureHandler.needsTapEndingEventForTesting());
1927     }
1928
1929     /**
1930      * Verify that touch move events are properly coalesced.
1931      * @throws Exception
1932      */
1933     @SmallTest
1934     @Feature({"Gestures"})
1935     public void testTouchMoveCoalescing() throws Exception {
1936         final long downTime = SystemClock.uptimeMillis();
1937         final long eventTime = SystemClock.uptimeMillis();
1938
1939         mGestureHandler.hasTouchEventHandlers(true);
1940
1941         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
1942         assertTrue(mGestureHandler.onTouchEvent(event));
1943         mGestureHandler.confirmTouchEvent(ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_CONSUMED);
1944         assertEquals(TouchPoint.TOUCH_EVENT_TYPE_START, mMockMotionEventDelegate.mLastTouchAction);
1945
1946         event = MotionEvent.obtain(
1947                 downTime, eventTime + 5, MotionEvent.ACTION_MOVE,
1948                 FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
1949         assertTrue(mGestureHandler.onTouchEvent(event));
1950         assertFalse("Should not have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
1951         assertEquals("Initial move events should offered to javascript and added to the queue",
1952                 1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1953         assertEquals(TouchPoint.TOUCH_EVENT_TYPE_MOVE, mMockMotionEventDelegate.mLastTouchAction);
1954
1955         event = MotionEvent.obtain(
1956                 downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
1957                 FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
1958         assertTrue(mGestureHandler.onTouchEvent(event));
1959         assertEquals("Move events already sent to javascript should not be coalesced",
1960                 2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1961
1962         event = MotionEvent.obtain(
1963                 downTime, eventTime + 15, MotionEvent.ACTION_MOVE,
1964                 FAKE_COORD_X * 15, FAKE_COORD_Y * 15, 0);
1965         assertTrue(mGestureHandler.onTouchEvent(event));
1966         assertEquals("Similar pending move events should be coalesced",
1967                 2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1968
1969         PointerProperties pp1 = new PointerProperties();
1970         pp1.id = 0;
1971         pp1.toolType = MotionEvent.TOOL_TYPE_FINGER;
1972         PointerProperties pp2 = new PointerProperties();
1973         pp2.id = 1;
1974         pp2.toolType = MotionEvent.TOOL_TYPE_FINGER;
1975         PointerProperties[] properties = new PointerProperties[] { pp1, pp2 };
1976
1977         PointerCoords pc1 = new PointerCoords();
1978         pc1.x = FAKE_COORD_X * 10;
1979         pc1.y = FAKE_COORD_Y * 10;
1980         pc1.pressure = 1;
1981         pc1.size = 1;
1982         PointerCoords pc2 = new PointerCoords();
1983         pc2.x = FAKE_COORD_X * 15;
1984         pc2.y = FAKE_COORD_Y * 15;
1985         pc2.pressure = 1;
1986         pc2.size = 1;
1987         PointerCoords[] coords = new PointerCoords[] { pc1, pc2 };
1988
1989         event = MotionEvent.obtain(
1990                 downTime, eventTime + 20, MotionEvent.ACTION_MOVE,
1991                 2, properties, coords, 0, 0, 1.0f, 1.0f, 0, 0, 0, 0);
1992         assertTrue(mGestureHandler.onTouchEvent(event));
1993         assertEquals("Move events with different pointer counts should not be coalesced",
1994                 3, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
1995
1996         event = MotionEvent.obtain(
1997                 downTime, eventTime + 25, MotionEvent.ACTION_MOVE,
1998                 2, properties, coords, 0, 0, 1.0f, 1.0f, 0, 0, 0, 0);
1999         assertTrue(mGestureHandler.onTouchEvent(event));
2000         assertEquals("Move events with similar pointer counts should be coalesced",
2001                 3, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
2002
2003         event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
2004         assertTrue(mGestureHandler.onTouchEvent(event));
2005         assertEquals("Move events should not be coalesced with other events",
2006                 4, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
2007     }
2008
2009     /**
2010      * Verify that synchronous confirmTouchEvent() calls made from the MotionEventDelegate behave
2011      * properly.
2012      * @throws Exception
2013      */
2014     @SmallTest
2015     @Feature({"Gestures"})
2016     public void testSynchronousConfirmTouchEvent() throws Exception {
2017         final long downTime = SystemClock.uptimeMillis();
2018         final long eventTime = SystemClock.uptimeMillis();
2019
2020         mGestureHandler.hasTouchEventHandlers(true);
2021
2022         mMockMotionEventDelegate.disableSynchronousConfirmTouchEvent();
2023
2024         // Queue an asynchronously handled event.
2025         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
2026         assertTrue(mGestureHandler.onTouchEvent(event));
2027         assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
2028
2029         // Queue another event; this will remain in the queue until the first event is confirmed.
2030         event = MotionEvent.obtain(
2031                 downTime, eventTime + 5, MotionEvent.ACTION_MOVE,
2032                 FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
2033         assertTrue(mGestureHandler.onTouchEvent(event));
2034         assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
2035
2036         // Enable synchronous event confirmation upon dispatch.
2037         mMockMotionEventDelegate.enableSynchronousConfirmTouchEvent(
2038                 mGestureHandler, ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_CONSUMED);
2039
2040         // Confirm the original event; this should dispatch the second event and confirm it
2041         // synchronously.
2042         mGestureHandler.confirmTouchEvent(
2043                 ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_CONSUMED);
2044         assertTrue(mGestureHandler.onTouchEvent(event));
2045         assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
2046         assertEquals(TouchPoint.TOUCH_EVENT_TYPE_MOVE, mMockMotionEventDelegate.mLastTouchAction);
2047
2048         // Adding events to any empty queue will trigger synchronous dispatch and confirmation.
2049         event = MotionEvent.obtain(
2050                 downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
2051                 FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
2052         assertTrue(mGestureHandler.onTouchEvent(event));
2053         assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
2054     }
2055
2056     /**
2057      * Verify that no double tap gestures are created if the gesture handler is
2058      * told to disable double tap gesture detection (according to the logic in
2059      * ContentViewCore.onRenderCoordinatesUpdated).
2060      * @throws Exception
2061      */
2062     @SmallTest
2063     @Feature({"Gestures"})
2064     public void testNoDoubleTapWhenDoubleTapDisabled() throws Exception {
2065         final long downTime = SystemClock.uptimeMillis();
2066         final long eventTime = SystemClock.uptimeMillis();
2067
2068         GestureRecordingMotionEventDelegate mockDelegate =
2069                 new GestureRecordingMotionEventDelegate();
2070         mGestureHandler = new ContentViewGestureHandler(
2071                 getInstrumentation().getTargetContext(), mockDelegate, mMockZoomManager);
2072         mGestureHandler.updateShouldDisableDoubleTap(true);
2073
2074         MotionEvent event = MotionEvent.obtain(
2075                 downTime, downTime, MotionEvent.ACTION_DOWN,
2076                 FAKE_COORD_X, FAKE_COORD_Y, 0);
2077         assertTrue(mGestureHandler.onTouchEvent(event));
2078         assertEquals("Only GESTURE_TAP_DOWN should have been sent",
2079                 1, mockDelegate.mGestureTypeList.size());
2080
2081         event = MotionEvent.obtain(
2082                 downTime, eventTime + 5, MotionEvent.ACTION_UP,
2083                 FAKE_COORD_X, FAKE_COORD_Y, 0);
2084         assertTrue(mGestureHandler.onTouchEvent(event));
2085         assertEquals("A GESTURE_SINGLE_TAP_CONFIRMED event should have been sent",
2086                 ContentViewGestureHandler.GESTURE_SINGLE_TAP_CONFIRMED,
2087                 mockDelegate.mMostRecentGestureEvent.mType);
2088         assertEquals("Only GESTURE_TAP_DOWN and GESTURE_SINGLE_TAP_CONFIRMED " +
2089                 "should have been sent",
2090                 2, mockDelegate.mGestureTypeList.size());
2091
2092         event = MotionEvent.obtain(
2093                 eventTime + 10, eventTime + 10, MotionEvent.ACTION_DOWN,
2094                 FAKE_COORD_X, FAKE_COORD_Y, 0);
2095         assertTrue(mGestureHandler.onTouchEvent(event));
2096         assertEquals("Only GESTURE_TAP_DOWN, " +
2097                 "GESTURE_SINGLE_TAP_CONFIRMED and " +
2098                 "GESTURE_TAP_DOWN should have been sent",
2099                 3, mockDelegate.mGestureTypeList.size());
2100
2101         event = MotionEvent.obtain(
2102                 eventTime + 10, eventTime + 15, MotionEvent.ACTION_UP,
2103                 FAKE_COORD_X, FAKE_COORD_Y, 0);
2104         assertTrue(mGestureHandler.onTouchEvent(event));
2105         assertEquals("A double tap should not have occurred",
2106                 ContentViewGestureHandler.GESTURE_SINGLE_TAP_CONFIRMED,
2107                 mockDelegate.mMostRecentGestureEvent.mType);
2108         assertEquals("Only GESTURE_TAP_DOWN, " +
2109                 "GESTURE_SINGLE_TAP_CONFIRMED, " +
2110                 "GESTURE_TAP_DOWN and " +
2111                 "GESTURE_SINGLE_TAP_CONFIRMED should have been sent",
2112                 4, mockDelegate.mGestureTypeList.size());
2113     }
2114
2115     /**
2116      * Verify that double tap drag zoom feature is not invoked when the gesture
2117      * handler is told to disable double tap gesture detection (according to the
2118      * logic in ContentViewCore.onRenderCoordinatesUpdated).
2119      * The second tap sequence should be treated just as the first would be.
2120      * @throws Exception
2121      */
2122     @SmallTest
2123     @Feature({"Gestures"})
2124     public void testNoDoubleTapDragZoomWhenDoubleTapDisabled() throws Exception {
2125         final long downTime1 = SystemClock.uptimeMillis();
2126         final long downTime2 = downTime1 + 100;
2127
2128         GestureRecordingMotionEventDelegate mockDelegate =
2129                 new GestureRecordingMotionEventDelegate();
2130         mGestureHandler = new ContentViewGestureHandler(
2131                 getInstrumentation().getTargetContext(), mockDelegate, mMockZoomManager);
2132         mGestureHandler.updateShouldDisableDoubleTap(true);
2133
2134         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime1, downTime1);
2135         assertTrue(mGestureHandler.onTouchEvent(event));
2136
2137         event = MotionEvent.obtain(
2138                 downTime1, downTime1 + 5, MotionEvent.ACTION_UP,
2139                 FAKE_COORD_X, FAKE_COORD_Y, 0);
2140         mGestureHandler.onTouchEvent(event);
2141
2142         event = MotionEvent.obtain(
2143                 downTime2, downTime2, MotionEvent.ACTION_DOWN,
2144                 FAKE_COORD_X, FAKE_COORD_Y, 0);
2145         assertTrue(mGestureHandler.onTouchEvent(event));
2146
2147         event = MotionEvent.obtain(
2148                 downTime2, downTime2 + 5, MotionEvent.ACTION_MOVE,
2149                 FAKE_COORD_X, FAKE_COORD_Y + 100, 0);
2150
2151         // The move should become a scroll, as double tap and drag to zoom is
2152         // disabled.
2153         assertTrue(mGestureHandler.onTouchEvent(event));
2154         assertTrue("GESTURE_SCROLL_START should have been sent",
2155                 mockDelegate.mGestureTypeList.contains(
2156                         ContentViewGestureHandler.GESTURE_SCROLL_START));
2157         assertFalse("No GESTURE_PINCH_BEGIN should have been sent",
2158                 mockDelegate.mGestureTypeList.contains(
2159                         ContentViewGestureHandler.GESTURE_PINCH_BEGIN));
2160
2161         event = MotionEvent.obtain(
2162                 downTime2, downTime2 + 10, MotionEvent.ACTION_MOVE,
2163                 FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
2164         assertTrue(mGestureHandler.onTouchEvent(event));
2165         assertEquals("GESTURE_SCROLL_BY should have been sent",
2166                 ContentViewGestureHandler.GESTURE_SCROLL_BY,
2167                 mockDelegate.mMostRecentGestureEvent.mType);
2168         assertEquals("GESTURE_SCROLL_BY should have been sent",
2169                 event.getEventTime(),
2170                 mockDelegate.mMostRecentGestureEvent.getTimeMs());
2171         assertTrue("No GESTURE_PINCH_BY should have been sent",
2172                 ContentViewGestureHandler.GESTURE_PINCH_BY !=
2173                 mockDelegate.mMostRecentGestureEvent.mType);
2174
2175         event = MotionEvent.obtain(
2176                 downTime2, downTime2 + 15, MotionEvent.ACTION_UP,
2177                 FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
2178         assertTrue(mGestureHandler.onTouchEvent(event));
2179         assertFalse("No GESTURE_PINCH_END should have been sent",
2180                 mockDelegate.mGestureTypeList.contains(
2181                         ContentViewGestureHandler.GESTURE_PINCH_END));
2182     }
2183
2184     /**
2185      * Verify that setting a fixed page scale (or a mobile viewport) during a double
2186      * tap drag zoom disables double tap detection after the gesture has ended.
2187      * @throws Exception
2188      */
2189     @SmallTest
2190     @Feature({"Gestures"})
2191     public void testFixedPageScaleDuringDoubleTapDragZoom() throws Exception {
2192         long downTime1 = SystemClock.uptimeMillis();
2193         long downTime2 = downTime1 + 100;
2194
2195         GestureRecordingMotionEventDelegate mockDelegate =
2196                 new GestureRecordingMotionEventDelegate();
2197         mGestureHandler = new ContentViewGestureHandler(
2198                 getInstrumentation().getTargetContext(), mockDelegate, mMockZoomManager);
2199         mLongPressDetector = new LongPressDetector(
2200                 getInstrumentation().getTargetContext(), mGestureHandler);
2201
2202         // Start a double-tap drag gesture.
2203         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime1, downTime1);
2204         assertTrue(mGestureHandler.onTouchEvent(event));
2205         mGestureHandler.sendShowPressedStateGestureForTesting();
2206         event = MotionEvent.obtain(
2207                 downTime1, downTime1 + 5, MotionEvent.ACTION_UP,
2208                 FAKE_COORD_X, FAKE_COORD_Y, 0);
2209         mGestureHandler.onTouchEvent(event);
2210         event = MotionEvent.obtain(
2211                 downTime2, downTime2, MotionEvent.ACTION_DOWN,
2212                 FAKE_COORD_X, FAKE_COORD_Y, 0);
2213         assertTrue(mGestureHandler.onTouchEvent(event));
2214         event = MotionEvent.obtain(
2215                 downTime2, downTime2 + 5, MotionEvent.ACTION_MOVE,
2216                 FAKE_COORD_X, FAKE_COORD_Y + 100, 0);
2217         assertTrue(mGestureHandler.onTouchEvent(event));
2218         assertTrue("GESTURE_SCROLL_START should have been sent",
2219                 mockDelegate.mGestureTypeList.contains(
2220                         ContentViewGestureHandler.GESTURE_SCROLL_START));
2221         assertEquals("GESTURE_PINCH_BEGIN should have been sent",
2222                 ContentViewGestureHandler.GESTURE_PINCH_BEGIN,
2223                 mockDelegate.mMostRecentGestureEvent.mType);
2224
2225         // Simulate setting a fixed page scale (or a mobile viewport);
2226         // this should not disrupt the current double-tap gesture.
2227         mGestureHandler.updateShouldDisableDoubleTap(true);
2228
2229         // Double tap zoom updates should continue.
2230         event = MotionEvent.obtain(
2231                 downTime2, downTime2 + 10, MotionEvent.ACTION_MOVE,
2232                 FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
2233         assertTrue(mGestureHandler.onTouchEvent(event));
2234         assertTrue("GESTURE_SCROLL_BY should have been sent",
2235                 mockDelegate.mGestureTypeList.contains(
2236                         ContentViewGestureHandler.GESTURE_SCROLL_BY));
2237         assertEquals("GESTURE_PINCH_BY should have been sent",
2238                 ContentViewGestureHandler.GESTURE_PINCH_BY,
2239                 mockDelegate.mMostRecentGestureEvent.mType);
2240         event = MotionEvent.obtain(
2241                 downTime2, downTime2 + 15, MotionEvent.ACTION_UP,
2242                 FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
2243         assertTrue(mGestureHandler.onTouchEvent(event));
2244         assertTrue("GESTURE_PINCH_END should have been sent",
2245                 mockDelegate.mGestureTypeList.contains(
2246                         ContentViewGestureHandler.GESTURE_PINCH_END));
2247         assertEquals("GESTURE_SCROLL_END should have been sent",
2248                 ContentViewGestureHandler.GESTURE_SCROLL_END,
2249                 mockDelegate.mMostRecentGestureEvent.mType);
2250
2251         // The double-tap gesture has finished, but the page scale is fixed.
2252         // The same event sequence should not generate any double tap getsures.
2253         mockDelegate.mGestureTypeList.clear();
2254         downTime1 += 200;
2255         downTime2 += 200;
2256
2257         // Start a double-tap drag gesture.
2258         event = motionEvent(MotionEvent.ACTION_DOWN, downTime1, downTime1);
2259         assertTrue(mGestureHandler.onTouchEvent(event));
2260         event = MotionEvent.obtain(
2261                 downTime1, downTime1 + 5, MotionEvent.ACTION_UP,
2262                 FAKE_COORD_X, FAKE_COORD_Y, 0);
2263         mGestureHandler.onTouchEvent(event);
2264         event = MotionEvent.obtain(
2265                 downTime2, downTime2, MotionEvent.ACTION_DOWN,
2266                 FAKE_COORD_X, FAKE_COORD_Y, 0);
2267         assertTrue(mGestureHandler.onTouchEvent(event));
2268         event = MotionEvent.obtain(
2269                 downTime2, downTime2 + 5, MotionEvent.ACTION_MOVE,
2270                 FAKE_COORD_X, FAKE_COORD_Y + 100, 0);
2271         assertTrue(mGestureHandler.onTouchEvent(event));
2272         assertTrue("GESTURE_SCROLL_START should have been sent",
2273                 mockDelegate.mGestureTypeList.contains(
2274                         ContentViewGestureHandler.GESTURE_SCROLL_START));
2275         assertFalse("GESTURE_PINCH_BEGIN should not have been sent",
2276                 mockDelegate.mGestureTypeList.contains(
2277                         ContentViewGestureHandler.GESTURE_PINCH_BEGIN));
2278
2279         // Double tap zoom updates should not be sent.
2280         // Instead, the second tap drag becomes a scroll gesture sequence.
2281         event = MotionEvent.obtain(
2282                 downTime2, downTime2 + 10, MotionEvent.ACTION_MOVE,
2283                 FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
2284         assertTrue(mGestureHandler.onTouchEvent(event));
2285         assertTrue("GESTURE_SCROLL_BY should have been sent",
2286                 mockDelegate.mGestureTypeList.contains(
2287                         ContentViewGestureHandler.GESTURE_SCROLL_BY));
2288         assertFalse("GESTURE_PINCH_BY should not have been sent",
2289                 mockDelegate.mGestureTypeList.contains(
2290                         ContentViewGestureHandler.GESTURE_PINCH_BY));
2291         event = MotionEvent.obtain(
2292                 downTime2, downTime2 + 15, MotionEvent.ACTION_UP,
2293                 FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
2294         assertTrue(mGestureHandler.onTouchEvent(event));
2295         assertFalse("GESTURE_PINCH_END should not have been sent",
2296                 mockDelegate.mGestureTypeList.contains(
2297                         ContentViewGestureHandler.GESTURE_PINCH_END));
2298     }
2299
2300     /**
2301      * Verify that a secondary pointer press with no consumer does not interfere
2302      * with Javascript touch handling.
2303      *
2304      * @throws Exception
2305      */
2306     @SmallTest
2307     @Feature({"Gestures"})
2308     public void testSecondaryPointerWithNoConsumer() throws Exception {
2309         final long downTime = SystemClock.uptimeMillis();
2310         final long eventTime = SystemClock.uptimeMillis();
2311
2312         mGestureHandler.hasTouchEventHandlers(true);
2313
2314         // Queue a primary pointer press, a secondary pointer press and release,
2315         // and a primary pointer move.
2316         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
2317         assertTrue(mGestureHandler.onTouchEvent(event));
2318         assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
2319
2320         event = motionEvent(MotionEvent.ACTION_POINTER_DOWN, downTime, eventTime);
2321         assertTrue(mGestureHandler.onTouchEvent(event));
2322         assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
2323
2324         event = motionEvent(MotionEvent.ACTION_POINTER_UP, downTime, eventTime + 10);
2325         assertTrue(mGestureHandler.onTouchEvent(event));
2326         assertEquals(3, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
2327
2328         event = MotionEvent.obtain(
2329                 downTime, eventTime + 15, MotionEvent.ACTION_MOVE,
2330                 FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
2331         assertTrue(mGestureHandler.onTouchEvent(event));
2332         assertEquals(4, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
2333
2334         // Simulate preventDefault from Javascript, forcing all touch events to Javascript
2335         // for the current sequence.
2336         mGestureHandler.confirmTouchEvent(
2337                 ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_CONSUMED);
2338         assertEquals(3, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
2339
2340         mGestureHandler.confirmTouchEvent(
2341                 ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
2342         assertEquals("Even if the secondary pointer has no consumer, continue sending events",
2343                 2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
2344
2345         mGestureHandler.confirmTouchEvent(
2346                 ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
2347         assertEquals("Even if the secondary pointer has no consumer, continue sending events",
2348                 1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
2349
2350         mGestureHandler.confirmTouchEvent(
2351                 ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
2352         assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
2353
2354         assertEquals("No gestures should result from the Javascript-consumed sequence",
2355                 0, mMockMotionEventDelegate.mTotalSentGestureCount);
2356     }
2357
2358     /**
2359      * Verify that multiple touch sequences in the queue are handled properly when
2360      * the Javascript response is different for each.
2361      *
2362      * @throws Exception
2363      */
2364     @SmallTest
2365     @Feature({"Gestures"})
2366     public void testMultiplyEnqueuedTouches() throws Exception {
2367         final long downTime = SystemClock.uptimeMillis();
2368         final long eventTime = SystemClock.uptimeMillis();
2369
2370         mGestureHandler.hasTouchEventHandlers(true);
2371         mGestureHandler.updateDoubleTapSupport(false);
2372
2373         // Queue a tap sequence.
2374         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
2375         assertTrue(mGestureHandler.onTouchEvent(event));
2376         assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
2377
2378         event = motionEvent(MotionEvent.ACTION_UP, downTime, eventTime + 5);
2379         assertTrue(mGestureHandler.onTouchEvent(event));
2380         assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
2381
2382         // Queue a scroll sequence.
2383         event = motionEvent(MotionEvent.ACTION_DOWN, downTime + 10, downTime + 10);
2384         assertTrue(mGestureHandler.onTouchEvent(event));
2385         assertEquals(3, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
2386
2387         event = MotionEvent.obtain(
2388                 downTime + 10, eventTime + 15, MotionEvent.ACTION_MOVE,
2389                 FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
2390         assertTrue(mGestureHandler.onTouchEvent(event));
2391         assertFalse(mGestureHandler.isNativeScrolling());
2392         assertEquals(4, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
2393
2394         event = motionEvent(MotionEvent.ACTION_UP, downTime + 10, downTime + 20);
2395         assertTrue(mGestureHandler.onTouchEvent(event));
2396         assertEquals(5, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
2397
2398         // Consume the first gesture.
2399         mGestureHandler.confirmTouchEvent(
2400                 ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_CONSUMED);
2401         assertEquals(4, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
2402
2403         mGestureHandler.confirmTouchEvent(
2404                 ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_CONSUMED);
2405         assertEquals(3, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
2406
2407         // Don't consume the second gesture; it should be fed to the gesture detector.
2408         mGestureHandler.confirmTouchEvent(
2409                 ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
2410         assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
2411         assertEquals("The down touch event should have been sent to the gesture detector",
2412                 MotionEvent.ACTION_DOWN, mMockGestureDetector.mLastEvent.getActionMasked());
2413         assertFalse(mGestureHandler.isNativeScrolling());
2414
2415         mGestureHandler.confirmTouchEvent(
2416                 ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
2417         assertEquals("The move touch event should have been sent to the gesture detector",
2418                 MotionEvent.ACTION_MOVE, mMockGestureDetector.mLastEvent.getActionMasked());
2419     }
2420
2421     /**
2422      * Verify that only complete gestures are forwarded to Javascript if we receive
2423      * a touch handler notification.
2424      * @throws Exception
2425      */
2426     @SmallTest
2427     @Feature({"Gestures"})
2428     public void testOnlyCompleteGesturesForwardedToTouchHandler() throws Exception {
2429         final long downTime = SystemClock.uptimeMillis();
2430         final long eventTime = SystemClock.uptimeMillis();
2431
2432         mGestureHandler.hasTouchEventHandlers(false);
2433
2434         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
2435         mGestureHandler.onTouchEvent(event);
2436         assertEquals("Initial down events should not be sent to Javascript without a touch handler",
2437                 0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
2438         assertTrue("Should have a pending gesture", mMockGestureDetector.mLastEvent != null);
2439         mMockGestureDetector.mLastEvent = null;
2440
2441         mGestureHandler.hasTouchEventHandlers(true);
2442
2443         event = MotionEvent.obtain(
2444                 downTime, eventTime + 5, MotionEvent.ACTION_MOVE,
2445                 FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
2446         mGestureHandler.onTouchEvent(event);
2447         assertEquals("A move event should only be offered to javascript if the down was offered",
2448                 0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
2449         assertTrue("Should have a pending gesture", mMockGestureDetector.mLastEvent != null);
2450
2451         event = motionEvent(MotionEvent.ACTION_POINTER_DOWN, downTime, eventTime + 10);
2452         mGestureHandler.onTouchEvent(event);
2453         assertEquals("A pointer event should only be offered to Javascript if the down was offered",
2454                 0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
2455
2456         // Ensure that redundant notifications have no effect.
2457         mGestureHandler.hasTouchEventHandlers(true);
2458
2459         event = motionEvent(MotionEvent.ACTION_POINTER_UP, downTime, eventTime + 15);
2460         mGestureHandler.onTouchEvent(event);
2461         assertEquals("A pointer event should only be offered to Javascript if the down was offered",
2462                 0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
2463
2464         event = motionEvent(MotionEvent.ACTION_UP, downTime, eventTime + 20);
2465         mGestureHandler.onTouchEvent(event);
2466         assertEquals("A pointer event should only be offered to Javascript if the down was offered",
2467                 0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
2468
2469         event = motionEvent(MotionEvent.ACTION_DOWN, downTime + 25, downTime + 25);
2470         mGestureHandler.onTouchEvent(event);
2471         assertEquals("A down event should be offered to Javascript with a registered touch handler",
2472                 1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
2473     }
2474
2475     /**
2476      * Verify that no timeout-based gestures are triggered after a touch event
2477      * is consumed.  In particular, LONG_PRESS and SHOW_PRESS should not fire
2478      * if TouchStart went unconsumed, but subsequent TouchMoves are consumed.
2479      *
2480      * @throws Exception
2481      */
2482     @SmallTest
2483     @Feature({"Gestures"})
2484     public void testNoTimeoutGestureAfterTouchConsumed() throws Exception {
2485         getInstrumentation().runOnMainSync(new Runnable() {
2486             @Override
2487             public void run() {
2488                 setUp();
2489
2490                 final long downTime = SystemClock.uptimeMillis();
2491                 final long eventTime = SystemClock.uptimeMillis();
2492
2493                 mGestureHandler.hasTouchEventHandlers(true);
2494
2495                 MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, eventTime);
2496                 assertTrue(mGestureHandler.onTouchEvent(event));
2497                 mGestureHandler.confirmTouchEvent(
2498                         ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
2499                 assertTrue("Should have a pending LONG_PRESS",
2500                         mLongPressDetector.hasPendingMessage());
2501
2502                 event = MotionEvent.obtain(
2503                         downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
2504                         FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
2505                 assertTrue(mGestureHandler.onTouchEvent(event));
2506                 mGestureHandler.confirmTouchEvent(
2507                         ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_CONSUMED);
2508                 assertFalse("Should not have a pending LONG_PRESS",
2509                         mLongPressDetector.hasPendingMessage());
2510             }
2511         });
2512         assertFalse(mMockListener.mShowPressCalled.await(
2513                 ScalableTimeout.ScaleTimeout(ViewConfiguration.getTapTimeout() + 10),
2514                 TimeUnit.MILLISECONDS));
2515         assertFalse(mMockListener.mLongPressCalled.await(
2516                 ScalableTimeout.ScaleTimeout(ViewConfiguration.getLongPressTimeout() + 10),
2517                 TimeUnit.MILLISECONDS));
2518     }
2519
2520     /**
2521      * Verify that a TAP_DOWN will be followed by a TAP_CANCEL if the first
2522      * touch is unconsumed, but the subsequent touch is consumed.
2523      *
2524      * @throws Exception
2525      */
2526     @SmallTest
2527     @Feature({"Gestures"})
2528     public void testTapCancelledAfterTouchConsumed() throws Exception {
2529         final long downTime = SystemClock.uptimeMillis();
2530         final long eventTime = SystemClock.uptimeMillis();
2531
2532         GestureRecordingMotionEventDelegate mockDelegate =
2533                 new GestureRecordingMotionEventDelegate();
2534         mGestureHandler = new ContentViewGestureHandler(
2535                 getInstrumentation().getTargetContext(), mockDelegate, mMockZoomManager);
2536         mGestureHandler.hasTouchEventHandlers(true);
2537
2538         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
2539         assertTrue(mGestureHandler.onTouchEvent(event));
2540         mGestureHandler.confirmTouchEvent(
2541                 ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
2542         assertEquals("A TAP_DOWN gesture should have been sent",
2543                 ContentViewGestureHandler.GESTURE_TAP_DOWN,
2544                         mockDelegate.mMostRecentGestureEvent.mType);
2545
2546         event = MotionEvent.obtain(
2547                 downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
2548                 FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
2549         assertTrue(mGestureHandler.onTouchEvent(event));
2550         mGestureHandler.confirmTouchEvent(
2551                 ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_CONSUMED);
2552         assertEquals("A TAP_CANCEL gesture should have been sent",
2553                 ContentViewGestureHandler.GESTURE_TAP_CANCEL,
2554                         mockDelegate.mMostRecentGestureEvent.mType);
2555
2556         event = MotionEvent.obtain(
2557                 downTime, eventTime + 15, MotionEvent.ACTION_MOVE,
2558                 FAKE_COORD_X, FAKE_COORD_Y + 400, 0);
2559         assertTrue(mGestureHandler.onTouchEvent(event));
2560         mGestureHandler.confirmTouchEvent(
2561                 ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
2562         assertEquals("No further gestures should be sent",
2563                 ContentViewGestureHandler.GESTURE_TAP_CANCEL,
2564                         mockDelegate.mMostRecentGestureEvent.mType);
2565     }
2566 }