2c465736fe3c8a63544e3191336943e44c19f5b3
[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.InputDevice;
14 import android.view.MotionEvent;
15 import android.view.MotionEvent.PointerCoords;
16 import android.view.MotionEvent.PointerProperties;
17 import android.view.ViewConfiguration;
18
19 import org.chromium.base.test.util.Feature;
20 import org.chromium.base.test.util.ScalableTimeout;
21 import org.chromium.content.browser.ContentViewGestureHandler.MotionEventDelegate;
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 MockMotionEventDelegate mMockMotionEventDelegate;
36     private ContentViewGestureHandler mGestureHandler;
37
38     static class MockMotionEventDelegate implements MotionEventDelegate {
39         static class GestureEvent {
40             public final int mType;
41             public final long mTimeMs;
42             public final int mX;
43             public final int mY;
44             public final Bundle mExtraParams;
45
46             public GestureEvent(int type, long timeMs, int x, int y, Bundle extraParams) {
47                 mType = type;
48                 mTimeMs = timeMs;
49                 mX = x;
50                 mY = y;
51                 mExtraParams = extraParams;
52             }
53
54             public int getType() {
55                 return mType;
56             }
57
58             public long getTimeMs() {
59                 return mTimeMs;
60             }
61
62             public int getX() {
63                 return mX;
64             }
65
66             public int getY() {
67                 return mY;
68             }
69
70             public Bundle getExtraParams() {
71                 return mExtraParams;
72             }
73         }
74         GestureEvent mMostRecentGestureEvent;
75         GestureEvent mActiveScrollStartEvent;
76         int mMostRecentMotionEventAction = -1;
77         final ArrayList<Integer> mGestureTypeList = new ArrayList<Integer>();
78         final ArrayList<Long> mGestureTimeList = new ArrayList<Long>();
79
80         final CountDownLatch mLongPressCalled = new CountDownLatch(1);
81         final CountDownLatch mShowPressCalled = new CountDownLatch(1);
82
83         @Override
84         public void onTouchEventHandlingBegin(MotionEvent event) {
85             mMostRecentMotionEventAction = event.getActionMasked();
86         }
87
88         @Override
89         public void onTouchEventHandlingEnd() {
90         }
91
92         @Override
93         public boolean onGestureEventCreated(
94                 int type, long timeMs, int x, int y, Bundle extraParams) {
95             Log.i(TAG, "Gesture event received with type id " + type);
96             mMostRecentGestureEvent = new GestureEvent(type, timeMs, x, y, extraParams);
97             mGestureTypeList.add(mMostRecentGestureEvent.mType);
98             mGestureTimeList.add(timeMs);
99             if (type == GestureEventType.SCROLL_START) {
100                 mActiveScrollStartEvent = mMostRecentGestureEvent;
101             } else if (type == GestureEventType.SCROLL_END
102                            || type == GestureEventType.FLING_CANCEL) {
103                 mActiveScrollStartEvent = null;
104             } else if (type == GestureEventType.LONG_PRESS) {
105                 mLongPressCalled.countDown();
106             } else if (type == GestureEventType.SHOW_PRESS) {
107                 mShowPressCalled.countDown();
108             }
109
110             return true;
111         }
112     }
113
114     private static MotionEvent motionEvent(int action, long downTime, long eventTime) {
115         return MotionEvent.obtain(downTime, eventTime, action, FAKE_COORD_X, FAKE_COORD_Y, 0);
116     }
117
118     @Override
119     public void setUp() {
120         mMockMotionEventDelegate = new MockMotionEventDelegate();
121         mGestureHandler = new ContentViewGestureHandler(
122                 getInstrumentation().getTargetContext(), mMockMotionEventDelegate);
123         mGestureHandler.updateMultiTouchSupport(false);
124     }
125
126     /**
127      * Verify that a DOWN followed shortly by an UP will trigger a single tap.
128      *
129      * @throws Exception
130      */
131     @SmallTest
132     @Feature({"Gestures"})
133     public void testGestureSingleClick() throws Exception {
134         final long downTime = SystemClock.uptimeMillis();
135         final long eventTime = SystemClock.uptimeMillis();
136
137         mGestureHandler.updateDoubleTapSupport(false);
138
139         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
140         assertTrue(mGestureHandler.onTouchEvent(event));
141         assertEquals(GestureEventType.TAP_DOWN,
142                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
143
144         event = motionEvent(MotionEvent.ACTION_UP, downTime, eventTime + 10);
145         assertTrue(mGestureHandler.onTouchEvent(event));
146         assertEquals(GestureEventType.SINGLE_TAP_CONFIRMED,
147                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
148     }
149
150     /**
151      * Verify that a DOWN followed by a MOVE will trigger fling (but not LONG).
152      * @throws Exception
153      */
154     @SmallTest
155     @Feature({"Gestures"})
156     public void testGestureFlingAndCancelLongClick() throws Exception {
157         final long downTime = SystemClock.uptimeMillis();
158         final long eventTime = SystemClock.uptimeMillis();
159
160         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
161
162         assertTrue(mGestureHandler.onTouchEvent(event));
163         assertEquals(GestureEventType.TAP_DOWN,
164                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
165
166         event = MotionEvent.obtain(
167                 downTime, eventTime + 5, MotionEvent.ACTION_MOVE,
168                 FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
169         assertTrue(mGestureHandler.onTouchEvent(event));
170
171         event = MotionEvent.obtain(
172                 downTime, eventTime + 10, MotionEvent.ACTION_UP,
173                 FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
174         assertTrue(mGestureHandler.onTouchEvent(event));
175         assertEquals(GestureEventType.FLING_START,
176                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
177         assertFalse(mMockMotionEventDelegate.mGestureTypeList.contains(
178                 GestureEventType.LONG_PRESS));
179     }
180
181     /**
182      * Verify that for a normal scroll the following events are sent:
183      * - GESTURE_SCROLL_START
184      * - GESTURE_SCROLL_BY
185      * - GESTURE_SCROLL_END
186      * @throws Exception
187      */
188     @SmallTest
189     @Feature({"Gestures"})
190     public void testScrollEventActionUpSequence() throws Exception {
191         checkScrollEventSequenceForEndActionType(MotionEvent.ACTION_UP);
192     }
193
194     /**
195      * Verify that for a cancelled scroll the following events are sent:
196      * - GESTURE_SCROLL_START
197      * - GESTURE_SCROLL_BY
198      * - GESTURE_SCROLL_END
199      * @throws Exception
200      */
201     @SmallTest
202     @Feature({"Gestures"})
203     public void testScrollEventActionCancelSequence() throws Exception {
204         checkScrollEventSequenceForEndActionType(MotionEvent.ACTION_CANCEL);
205     }
206
207     private void checkScrollEventSequenceForEndActionType(int endActionType) throws Exception {
208         final long downTime = SystemClock.uptimeMillis();
209         final long eventTime = SystemClock.uptimeMillis();
210         final int scrollToX = FAKE_COORD_X + 100;
211         final int scrollToY = FAKE_COORD_Y + 100;
212
213         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
214
215         assertTrue(mGestureHandler.onTouchEvent(event));
216
217         event = MotionEvent.obtain(
218                 downTime, eventTime + 1000, MotionEvent.ACTION_MOVE, scrollToX, scrollToY, 0);
219         assertTrue(mGestureHandler.onTouchEvent(event));
220         assertTrue(mGestureHandler.isNativeScrolling());
221         assertTrue("A scrollStart event should have been sent",
222                 mMockMotionEventDelegate.mGestureTypeList.contains(
223                         GestureEventType.SCROLL_START));
224         assertEquals("We should have started scrolling",
225                 GestureEventType.SCROLL_BY,
226                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
227         assertEquals("Only tapDown, tapCancel, scrollBegin and scrollBy should have been sent",
228                 4, mMockMotionEventDelegate.mGestureTypeList.size());
229         assertEquals("scrollBegin should be sent before scrollBy",
230                 GestureEventType.SCROLL_START,
231                 (int) mMockMotionEventDelegate.mGestureTypeList.get(2));
232         assertEquals("scrollBegin should have the time of the ACTION_MOVE",
233                 eventTime + 1000, (long) mMockMotionEventDelegate.mGestureTimeList.get(2));
234
235         event = MotionEvent.obtain(
236                 downTime, eventTime + 1000, endActionType, scrollToX, scrollToY, 0);
237         mGestureHandler.onTouchEvent(event);
238         assertFalse(mGestureHandler.isNativeScrolling());
239         assertTrue("A scrollEnd event should have been sent",
240                 mMockMotionEventDelegate.mGestureTypeList.contains(
241                         GestureEventType.SCROLL_END));
242         assertEquals("We should have stopped scrolling",
243                 GestureEventType.SCROLL_END,
244                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
245         assertEquals("Only tapDown, scrollBegin and scrollBy and scrollEnd should have been sent",
246                 5, mMockMotionEventDelegate.mGestureTypeList.size());
247     }
248
249     /**
250      * Verify that for a normal fling (fling after scroll) the following events are sent:
251      * - GESTURE_SCROLL_BEGIN
252      * - GESTURE_FLING_START
253      * and GESTURE_FLING_CANCEL is sent on the next touch.
254      * @throws Exception
255      */
256     @SmallTest
257     @Feature({"Gestures"})
258     public void testFlingEventSequence() throws Exception {
259         final long downTime = SystemClock.uptimeMillis();
260         final long eventTime = SystemClock.uptimeMillis();
261
262         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
263
264         assertTrue(mGestureHandler.onTouchEvent(event));
265
266         event = MotionEvent.obtain(
267                 downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
268                 FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
269         assertTrue(mGestureHandler.onTouchEvent(event));
270         assertTrue(mGestureHandler.isNativeScrolling());
271         assertTrue("A scrollStart event should have been sent",
272                 mMockMotionEventDelegate.mGestureTypeList.contains(
273                         GestureEventType.SCROLL_START));
274         assertEquals("We should have started scrolling",
275                 GestureEventType.SCROLL_BY,
276                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
277         assertEquals("Only tapDown, tapCancel, scrollBegin and scrollBy should have been sent",
278                 4, mMockMotionEventDelegate.mGestureTypeList.size());
279         assertEquals("scrollBegin should be sent before scrollBy",
280                 GestureEventType.SCROLL_START,
281                 (int) mMockMotionEventDelegate.mGestureTypeList.get(2));
282         MockMotionEventDelegate.GestureEvent startEvent =
283                 mMockMotionEventDelegate.mActiveScrollStartEvent;
284         assertNotNull(startEvent);
285         assertEquals("scrollBegin should have the time of the ACTION_MOVE",
286                 eventTime + 10, (long) startEvent.getTimeMs());
287         int hintX = startEvent.getExtraParams().getInt(ContentViewGestureHandler.DELTA_HINT_X);
288         int hintY = startEvent.getExtraParams().getInt(ContentViewGestureHandler.DELTA_HINT_Y);
289         // We don't want to take a dependency here on exactly how hints are calculated for a
290         // fling (eg. may depend on velocity), so just validate the direction.
291         assertTrue("scrollBegin hint should be in positive X axis",
292                 hintX > 0 && hintY > 0 && hintX > hintY);
293
294         event = MotionEvent.obtain(
295                 downTime, eventTime + 15, MotionEvent.ACTION_UP,
296                 FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
297         assertTrue(mGestureHandler.onTouchEvent(event));
298         assertFalse(mGestureHandler.isNativeScrolling());
299         assertEquals("We should have started flinging",
300                 GestureEventType.FLING_START,
301                         mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
302         assertTrue("A scroll end event should not have been sent",
303                 !mMockMotionEventDelegate.mGestureTypeList.contains(
304                         GestureEventType.SCROLL_END));
305         assertEquals("The last up should have caused flingStart to be sent",
306                 5, mMockMotionEventDelegate.mGestureTypeList.size());
307         assertEquals("flingStart should have the time of the ACTION_UP",
308                 eventTime + 15, (long) mMockMotionEventDelegate.mGestureTimeList.get(4));
309     }
310
311     /**
312      * Verify that a zero-velocity fling is never forwarded, and cancels any
313      * previous fling or scroll sequence.
314      * @throws Exception
315      */
316     @SmallTest
317     @Feature({"Gestures"})
318     public void testZeroVelocityFling() throws Exception {
319         final long downTime = SystemClock.uptimeMillis();
320         final long eventTime = SystemClock.uptimeMillis();
321
322         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
323         assertTrue(mGestureHandler.onTouchEvent(event));
324
325         mGestureHandler.fling(eventTime, 5, 5, 0, 0);
326         assertEquals("A zero-velocity fling should not be forwarded",
327                 GestureEventType.TAP_DOWN,
328                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
329
330         mGestureHandler.fling(eventTime, 5, 5, 5, 0);
331         assertEquals("Subsequent flings should work properly",
332                 GestureEventType.FLING_START,
333                         mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
334
335         event = MotionEvent.obtain(
336                 downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
337                 FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
338         assertTrue(mGestureHandler.onTouchEvent(event));
339         assertTrue(mGestureHandler.isNativeScrolling());
340         assertTrue("A scrollStart event should have been sent",
341                 mMockMotionEventDelegate.mGestureTypeList.contains(
342                         GestureEventType.SCROLL_START));
343         assertEquals("We should have started scrolling",
344                 GestureEventType.SCROLL_BY,
345                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
346
347         mGestureHandler.fling(eventTime, 5, 5, 0, 0);
348         assertEquals("A zero-velicty fling should end the current scroll sequence",
349                 GestureEventType.SCROLL_END,
350                         mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
351     }
352
353     /**
354      * Verify that a show pressed state gesture followed by a long press followed by the focus
355      * loss in the window due to context menu cancels show pressed.
356      * @throws Exception
357      */
358     @SmallTest
359     @Feature({"Gestures"})
360     public void testShowPressCancelOnWindowFocusLost() throws Exception {
361         final long time = SystemClock.uptimeMillis();
362
363         getInstrumentation().runOnMainSync(new Runnable() {
364             @Override
365             public void run() {
366                 setUp();
367                 MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, time, time);
368                 mGestureHandler.onTouchEvent(event);
369             }
370         });
371
372         assertTrue(mMockMotionEventDelegate.mShowPressCalled.await(
373                 ScalableTimeout.scaleTimeout(ViewConfiguration.getTapTimeout() + 10),
374                 TimeUnit.MILLISECONDS));
375
376         assertTrue(mMockMotionEventDelegate.mLongPressCalled.await(
377                 ScalableTimeout.scaleTimeout(ViewConfiguration.getLongPressTimeout()
378                         + ViewConfiguration.getTapTimeout() + 10),
379                 TimeUnit.MILLISECONDS));
380
381         assertEquals("Should have sent SHOW_PRESS and LONG_PRESS events",
382                 3, mMockMotionEventDelegate.mGestureTypeList.size());
383         assertEquals("Should have a show press event",
384                 GestureEventType.SHOW_PRESS,
385                 mMockMotionEventDelegate.mGestureTypeList.get(1).intValue());
386         assertEquals("Should have a long press event",
387                 GestureEventType.LONG_PRESS,
388                 mMockMotionEventDelegate.mGestureTypeList.get(2).intValue());
389
390         // The long press triggers window focus loss by opening a context menu
391         mGestureHandler.onWindowFocusLost();
392
393         assertEquals("Only should have sent only GESTURE_TAP_CANCEL event",
394                 4, mMockMotionEventDelegate.mGestureTypeList.size());
395         assertEquals("Should have a gesture show press cancel event next",
396                 GestureEventType.TAP_CANCEL,
397                 mMockMotionEventDelegate.mGestureTypeList.get(3).intValue());
398     }
399
400     /**
401      * Verify that a recent show pressed state gesture is canceled when scrolling begins.
402      * @throws Exception
403      */
404     @SmallTest
405     @Feature({"Gestures"})
406     public void testShowPressCancelWhenScrollBegins() throws Exception {
407         final long downTime = SystemClock.uptimeMillis();
408         final long eventTime = SystemClock.uptimeMillis();
409
410         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
411
412         assertTrue(mGestureHandler.onTouchEvent(event));
413
414         mGestureHandler.sendShowPressedStateGestureForTesting();
415
416         assertEquals("A show pressed state event should have been sent",
417                 GestureEventType.SHOW_PRESS,
418                         mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
419         assertEquals("Only tapDown and showPressedState should have been sent",
420                 2, mMockMotionEventDelegate.mGestureTypeList.size());
421
422         event = MotionEvent.obtain(
423                 downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
424                 FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
425         assertTrue(mGestureHandler.onTouchEvent(event));
426
427         assertEquals("We should have started scrolling",
428                 GestureEventType.SCROLL_BY,
429                         mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
430         assertTrue("A show press cancel event should have been sent",
431                 mMockMotionEventDelegate.mGestureTypeList.contains(
432                         GestureEventType.TAP_CANCEL));
433         assertEquals("Only tapDown, showPressedState, showPressCancel, scrollBegin and scrollBy" +
434                 " should have been sent",
435                 5, mMockMotionEventDelegate.mGestureTypeList.size());
436
437         event = MotionEvent.obtain(
438                 downTime, eventTime + 15, MotionEvent.ACTION_UP,
439                 FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
440         assertTrue(mGestureHandler.onTouchEvent(event));
441         assertEquals("We should have started flinging",
442                 GestureEventType.FLING_START,
443                         mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
444         assertTrue("A scroll end event should not have been sent",
445                 !mMockMotionEventDelegate.mGestureTypeList.contains(
446                         GestureEventType.SCROLL_END));
447         assertEquals("The last up should have caused flingStart to be sent",
448                 6, mMockMotionEventDelegate.mGestureTypeList.size());
449     }
450
451     /**
452      * Verify that double tap is correctly handled including the recent show pressed state gesture
453      * cancellation.
454      * @throws Exception
455      */
456     @SmallTest
457     @Feature({"Gestures"})
458     public void testDoubleTap() throws Exception {
459         final long downTime = SystemClock.uptimeMillis();
460         final long eventTime = SystemClock.uptimeMillis();
461
462         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
463         assertTrue(mGestureHandler.onTouchEvent(event));
464
465         mGestureHandler.sendShowPressedStateGestureForTesting();
466         assertEquals("GESTURE_SHOW_PRESS should have been sent",
467                 GestureEventType.SHOW_PRESS,
468                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
469         assertEquals("Only GESTURE_TAP_DOWN and GESTURE_SHOW_PRESS should have been sent",
470                 2, mMockMotionEventDelegate.mGestureTypeList.size());
471
472         event = MotionEvent.obtain(
473                 downTime, eventTime + 5, MotionEvent.ACTION_UP,
474                 FAKE_COORD_X, FAKE_COORD_Y, 0);
475         mGestureHandler.onTouchEvent(event);
476         assertEquals("A GESTURE_SINGLE_TAP_UNCONFIRMED event should have been sent",
477                 GestureEventType.SINGLE_TAP_UNCONFIRMED,
478                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
479         assertEquals("Only GESTURE_TAP_DOWN, " +
480                 "GESTURE_SHOW_PRESS and " +
481                 "GESTURE_SINGLE_TAP_UNCONFIRMED should have been sent",
482                 3, mMockMotionEventDelegate.mGestureTypeList.size());
483
484         event = MotionEvent.obtain(
485                 eventTime + 10, eventTime + 10, MotionEvent.ACTION_DOWN,
486                 FAKE_COORD_X, FAKE_COORD_Y, 0);
487         assertTrue(mGestureHandler.onTouchEvent(event));
488         assertEquals("A GESTURE_TAP_DOWN event should have been sent ",
489                 GestureEventType.TAP_DOWN,
490                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
491         assertEquals("Only GESTURE_TAP_DOWN, " +
492                 "GESTURE_SHOW_PRESS, " +
493                 "GESTURE_SINGLE_TAP_UNCONFIRMED," +
494                 "GESTURE_TAP_CANCEL and" +
495                 "GESTURE_TAP_DOWN should have been sent",
496                 5, mMockMotionEventDelegate.mGestureTypeList.size());
497
498         // Moving a very small amount of distance should not trigger the double tap drag zoom mode.
499         event = MotionEvent.obtain(
500                 eventTime + 10, eventTime + 10, MotionEvent.ACTION_MOVE,
501                 FAKE_COORD_X, FAKE_COORD_Y + 1, 0);
502         assertTrue(mGestureHandler.onTouchEvent(event));
503         assertEquals("Only GESTURE_TAP_DOWN, " +
504                 "GESTURE_SHOW_PRESS, " +
505                 "GESTURE_SINGLE_TAP_UNCONFIRMED and" +
506                 "GESTURE_TAP_CANCEL and " +
507                 "GESTURE_TAP_DOWN should have been sent",
508                 5, mMockMotionEventDelegate.mGestureTypeList.size());
509
510         event = MotionEvent.obtain(
511                 eventTime + 10, eventTime + 15, MotionEvent.ACTION_UP,
512                 FAKE_COORD_X, FAKE_COORD_Y + 1, 0);
513         assertTrue(mGestureHandler.onTouchEvent(event));
514         assertEquals("A double tap should have occurred",
515                 GestureEventType.DOUBLE_TAP,
516                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
517         assertEquals("Only GESTURE_TAP_DOWN, " +
518                 "GESTURE_SHOW_PRESS, " +
519                 "GESTURE_SINGLE_TAP_UNCONFIRMED, " +
520                 "GESTURE_TAP_CANCEL, " +
521                 "GESTURE_TAP_DOWN, " +
522                 "GESTURE_DOUBLE_TAP should have been sent",
523                 6, mMockMotionEventDelegate.mGestureTypeList.size());
524     }
525
526     /**
527      * Verify that double tap drag zoom feature is correctly executed.
528      * @throws Exception
529      */
530     @SmallTest
531     @Feature({"Gestures"})
532     public void testDoubleTapDragZoom() throws Exception {
533         final long downTime1 = SystemClock.uptimeMillis();
534         final long downTime2 = downTime1 + 100;
535
536         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime1, downTime1);
537         assertTrue(mGestureHandler.onTouchEvent(event));
538
539         mGestureHandler.sendShowPressedStateGestureForTesting();
540         assertEquals("GESTURE_SHOW_PRESS should have been sent",
541                 GestureEventType.SHOW_PRESS,
542                         mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
543         assertEquals("Only GESTURE_TAP_DOWN and GESTURE_SHOW_PRESS should have been sent",
544                 2, mMockMotionEventDelegate.mGestureTypeList.size());
545
546
547         event = MotionEvent.obtain(
548                 downTime1, downTime1 + 5, MotionEvent.ACTION_UP,
549                 FAKE_COORD_X, FAKE_COORD_Y, 0);
550         mGestureHandler.onTouchEvent(event);
551         assertEquals("A GESTURE_SINGLE_TAP_UNCONFIRMED event should have been sent",
552                 GestureEventType.SINGLE_TAP_UNCONFIRMED,
553                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
554         assertEquals("Only GESTURE_TAP_DOWN, " +
555                 "GESTURE_SHOW_PRESS and " +
556                 "GESTURE_TAP_UNCONFIRMED " +
557                 "should have been sent",
558                 3, mMockMotionEventDelegate.mGestureTypeList.size());
559
560         event = MotionEvent.obtain(
561                 downTime2, downTime2, MotionEvent.ACTION_DOWN,
562                 FAKE_COORD_X, FAKE_COORD_Y, 0);
563         assertTrue(mGestureHandler.onTouchEvent(event));
564         assertEquals("GESTURE_TAP_DOWN should have been sent",
565                 GestureEventType.TAP_DOWN,
566                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
567         assertEquals("Only GESTURE_TAP_DOWN, " +
568                 "GESTURE_SHOW_PRESS, " +
569                 "GESTURE_TAP_UNCONFIRMED," +
570                 "GESTURE_TAP_CANCEL and" +
571                 "GESTURE_TAP_DOWN should have been sent",
572                 5, mMockMotionEventDelegate.mGestureTypeList.size());
573
574         event = MotionEvent.obtain(
575                 downTime2, downTime2 + 5, MotionEvent.ACTION_MOVE,
576                 FAKE_COORD_X, FAKE_COORD_Y + 100, 0);
577         assertTrue(mGestureHandler.onTouchEvent(event));
578         assertTrue("GESTURE_SCROLL_START should have been sent",
579                 mMockMotionEventDelegate.mGestureTypeList.contains(
580                         GestureEventType.SCROLL_START));
581         MockMotionEventDelegate.GestureEvent startEvent =
582                 mMockMotionEventDelegate.mActiveScrollStartEvent;
583         assertEquals(FAKE_COORD_X, startEvent.getX());
584         assertEquals(FAKE_COORD_Y + 100, startEvent.getY());
585         Bundle extraParams = startEvent.getExtraParams();
586         assertNotNull(extraParams);
587         assertEquals("GESTURE_SCROLL_START should have an X hint equal to the distance traveled",
588                 0, extraParams.getInt(ContentViewGestureHandler.DELTA_HINT_X));
589         assertEquals("GESTURE_SCROLL_START should have an X hint equal to the distance traveled",
590                 100, extraParams.getInt(ContentViewGestureHandler.DELTA_HINT_Y));
591
592         assertEquals("GESTURE_PINCH_BEGIN should have been sent",
593                 GestureEventType.PINCH_BEGIN,
594                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
595         assertEquals("Only GESTURE_TAP_DOWN, " +
596                 "GESTURE_SHOW_PRESS, " +
597                 "GESTURE_TAP_UNCONFIRMED," +
598                 "GESTURE_TAP_CANCEL, " +
599                 "GESTURE_TAP_DOWN, " +
600                 "GESTURE_TAP_CANCEL, " +
601                 "GESTURE_SCROLL_START, and " +
602                 "GESTURE_PINCH_BEGIN should have been sent",
603                 8, mMockMotionEventDelegate.mGestureTypeList.size());
604
605         event = MotionEvent.obtain(
606                 downTime2, downTime2 + 10, MotionEvent.ACTION_MOVE,
607                 FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
608         assertTrue(mGestureHandler.onTouchEvent(event));
609         assertTrue("GESTURE_SCROLL_BY should have been sent",
610                 mMockMotionEventDelegate.mGestureTypeList.contains(
611                         GestureEventType.SCROLL_BY));
612         assertEquals("GESTURE_PINCH_BY should have been sent",
613                 GestureEventType.PINCH_BY,
614                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
615         assertEquals("Only GESTURE_TAP_DOWN, " +
616                 "GESTURE_SHOW_PRESS, " +
617                 "GESTURE_TAP_UNCONFIRMED," +
618                 "GESTURE_TAP_CANCEL, " +
619                 "GESTURE_TAP_DOWN, " +
620                 "GESTURE_TAP_CANCEL, " +
621                 "GESTURE_SCROLL_START," +
622                 "GESTURE_PINCH_BEGIN, " +
623                 "GESTURE_SCROLL_BY, and " +
624                 "GESTURE_PINCH_BY should have been sent",
625                 10, mMockMotionEventDelegate.mGestureTypeList.size());
626
627         event = MotionEvent.obtain(
628                 downTime2, downTime2 + 15, MotionEvent.ACTION_UP,
629                 FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
630         assertTrue(mGestureHandler.onTouchEvent(event));
631         assertTrue("GESTURE_PINCH_END should have been sent",
632                 mMockMotionEventDelegate.mGestureTypeList.contains(
633                         GestureEventType.PINCH_END));
634         assertEquals("GESTURE_SCROLL_END should have been sent",
635                 GestureEventType.SCROLL_END,
636                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
637         assertEquals("Only GESTURE_TAP_DOWN, " +
638                 "GESTURE_SHOW_PRESS, " +
639                 "GESTURE_TAP_UNCONFIRMED," +
640                 "GESTURE_TAP_CANCEL, " +
641                 "GESTURE_TAP_DOWN, " +
642                 "GESTURE_TAP_CANCEL, " +
643                 "GESTURE_SCROLL_START," +
644                 "GESTURE_PINCH_BEGIN, " +
645                 "GESTURE_SCROLL_BY," +
646                 "GESTURE_PINCH_BY, " +
647                 "GESTURE_PINCH_END, and " +
648                 "GESTURE_SCROLL_END should have been sent",
649                 12, mMockMotionEventDelegate.mGestureTypeList.size());
650     }
651
652
653     /**
654      * Verify that double tap drag zoom is cancelled if the user presses a
655      * secondary pointer.
656      * @throws Exception
657      */
658     @SmallTest
659     @Feature({"Gestures"})
660     public void testDoubleTapDragZoomCancelledOnSecondaryPointerDown() throws Exception {
661         final long downTime1 = SystemClock.uptimeMillis();
662         final long downTime2 = downTime1 + 100;
663
664         PointerProperties pp0 = new PointerProperties();
665         pp0.id = 0;
666         pp0.toolType = MotionEvent.TOOL_TYPE_FINGER;
667         PointerProperties pp1 = new PointerProperties();
668         pp1.id = 1;
669         pp1.toolType = MotionEvent.TOOL_TYPE_FINGER;
670
671         PointerCoords pc0 = new PointerCoords();
672         pc0.x = FAKE_COORD_X;
673         pc0.y = FAKE_COORD_Y;
674         pc0.pressure = 1;
675         pc0.size = 1;
676         PointerCoords pc1 = new PointerCoords();
677         pc1.x = FAKE_COORD_X + 50;
678         pc1.y = FAKE_COORD_Y + 50;
679         pc1.pressure = 1;
680         pc1.size = 1;
681
682         MotionEvent event = MotionEvent.obtain(
683                 downTime1, downTime1, MotionEvent.ACTION_DOWN,
684                 1, new PointerProperties[] { pp0 }, new PointerCoords[] { pc0 },
685                 0, 0, 1.0f, 1.0f, 0, 0, InputDevice.SOURCE_CLASS_POINTER, 0);
686         assertTrue(mGestureHandler.onTouchEvent(event));
687
688         mGestureHandler.sendShowPressedStateGestureForTesting();
689         assertEquals("GESTURE_SHOW_PRESS should have been sent",
690                 GestureEventType.SHOW_PRESS,
691                         mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
692         assertEquals("Only GESTURE_TAP_DOWN and GESTURE_SHOW_PRESS should have been sent",
693                 2, mMockMotionEventDelegate.mGestureTypeList.size());
694
695         event = MotionEvent.obtain(
696                 downTime1, downTime1 + 5, MotionEvent.ACTION_UP,
697                 1, new PointerProperties[] { pp0 }, new PointerCoords[] { pc0 },
698                 0, 0, 1.0f, 1.0f, 0, 0, InputDevice.SOURCE_CLASS_POINTER, 0);
699         mGestureHandler.onTouchEvent(event);
700         assertEquals("A GESTURE_SINGLE_TAP_UNCONFIRMED event should have been sent",
701                 GestureEventType.SINGLE_TAP_UNCONFIRMED,
702                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
703         assertEquals("Only GESTURE_TAP_DOWN, " +
704                 "GESTURE_SHOW_PRESS and " +
705                 "GESTURE_TAP_UNCONFIRMED " +
706                 "should have been sent",
707                 3, mMockMotionEventDelegate.mGestureTypeList.size());
708
709         event = MotionEvent.obtain(
710                 downTime2, downTime2, MotionEvent.ACTION_DOWN,
711                 1, new PointerProperties[] { pp0 }, new PointerCoords[] { pc0 },
712                 0, 0, 1.0f, 1.0f, 0, 0, InputDevice.SOURCE_CLASS_POINTER, 0);
713         assertTrue(mGestureHandler.onTouchEvent(event));
714         assertEquals("GESTURE_TAP_DOWN should have been sent",
715                 GestureEventType.TAP_DOWN,
716                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
717         assertEquals("Only GESTURE_TAP_DOWN, " +
718                 "GESTURE_SHOW_PRESS, " +
719                 "GESTURE_TAP_UNCONFIRMED," +
720                 "GESTURE_TAP_CANCEL and" +
721                 "GESTURE_TAP_DOWN should have been sent",
722                 5, mMockMotionEventDelegate.mGestureTypeList.size());
723
724         pc0.y = pc0.y - 30;
725
726         event = MotionEvent.obtain(
727                 downTime2, downTime2 + 50, MotionEvent.ACTION_MOVE,
728                 1, new PointerProperties[] { pp0 }, new PointerCoords[] { pc0 },
729                 0, 0, 1.0f, 1.0f, 0, 0, InputDevice.SOURCE_CLASS_POINTER, 0);
730         assertTrue(mGestureHandler.onTouchEvent(event));
731         assertTrue("GESTURE_SCROLL_START should have been sent",
732                 mMockMotionEventDelegate.mGestureTypeList.contains(
733                         GestureEventType.SCROLL_START));
734
735         assertEquals("GESTURE_PINCH_BEGIN should have been sent",
736                 GestureEventType.PINCH_BEGIN,
737                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
738         assertEquals("Only GESTURE_TAP_DOWN, " +
739                 "GESTURE_SHOW_PRESS, " +
740                 "GESTURE_TAP_UNCONFIRMED," +
741                 "GESTURE_TAP_CANCEL, " +
742                 "GESTURE_TAP_DOWN, " +
743                 "GESTURE_TAP_CANCEL, " +
744                 "GESTURE_SCROLL_START, and " +
745                 "GESTURE_PINCH_BEGIN should have been sent",
746                 8, mMockMotionEventDelegate.mGestureTypeList.size());
747
748         event = MotionEvent.obtain(
749                 downTime2, downTime2 + 100, MotionEvent.ACTION_POINTER_DOWN,
750                 2, new PointerProperties[] { pp1, pp0 }, new PointerCoords[] { pc1, pc0 },
751                 0, 0, 1.0f, 1.0f, 0, 0, InputDevice.SOURCE_CLASS_POINTER, 0);
752         mGestureHandler.onTouchEvent(event);
753         assertTrue("GESTURE_PINCH_END should have been sent",
754                 mMockMotionEventDelegate.mGestureTypeList.contains(
755                         GestureEventType.PINCH_END));
756         assertEquals("GESTURE_SCROLL_END should have been sent",
757                 GestureEventType.SCROLL_END,
758                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
759         assertEquals("Only GESTURE_TAP_DOWN, " +
760                 "GESTURE_SHOW_PRESS, " +
761                 "GESTURE_TAP_UNCONFIRMED," +
762                 "GESTURE_TAP_CANCEL, " +
763                 "GESTURE_TAP_DOWN, " +
764                 "GESTURE_TAP_CANCEL, " +
765                 "GESTURE_SCROLL_START," +
766                 "GESTURE_PINCH_BEGIN, " +
767                 "GESTURE_PINCH_END, and " +
768                 "GESTURE_SCROLL_END should have been sent",
769                 10, mMockMotionEventDelegate.mGestureTypeList.size());
770
771         event = MotionEvent.obtain(
772                 downTime2, downTime2 + 150, MotionEvent.ACTION_POINTER_UP,
773                 2, new PointerProperties[] { pp1, pp0 }, new PointerCoords[] { pc1, pc0 },
774                 0, 0, 1.0f, 1.0f, 0, 0, InputDevice.SOURCE_CLASS_POINTER, 0);
775         mGestureHandler.onTouchEvent(event);
776         assertEquals("No new gestures should have been sent",
777                 10, mMockMotionEventDelegate.mGestureTypeList.size());
778     }
779
780     /**
781      * Verify that double tap drag zoom feature is not invoked
782      * when it is disabled..
783      * @throws Exception
784      */
785     @SmallTest
786     @Feature({"Gestures"})
787     public void testDoubleTapDragZoomNothingWhenDisabled() throws Exception {
788         final long downTime1 = SystemClock.uptimeMillis();
789         final long downTime2 = downTime1 + 100;
790
791         mGestureHandler.updateDoubleTapSupport(false);
792
793         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime1, downTime1);
794         assertTrue(mGestureHandler.onTouchEvent(event));
795
796         event = MotionEvent.obtain(
797                 downTime1, downTime1 + 5, MotionEvent.ACTION_UP,
798                 FAKE_COORD_X, FAKE_COORD_Y, 0);
799         mGestureHandler.onTouchEvent(event);
800
801         event = MotionEvent.obtain(
802                 downTime2, downTime2, MotionEvent.ACTION_DOWN,
803                 FAKE_COORD_X, FAKE_COORD_Y, 0);
804         assertTrue(mGestureHandler.onTouchEvent(event));
805
806         event = MotionEvent.obtain(
807                 downTime2, downTime2 + 5, MotionEvent.ACTION_MOVE,
808                 FAKE_COORD_X, FAKE_COORD_Y + 100, 0);
809
810         // The move should become a scroll, as double tap and drag to zoom is
811         // disabled.
812         assertTrue(mGestureHandler.onTouchEvent(event));
813         assertTrue("GESTURE_SCROLL_START should have been sent",
814                 mMockMotionEventDelegate.mGestureTypeList.contains(
815                         GestureEventType.SCROLL_START));
816         assertFalse("No GESTURE_PINCH_BEGIN should have been sent",
817                 mMockMotionEventDelegate.mGestureTypeList.contains(
818                         GestureEventType.PINCH_BEGIN));
819
820         event = MotionEvent.obtain(
821                 downTime2, downTime2 + 10, MotionEvent.ACTION_MOVE,
822                 FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
823         assertTrue(mGestureHandler.onTouchEvent(event));
824         assertEquals("GESTURE_SCROLL_BY should have been sent",
825                 GestureEventType.SCROLL_BY,
826                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
827         assertEquals("GESTURE_SCROLL_BY should have been sent",
828                 event.getEventTime(),
829                 mMockMotionEventDelegate.mMostRecentGestureEvent.getTimeMs());
830         assertTrue("No GESTURE_PINCH_BY should have been sent",
831                 GestureEventType.PINCH_BY !=
832                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
833
834         event = MotionEvent.obtain(
835                 downTime2, downTime2 + 15, MotionEvent.ACTION_UP,
836                 FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
837         assertTrue(mGestureHandler.onTouchEvent(event));
838         assertFalse("No GESTURE_PINCH_END should have been sent",
839                 mMockMotionEventDelegate.mGestureTypeList.contains(
840                         GestureEventType.PINCH_END));
841     }
842
843     /**
844      * Generate a scroll gesture and verify that the resulting scroll motion event has both absolute
845      * and relative position information.
846      */
847     @SmallTest
848     @Feature({"Gestures"})
849     public void testScrollUpdateCoordinates() {
850         final int deltaX = 16;
851         final int deltaY = 84;
852         final long downTime = SystemClock.uptimeMillis();
853
854         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
855         assertTrue(mGestureHandler.onTouchEvent(event));
856
857         // Move twice so that we get two GESTURE_SCROLL_BY events and can compare
858         // the relative and absolute coordinates.
859         event = MotionEvent.obtain(
860                 downTime, downTime + 5, MotionEvent.ACTION_MOVE,
861                 FAKE_COORD_X - deltaX / 2, FAKE_COORD_Y - deltaY / 2, 0);
862         assertTrue(mGestureHandler.onTouchEvent(event));
863
864         event = MotionEvent.obtain(
865                 downTime, downTime + 10, MotionEvent.ACTION_MOVE,
866                 FAKE_COORD_X - deltaX, FAKE_COORD_Y - deltaY, 0);
867         assertTrue(mGestureHandler.onTouchEvent(event));
868
869         // Make sure the reported gesture event has all the expected data.
870         MockMotionEventDelegate.GestureEvent gestureEvent =
871                 mMockMotionEventDelegate.mMostRecentGestureEvent;
872         assertNotNull(gestureEvent);
873         assertEquals(GestureEventType.SCROLL_BY, gestureEvent.getType());
874         assertEquals(downTime + 10, gestureEvent.getTimeMs());
875         assertEquals(FAKE_COORD_X - deltaX, gestureEvent.getX());
876         assertEquals(FAKE_COORD_Y - deltaY, gestureEvent.getY());
877
878         Bundle extraParams = gestureEvent.getExtraParams();
879         assertNotNull(extraParams);
880         // No horizontal delta because of snapping.
881         assertEquals(0, extraParams.getInt(ContentViewGestureHandler.DISTANCE_X));
882         assertEquals(deltaY / 2, extraParams.getInt(ContentViewGestureHandler.DISTANCE_Y));
883     }
884
885     /**
886      * Generate a scroll gesture and verify that the resulting scroll start event
887      * has the expected hint values.
888      */
889     @SmallTest
890     @Feature({"Gestures"})
891     public void testScrollStartValues() {
892         final int deltaX = 13;
893         final int deltaY = 89;
894         final long downTime = SystemClock.uptimeMillis();
895
896         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
897         assertTrue(mGestureHandler.onTouchEvent(event));
898
899         // Move twice such that the first event isn't sufficient to start
900         // scrolling on it's own.
901         event = MotionEvent.obtain(
902                 downTime, downTime + 5, MotionEvent.ACTION_MOVE,
903                 FAKE_COORD_X + 2, FAKE_COORD_Y + 1, 0);
904         assertTrue(mGestureHandler.onTouchEvent(event));
905         assertNull("Expect scrolling hasn't yet started",
906                 mMockMotionEventDelegate.mActiveScrollStartEvent);
907
908         event = MotionEvent.obtain(
909                 downTime, downTime + 10, MotionEvent.ACTION_MOVE,
910                 FAKE_COORD_X + deltaX, FAKE_COORD_Y + deltaY, 0);
911         assertTrue(mGestureHandler.onTouchEvent(event));
912
913         MockMotionEventDelegate.GestureEvent startEvent =
914                 mMockMotionEventDelegate.mActiveScrollStartEvent;
915         assertNotNull(startEvent);
916         assertEquals(GestureEventType.SCROLL_START, startEvent.getType());
917         assertEquals(downTime + 10, startEvent.getTimeMs());
918         assertEquals(FAKE_COORD_X, startEvent.getX());
919         assertEquals(FAKE_COORD_Y, startEvent.getY());
920
921         Bundle extraParams = startEvent.getExtraParams();
922         assertNotNull(extraParams);
923         assertEquals(deltaX, extraParams.getInt(ContentViewGestureHandler.DELTA_HINT_X));
924         assertEquals(deltaY, extraParams.getInt(ContentViewGestureHandler.DELTA_HINT_Y));
925     }
926
927     /**
928      * Verify that the timer of LONG_PRESS will be cancelled when scrolling begins so
929      * LONG_PRESS and LONG_TAP won't be triggered.
930      *
931      * @throws Exception
932      */
933     @SmallTest
934     @Feature({"Gestures"})
935     public void testLongPressAndTapCancelWhenScrollBegins() throws Exception {
936         final long downTime = SystemClock.uptimeMillis();
937         final long eventTime = SystemClock.uptimeMillis();
938
939         getInstrumentation().runOnMainSync(new Runnable() {
940             @Override
941             public void run() {
942                 setUp();
943
944                 MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
945                 assertTrue(mGestureHandler.onTouchEvent(event));
946                 event = MotionEvent.obtain(
947                         downTime, eventTime + 5, MotionEvent.ACTION_MOVE,
948                         FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
949                 assertTrue(mGestureHandler.onTouchEvent(event));
950                 event = MotionEvent.obtain(
951                         downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
952                         FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
953                 assertTrue(mGestureHandler.onTouchEvent(event));
954             }
955         });
956
957         assertFalse(mMockMotionEventDelegate.mLongPressCalled.await(
958                 ScalableTimeout.scaleTimeout(ViewConfiguration.getLongPressTimeout()
959                         + ViewConfiguration.getTapTimeout() + 10),
960                 TimeUnit.MILLISECONDS));
961
962         // No LONG_TAP because LONG_PRESS timer is cancelled.
963         assertFalse("No LONG_PRESS should be sent",
964                 mMockMotionEventDelegate.mGestureTypeList.contains(
965                         GestureEventType.LONG_PRESS));
966         assertFalse("No LONG_TAP should be sent",
967                 mMockMotionEventDelegate.mGestureTypeList.contains(
968                         GestureEventType.LONG_TAP));
969     }
970
971     /**
972      * Verify that LONG_TAP is triggered after LongPress followed by an UP.
973      *
974      * @throws Exception
975      */
976     @SmallTest
977     @Feature({"Gestures"})
978     public void testGestureLongTap() throws Exception {
979         final long downTime = SystemClock.uptimeMillis();
980         final long eventTime = SystemClock.uptimeMillis();
981
982         getInstrumentation().runOnMainSync(new Runnable() {
983             @Override
984             public void run() {
985                 setUp();
986                 mGestureHandler = new ContentViewGestureHandler(
987                         getInstrumentation().getTargetContext(), mMockMotionEventDelegate);
988                 MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
989                 assertTrue(mGestureHandler.onTouchEvent(event));
990             }
991         });
992
993         assertTrue(mMockMotionEventDelegate.mLongPressCalled.await(
994                 ScalableTimeout.scaleTimeout(ViewConfiguration.getLongPressTimeout()
995                         + ViewConfiguration.getTapTimeout() + 10),
996                 TimeUnit.MILLISECONDS));
997
998         assertEquals("A LONG_PRESS gesture should have been sent",
999                 GestureEventType.LONG_PRESS,
1000                         mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
1001
1002         MotionEvent event = motionEvent(MotionEvent.ACTION_UP, downTime, eventTime + 1000);
1003         mGestureHandler.onTouchEvent(event);
1004         assertEquals("A LONG_TAP gesture should have been sent",
1005                 GestureEventType.LONG_TAP,
1006                         mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
1007     }
1008
1009     /**
1010      * Verify that a LONG_PRESS gesture does not prevent further scrolling.
1011      *
1012      * @throws Exception
1013      */
1014     @SmallTest
1015     @Feature({"Gestures"})
1016     public void testGestureLongPressDoesNotPreventScrolling() throws Exception {
1017         final long downTime = SystemClock.uptimeMillis();
1018         final long eventTime = SystemClock.uptimeMillis();
1019
1020         getInstrumentation().runOnMainSync(new Runnable() {
1021             @Override
1022             public void run() {
1023                 setUp();
1024                 mGestureHandler = new ContentViewGestureHandler(
1025                         getInstrumentation().getTargetContext(), mMockMotionEventDelegate);
1026                 MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
1027                 assertTrue(mGestureHandler.onTouchEvent(event));
1028             }
1029         });
1030
1031         final long longPressTimeoutMs = ViewConfiguration.getLongPressTimeout()
1032                 + ViewConfiguration.getTapTimeout() + 10;
1033         assertTrue(mMockMotionEventDelegate.mLongPressCalled.await(
1034                 ScalableTimeout.scaleTimeout(longPressTimeoutMs), TimeUnit.MILLISECONDS));
1035
1036         assertEquals("A LONG_PRESS gesture should have been sent",
1037                 GestureEventType.LONG_PRESS,
1038                         mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
1039
1040         MotionEvent event = MotionEvent.obtain(
1041                 downTime, eventTime + longPressTimeoutMs, MotionEvent.ACTION_MOVE,
1042                 FAKE_COORD_X + 100, FAKE_COORD_Y + 100, 0);
1043         mGestureHandler.onTouchEvent(event);
1044
1045         assertEquals("Scrolling should have started",
1046                 GestureEventType.SCROLL_BY,
1047                         mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
1048         assertTrue(mMockMotionEventDelegate.mGestureTypeList.contains(
1049                 GestureEventType.SCROLL_START));
1050         assertTrue(mMockMotionEventDelegate.mGestureTypeList.contains(
1051                 GestureEventType.TAP_CANCEL));
1052
1053         event = motionEvent(MotionEvent.ACTION_UP, downTime, eventTime + longPressTimeoutMs);
1054         mGestureHandler.onTouchEvent(event);
1055         assertFalse("Scrolling should have prevented the LONG_TAP",
1056                 mMockMotionEventDelegate.mGestureTypeList.contains(
1057                         GestureEventType.LONG_TAP));
1058     }
1059
1060     /**
1061      * Verify that LONG_PRESS is not fired during a double-tap sequence.
1062      *
1063      * @throws Exception
1064      */
1065     @SmallTest
1066     @Feature({"Gestures"})
1067     public void testNoGestureLongPressDuringDoubleTap() throws Exception {
1068         final long downTime = SystemClock.uptimeMillis();
1069         final long eventTime = SystemClock.uptimeMillis();
1070
1071         getInstrumentation().runOnMainSync(new Runnable() {
1072             @Override
1073             public void run() {
1074                 setUp();
1075                 mGestureHandler.updateDoubleTapSupport(true);
1076
1077                 MotionEvent event = MotionEvent.obtain(
1078                         downTime, eventTime, MotionEvent.ACTION_DOWN,
1079                         FAKE_COORD_X, FAKE_COORD_Y, 0);
1080                 assertTrue(mGestureHandler.onTouchEvent(event));
1081
1082                 event = MotionEvent.obtain(
1083                         downTime, eventTime + 1, MotionEvent.ACTION_UP,
1084                         FAKE_COORD_X, FAKE_COORD_Y, 0);
1085                 mGestureHandler.onTouchEvent(event);
1086                 assertEquals("A GESTURE_SINGLE_TAP_UNCONFIRMED event should have been sent",
1087                         GestureEventType.SINGLE_TAP_UNCONFIRMED,
1088                         mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
1089
1090                 event = MotionEvent.obtain(
1091                         downTime + 2, eventTime + 2, MotionEvent.ACTION_DOWN,
1092                         FAKE_COORD_X, FAKE_COORD_Y, 0);
1093                 assertTrue(mGestureHandler.onTouchEvent(event));
1094                 assertEquals("A GESTURE_TAP_DOWN event should have been sent ",
1095                         GestureEventType.TAP_DOWN,
1096                         mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
1097                 assertTrue(mGestureHandler.isDoubleTapActive());
1098             }
1099         });
1100
1101         final int longPressTimeoutMs = ViewConfiguration.getLongPressTimeout()
1102                 + ViewConfiguration.getTapTimeout() + 10;
1103
1104         assertFalse(mMockMotionEventDelegate.mLongPressCalled.await(
1105                 longPressTimeoutMs, TimeUnit.MILLISECONDS));
1106
1107         assertFalse("A LONG_PRESS gesture should not have been sent",
1108                 GestureEventType.LONG_PRESS
1109                         == mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
1110
1111         MotionEvent event = MotionEvent.obtain(
1112                 downTime + 2, eventTime + longPressTimeoutMs, MotionEvent.ACTION_MOVE,
1113                 FAKE_COORD_X + 20, FAKE_COORD_Y + 20, 0);
1114         assertTrue(mGestureHandler.onTouchEvent(event));
1115         assertEquals("A double tap drag should have started",
1116                 GestureEventType.PINCH_BEGIN,
1117                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
1118         assertTrue(mGestureHandler.isDoubleTapActive());
1119
1120         event = MotionEvent.obtain(
1121                 downTime + 2, eventTime + longPressTimeoutMs, MotionEvent.ACTION_UP,
1122                 FAKE_COORD_X, FAKE_COORD_Y + 1, 0);
1123         assertTrue(mGestureHandler.onTouchEvent(event));
1124         assertEquals("A double tap drag should have ended",
1125                 GestureEventType.SCROLL_END,
1126                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
1127         assertFalse(mGestureHandler.isDoubleTapActive());
1128     }
1129
1130     /**
1131      * Verify that the touch slop region is removed from the first scroll delta to avoid a jump when
1132      * starting to scroll.
1133      * @throws Exception
1134      */
1135     @SmallTest
1136     @Feature({"Gestures"})
1137     public void testTouchSlopRemovedFromScroll() throws Exception {
1138         Context context = getInstrumentation().getTargetContext();
1139         final long downTime = SystemClock.uptimeMillis();
1140         final long eventTime = SystemClock.uptimeMillis();
1141         final int scaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
1142         final int scrollDelta = 5;
1143
1144         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
1145         assertTrue(mGestureHandler.onTouchEvent(event));
1146
1147         event = MotionEvent.obtain(
1148                 downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
1149                 FAKE_COORD_X, FAKE_COORD_Y + scaledTouchSlop + scrollDelta, 0);
1150         assertTrue(mGestureHandler.onTouchEvent(event));
1151
1152         assertEquals("We should have started scrolling",
1153                 GestureEventType.SCROLL_BY,
1154                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
1155
1156         MockMotionEventDelegate.GestureEvent gestureEvent =
1157                 mMockMotionEventDelegate.mMostRecentGestureEvent;
1158         assertNotNull(gestureEvent);
1159         Bundle extraParams = gestureEvent.getExtraParams();
1160         assertEquals(0, extraParams.getInt(ContentViewGestureHandler.DISTANCE_X));
1161         assertEquals(-scrollDelta, extraParams.getInt(ContentViewGestureHandler.DISTANCE_Y));
1162     }
1163
1164     /**
1165      * Verify that a DOWN followed shortly by an UP will trigger
1166      * a GESTURE_SINGLE_TAP_UNCONFIRMED event immediately.
1167      *
1168      * @throws Exception
1169      */
1170     @SmallTest
1171     @Feature({"Gestures"})
1172     public void testGestureEventsSingleTapUnconfirmed() throws Exception {
1173         final long downTime = SystemClock.uptimeMillis();
1174         final long eventTime = SystemClock.uptimeMillis();
1175
1176         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
1177         assertTrue(mGestureHandler.onTouchEvent(event));
1178         assertEquals("A TAP_DOWN gesture should have been sent",
1179                 GestureEventType.TAP_DOWN,
1180                         mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
1181
1182         event = motionEvent(MotionEvent.ACTION_UP, downTime, eventTime + 10);
1183         assertFalse(mGestureHandler.onTouchEvent(event));
1184         assertEquals("A GESTURE_SINGLE_TAP_UNCONFIRMED gesture should have been sent",
1185                 GestureEventType.SINGLE_TAP_UNCONFIRMED,
1186                         mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
1187         assertFalse(mMockMotionEventDelegate.mGestureTypeList.contains(
1188                 GestureEventType.SINGLE_TAP_CONFIRMED));
1189     }
1190
1191     /**
1192      * Verify that a tap-ending event will follow a TAP_DOWN event.
1193      *
1194      * @throws Exception
1195      */
1196     @SmallTest
1197     @Feature({"Gestures"})
1198     public void testTapDownFollowedByTapEndingEvent() throws Exception {
1199         long downTime = SystemClock.uptimeMillis();
1200         long eventTime = SystemClock.uptimeMillis();
1201
1202         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
1203         assertTrue(mGestureHandler.onTouchEvent(event));
1204         assertEquals(GestureEventType.TAP_DOWN,
1205                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
1206         assertTrue(mGestureHandler.needsTapEndingEventForTesting());
1207         event = motionEvent(MotionEvent.ACTION_UP, downTime, eventTime + 5);
1208         mGestureHandler.onTouchEvent(event);
1209         assertEquals(GestureEventType.SINGLE_TAP_UNCONFIRMED,
1210                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
1211         assertTrue("An unconfirmed tap does not terminate the tap down.",
1212                 mGestureHandler.needsTapEndingEventForTesting());
1213
1214         // A confirmed tap is a tap-ending event.
1215         downTime += 20;
1216         eventTime += 20;
1217         mMockMotionEventDelegate.mGestureTypeList.clear();
1218         mGestureHandler.updateShouldDisableDoubleTap(true);
1219         event = MotionEvent.obtain(
1220                 downTime, downTime, MotionEvent.ACTION_DOWN,
1221                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1222         assertTrue(mGestureHandler.onTouchEvent(event));
1223         assertTrue(mGestureHandler.needsTapEndingEventForTesting());
1224         event = MotionEvent.obtain(
1225                 downTime, eventTime + 5, MotionEvent.ACTION_UP,
1226                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1227         assertTrue(mGestureHandler.onTouchEvent(event));
1228         assertEquals(GestureEventType.SINGLE_TAP_CONFIRMED,
1229                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
1230         assertFalse("A confirmed single tap should terminate the tap down.",
1231                 mGestureHandler.needsTapEndingEventForTesting());
1232
1233         // A double tap gesture is a tap-ending event.
1234         downTime += 20;
1235         eventTime += 20;
1236         mMockMotionEventDelegate.mGestureTypeList.clear();
1237         mGestureHandler.updateShouldDisableDoubleTap(false);
1238         event = MotionEvent.obtain(
1239                 downTime, downTime, MotionEvent.ACTION_DOWN,
1240                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1241         assertTrue(mGestureHandler.onTouchEvent(event));
1242         assertTrue(mGestureHandler.needsTapEndingEventForTesting());
1243         event = MotionEvent.obtain(
1244                 downTime, eventTime + 5, MotionEvent.ACTION_UP,
1245                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1246         event = MotionEvent.obtain(
1247                 eventTime + 10, eventTime + 10, MotionEvent.ACTION_DOWN,
1248                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1249         assertTrue(mGestureHandler.onTouchEvent(event));
1250         assertTrue(mGestureHandler.needsTapEndingEventForTesting());
1251         event = MotionEvent.obtain(
1252                 eventTime + 10, eventTime + 15, MotionEvent.ACTION_UP,
1253                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1254         assertTrue(mGestureHandler.onTouchEvent(event));
1255         assertEquals(GestureEventType.DOUBLE_TAP,
1256                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
1257         assertFalse("A double tap should terminate the tap down.",
1258                 mGestureHandler.needsTapEndingEventForTesting());
1259
1260         // A double tap drag gesture will trigger a tap-ending event.
1261         downTime += 20;
1262         eventTime += 20;
1263         mMockMotionEventDelegate.mGestureTypeList.clear();
1264         mGestureHandler.updateShouldDisableDoubleTap(false);
1265         event = MotionEvent.obtain(
1266                 downTime, downTime, MotionEvent.ACTION_DOWN,
1267                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1268         assertTrue(mGestureHandler.onTouchEvent(event));
1269         assertTrue(mGestureHandler.needsTapEndingEventForTesting());
1270         event = MotionEvent.obtain(
1271                 downTime, eventTime + 5, MotionEvent.ACTION_UP,
1272                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1273         event = MotionEvent.obtain(
1274                 eventTime + 10, eventTime + 10, MotionEvent.ACTION_DOWN,
1275                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1276         assertTrue(mGestureHandler.onTouchEvent(event));
1277         assertTrue(mGestureHandler.needsTapEndingEventForTesting());
1278         event = MotionEvent.obtain(
1279                 eventTime + 10, eventTime + 15, MotionEvent.ACTION_MOVE,
1280                 FAKE_COORD_X, FAKE_COORD_Y + 100, 0);
1281         assertTrue(mGestureHandler.onTouchEvent(event));
1282         assertFalse("A double tap drag should terminate the tap down.",
1283                 mGestureHandler.needsTapEndingEventForTesting());
1284         assertTrue(mMockMotionEventDelegate.mGestureTypeList.contains(
1285                 GestureEventType.SCROLL_START));
1286         assertTrue(mMockMotionEventDelegate.mGestureTypeList.contains(
1287                 GestureEventType.TAP_CANCEL));
1288         event = MotionEvent.obtain(
1289                 eventTime + 10, eventTime + 20, MotionEvent.ACTION_UP,
1290                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1291         assertTrue(mGestureHandler.onTouchEvent(event));
1292         assertFalse(mGestureHandler.needsTapEndingEventForTesting());
1293
1294         // A scroll event will trigger a tap-ending (cancel) event.
1295         downTime += 25;
1296         eventTime += 25;
1297         mMockMotionEventDelegate.mGestureTypeList.clear();
1298         event = MotionEvent.obtain(
1299                 downTime, downTime, MotionEvent.ACTION_DOWN,
1300                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1301         assertTrue(mGestureHandler.onTouchEvent(event));
1302         assertTrue(mGestureHandler.needsTapEndingEventForTesting());
1303         event = MotionEvent.obtain(
1304                 downTime, eventTime + 5, MotionEvent.ACTION_MOVE,
1305                 FAKE_COORD_X, FAKE_COORD_Y + 100, 0);
1306         assertTrue(mGestureHandler.onTouchEvent(event));
1307         assertTrue(mMockMotionEventDelegate.mGestureTypeList.contains(
1308                 GestureEventType.SCROLL_START));
1309         assertTrue(mMockMotionEventDelegate.mGestureTypeList.contains(
1310                 GestureEventType.TAP_CANCEL));
1311         assertFalse("A scroll should terminate the tap down.",
1312                    mGestureHandler.needsTapEndingEventForTesting());
1313         assertFalse(mGestureHandler.needsTapEndingEventForTesting());
1314     }
1315
1316     /**
1317      * Verify that no double tap gestures are created if the gesture handler is
1318      * told to disable double tap gesture detection (according to the logic in
1319      * ContentViewCore.onRenderCoordinatesUpdated).
1320      * @throws Exception
1321      */
1322     @SmallTest
1323     @Feature({"Gestures"})
1324     public void testNoDoubleTapWhenDoubleTapDisabled() throws Exception {
1325         final long downTime = SystemClock.uptimeMillis();
1326         final long eventTime = SystemClock.uptimeMillis();
1327
1328         mGestureHandler.updateShouldDisableDoubleTap(true);
1329
1330         MotionEvent event = MotionEvent.obtain(
1331                 downTime, downTime, MotionEvent.ACTION_DOWN,
1332                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1333         assertTrue(mGestureHandler.onTouchEvent(event));
1334         assertEquals("Only GESTURE_TAP_DOWN should have been sent",
1335                 1, mMockMotionEventDelegate.mGestureTypeList.size());
1336
1337         event = MotionEvent.obtain(
1338                 downTime, eventTime + 5, MotionEvent.ACTION_UP,
1339                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1340         assertTrue(mGestureHandler.onTouchEvent(event));
1341         assertEquals("A GESTURE_SINGLE_TAP_CONFIRMED event should have been sent",
1342                 GestureEventType.SINGLE_TAP_CONFIRMED,
1343                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
1344         assertEquals("Only GESTURE_TAP_DOWN and GESTURE_SINGLE_TAP_CONFIRMED " +
1345                 "should have been sent",
1346                 2, mMockMotionEventDelegate.mGestureTypeList.size());
1347
1348         event = MotionEvent.obtain(
1349                 eventTime + 10, eventTime + 10, MotionEvent.ACTION_DOWN,
1350                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1351         assertTrue(mGestureHandler.onTouchEvent(event));
1352         assertEquals("Only GESTURE_TAP_DOWN, " +
1353                 "GESTURE_SINGLE_TAP_CONFIRMED and " +
1354                 "GESTURE_TAP_DOWN should have been sent",
1355                 3, mMockMotionEventDelegate.mGestureTypeList.size());
1356
1357         event = MotionEvent.obtain(
1358                 eventTime + 10, eventTime + 15, MotionEvent.ACTION_UP,
1359                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1360         assertTrue(mGestureHandler.onTouchEvent(event));
1361         assertEquals("A double tap should not have occurred",
1362                 GestureEventType.SINGLE_TAP_CONFIRMED,
1363                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
1364         assertEquals("Only GESTURE_TAP_DOWN, " +
1365                 "GESTURE_SINGLE_TAP_CONFIRMED, " +
1366                 "GESTURE_TAP_DOWN and " +
1367                 "GESTURE_SINGLE_TAP_CONFIRMED should have been sent",
1368                 4, mMockMotionEventDelegate.mGestureTypeList.size());
1369     }
1370
1371     /**
1372      * Verify that double tap drag zoom feature is not invoked when the gesture
1373      * handler is told to disable double tap gesture detection (according to the
1374      * logic in ContentViewCore.onRenderCoordinatesUpdated).
1375      * The second tap sequence should be treated just as the first would be.
1376      * @throws Exception
1377      */
1378     @SmallTest
1379     @Feature({"Gestures"})
1380     public void testNoDoubleTapDragZoomWhenDoubleTapDisabled() throws Exception {
1381         final long downTime1 = SystemClock.uptimeMillis();
1382         final long downTime2 = downTime1 + 100;
1383
1384         mGestureHandler.updateShouldDisableDoubleTap(true);
1385
1386         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime1, downTime1);
1387         assertTrue(mGestureHandler.onTouchEvent(event));
1388
1389         event = MotionEvent.obtain(
1390                 downTime1, downTime1 + 5, MotionEvent.ACTION_UP,
1391                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1392         mGestureHandler.onTouchEvent(event);
1393
1394         event = MotionEvent.obtain(
1395                 downTime2, downTime2, MotionEvent.ACTION_DOWN,
1396                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1397         assertTrue(mGestureHandler.onTouchEvent(event));
1398
1399         event = MotionEvent.obtain(
1400                 downTime2, downTime2 + 5, MotionEvent.ACTION_MOVE,
1401                 FAKE_COORD_X, FAKE_COORD_Y + 100, 0);
1402
1403         // The move should become a scroll, as double tap and drag to zoom is
1404         // disabled.
1405         assertTrue(mGestureHandler.onTouchEvent(event));
1406         assertTrue("GESTURE_SCROLL_START should have been sent",
1407                 mMockMotionEventDelegate.mGestureTypeList.contains(
1408                         GestureEventType.SCROLL_START));
1409         assertFalse("No GESTURE_PINCH_BEGIN should have been sent",
1410                 mMockMotionEventDelegate.mGestureTypeList.contains(
1411                         GestureEventType.PINCH_BEGIN));
1412
1413         event = MotionEvent.obtain(
1414                 downTime2, downTime2 + 10, MotionEvent.ACTION_MOVE,
1415                 FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
1416         assertTrue(mGestureHandler.onTouchEvent(event));
1417         assertEquals("GESTURE_SCROLL_BY should have been sent",
1418                 GestureEventType.SCROLL_BY,
1419                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
1420         assertEquals("GESTURE_SCROLL_BY should have been sent",
1421                 event.getEventTime(),
1422                 mMockMotionEventDelegate.mMostRecentGestureEvent.getTimeMs());
1423         assertTrue("No GESTURE_PINCH_BY should have been sent",
1424                 GestureEventType.PINCH_BY !=
1425                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
1426
1427         event = MotionEvent.obtain(
1428                 downTime2, downTime2 + 15, MotionEvent.ACTION_UP,
1429                 FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
1430         assertTrue(mGestureHandler.onTouchEvent(event));
1431         assertFalse("No GESTURE_PINCH_END should have been sent",
1432                 mMockMotionEventDelegate.mGestureTypeList.contains(
1433                         GestureEventType.PINCH_END));
1434     }
1435
1436     /**
1437      * Verify that setting a fixed page scale (or a mobile viewport) during a double
1438      * tap drag zoom disables double tap detection after the gesture has ended.
1439      * @throws Exception
1440      */
1441     @SmallTest
1442     @Feature({"Gestures"})
1443     public void testFixedPageScaleDuringDoubleTapDragZoom() throws Exception {
1444         long downTime1 = SystemClock.uptimeMillis();
1445         long downTime2 = downTime1 + 100;
1446
1447         // Start a double-tap drag gesture.
1448         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime1, downTime1);
1449         assertTrue(mGestureHandler.onTouchEvent(event));
1450         mGestureHandler.sendShowPressedStateGestureForTesting();
1451         event = MotionEvent.obtain(
1452                 downTime1, downTime1 + 5, MotionEvent.ACTION_UP,
1453                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1454         mGestureHandler.onTouchEvent(event);
1455         event = MotionEvent.obtain(
1456                 downTime2, downTime2, MotionEvent.ACTION_DOWN,
1457                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1458         assertTrue(mGestureHandler.onTouchEvent(event));
1459         event = MotionEvent.obtain(
1460                 downTime2, downTime2 + 5, MotionEvent.ACTION_MOVE,
1461                 FAKE_COORD_X, FAKE_COORD_Y + 100, 0);
1462         assertTrue(mGestureHandler.onTouchEvent(event));
1463         assertTrue("GESTURE_SCROLL_START should have been sent",
1464                 mMockMotionEventDelegate.mGestureTypeList.contains(
1465                         GestureEventType.SCROLL_START));
1466         assertEquals("GESTURE_PINCH_BEGIN should have been sent",
1467                 GestureEventType.PINCH_BEGIN,
1468                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
1469
1470         // Simulate setting a fixed page scale (or a mobile viewport);
1471         // this should not disrupt the current double-tap gesture.
1472         mGestureHandler.updateShouldDisableDoubleTap(true);
1473
1474         // Double tap zoom updates should continue.
1475         event = MotionEvent.obtain(
1476                 downTime2, downTime2 + 10, MotionEvent.ACTION_MOVE,
1477                 FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
1478         assertTrue(mGestureHandler.onTouchEvent(event));
1479         assertTrue("GESTURE_SCROLL_BY should have been sent",
1480                 mMockMotionEventDelegate.mGestureTypeList.contains(
1481                         GestureEventType.SCROLL_BY));
1482         assertEquals("GESTURE_PINCH_BY should have been sent",
1483                 GestureEventType.PINCH_BY,
1484                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
1485         event = MotionEvent.obtain(
1486                 downTime2, downTime2 + 15, MotionEvent.ACTION_UP,
1487                 FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
1488         assertTrue(mGestureHandler.onTouchEvent(event));
1489         assertTrue("GESTURE_PINCH_END should have been sent",
1490                 mMockMotionEventDelegate.mGestureTypeList.contains(
1491                         GestureEventType.PINCH_END));
1492         assertEquals("GESTURE_SCROLL_END should have been sent",
1493                 GestureEventType.SCROLL_END,
1494                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
1495
1496         // The double-tap gesture has finished, but the page scale is fixed.
1497         // The same event sequence should not generate any double tap getsures.
1498         mMockMotionEventDelegate.mGestureTypeList.clear();
1499         downTime1 += 200;
1500         downTime2 += 200;
1501
1502         // Start a double-tap drag gesture.
1503         event = motionEvent(MotionEvent.ACTION_DOWN, downTime1, downTime1);
1504         assertTrue(mGestureHandler.onTouchEvent(event));
1505         event = MotionEvent.obtain(
1506                 downTime1, downTime1 + 5, MotionEvent.ACTION_UP,
1507                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1508         mGestureHandler.onTouchEvent(event);
1509         event = MotionEvent.obtain(
1510                 downTime2, downTime2, MotionEvent.ACTION_DOWN,
1511                 FAKE_COORD_X, FAKE_COORD_Y, 0);
1512         assertTrue(mGestureHandler.onTouchEvent(event));
1513         event = MotionEvent.obtain(
1514                 downTime2, downTime2 + 5, MotionEvent.ACTION_MOVE,
1515                 FAKE_COORD_X, FAKE_COORD_Y + 100, 0);
1516         assertTrue(mGestureHandler.onTouchEvent(event));
1517         assertTrue("GESTURE_SCROLL_START should have been sent",
1518                 mMockMotionEventDelegate.mGestureTypeList.contains(
1519                         GestureEventType.SCROLL_START));
1520         assertFalse("GESTURE_PINCH_BEGIN should not have been sent",
1521                 mMockMotionEventDelegate.mGestureTypeList.contains(
1522                         GestureEventType.PINCH_BEGIN));
1523
1524         // Double tap zoom updates should not be sent.
1525         // Instead, the second tap drag becomes a scroll gesture sequence.
1526         event = MotionEvent.obtain(
1527                 downTime2, downTime2 + 10, MotionEvent.ACTION_MOVE,
1528                 FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
1529         assertTrue(mGestureHandler.onTouchEvent(event));
1530         assertTrue("GESTURE_SCROLL_BY should have been sent",
1531                 mMockMotionEventDelegate.mGestureTypeList.contains(
1532                         GestureEventType.SCROLL_BY));
1533         assertFalse("GESTURE_PINCH_BY should not have been sent",
1534                 mMockMotionEventDelegate.mGestureTypeList.contains(
1535                         GestureEventType.PINCH_BY));
1536         event = MotionEvent.obtain(
1537                 downTime2, downTime2 + 15, MotionEvent.ACTION_UP,
1538                 FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
1539         assertTrue(mGestureHandler.onTouchEvent(event));
1540         assertFalse("GESTURE_PINCH_END should not have been sent",
1541                 mMockMotionEventDelegate.mGestureTypeList.contains(
1542                         GestureEventType.PINCH_END));
1543     }
1544
1545     /**
1546      * Verify that pinch zoom sends the proper event sequence.
1547      * @throws Exception
1548      */
1549     @SmallTest
1550     @Feature({"Gestures"})
1551     public void testPinchZoom() throws Exception {
1552         final long downTime = SystemClock.uptimeMillis();
1553         final long eventTime = SystemClock.uptimeMillis();
1554         final Context context = getInstrumentation().getTargetContext();
1555         final int scaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
1556
1557         mGestureHandler.updateMultiTouchSupport(true);
1558
1559         final int secondaryCoordX = FAKE_COORD_X + 20 * scaledTouchSlop;
1560         final int secondaryCoordY = FAKE_COORD_Y + 20 * scaledTouchSlop;
1561
1562         PointerProperties pp0 = new PointerProperties();
1563         pp0.id = 0;
1564         pp0.toolType = MotionEvent.TOOL_TYPE_FINGER;
1565         PointerProperties pp1 = new PointerProperties();
1566         pp1.id = 1;
1567         pp1.toolType = MotionEvent.TOOL_TYPE_FINGER;
1568
1569         PointerCoords pc0 = new PointerCoords();
1570         pc0.x = FAKE_COORD_X;
1571         pc0.y = FAKE_COORD_Y;
1572         pc0.pressure = 1;
1573         pc0.size = 1;
1574         PointerCoords pc1 = new PointerCoords();
1575         pc1.x = secondaryCoordX;
1576         pc1.y = secondaryCoordY;
1577         pc1.pressure = 1;
1578         pc1.size = 1;
1579
1580         MotionEvent event = MotionEvent.obtain(
1581                 eventTime, eventTime, MotionEvent.ACTION_DOWN,
1582                 1, new PointerProperties[] { pp0 }, new PointerCoords[] { pc0 },
1583                 0, 0, 1.0f, 1.0f, 0, 0, InputDevice.SOURCE_CLASS_POINTER, 0);
1584         assertTrue(mGestureHandler.onTouchEvent(event));
1585         assertEquals(GestureEventType.TAP_DOWN,
1586                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
1587         assertEquals("Only GESTURE_TAP_DOWN should have been sent",
1588                 1, mMockMotionEventDelegate.mGestureTypeList.size());
1589
1590         event = MotionEvent.obtain(
1591                 eventTime, eventTime, MotionEvent.ACTION_POINTER_DOWN,
1592                 2, new PointerProperties[] { pp1, pp0 }, new PointerCoords[] { pc1, pc0 },
1593                 0, 0, 1.0f, 1.0f, 0, 0, InputDevice.SOURCE_CLASS_POINTER, 0);
1594         mGestureHandler.onTouchEvent(event);
1595         assertEquals("No additional gestures should be triggered on a secondary pointer press",
1596                 1, mMockMotionEventDelegate.mGestureTypeList.size());
1597
1598         pc1.x = secondaryCoordX + 5 * scaledTouchSlop;
1599         pc1.y = secondaryCoordY + 5 * scaledTouchSlop;
1600
1601         event = MotionEvent.obtain(
1602                 eventTime, eventTime + 10, MotionEvent.ACTION_MOVE,
1603                 2, new PointerProperties[] { pp1, pp0 }, new PointerCoords[] { pc1, pc0 },
1604                 0, 0, 1.0f, 1.0f, 0, 0, InputDevice.SOURCE_CLASS_POINTER, 0);
1605         assertTrue(mGestureHandler.onTouchEvent(event));
1606         assertEquals("Only GESTURE_TAP_DOWN, " +
1607                 "GESTURE_TAP_CANCEL, " +
1608                 "GESTURE_SCROLL_BEGIN, " +
1609                 "GESTURE_PINCH_BEGIN, " +
1610                 "GESTURE_SCROLL_BY, and " +
1611                 "GESTURE_PINCH_BY should have been sent",
1612                 6, mMockMotionEventDelegate.mGestureTypeList.size());
1613         assertTrue(mMockMotionEventDelegate.mGestureTypeList.contains(
1614                 GestureEventType.PINCH_BEGIN));
1615         assertTrue(mMockMotionEventDelegate.mGestureTypeList.contains(
1616                 GestureEventType.SCROLL_START));
1617         assertTrue(mMockMotionEventDelegate.mGestureTypeList.contains(
1618                 GestureEventType.PINCH_BY));
1619         assertTrue(mMockMotionEventDelegate.mGestureTypeList.contains(
1620                 GestureEventType.SCROLL_BY));
1621
1622         event = MotionEvent.obtain(
1623                 eventTime, eventTime + 10, MotionEvent.ACTION_POINTER_UP,
1624                 2, new PointerProperties[] { pp1, pp0 }, new PointerCoords[] { pc1, pc0 },
1625                 0, 0, 1.0f, 1.0f, 0, 0, InputDevice.SOURCE_CLASS_POINTER, 0);
1626         assertTrue(mGestureHandler.onTouchEvent(event));
1627         assertEquals(GestureEventType.PINCH_END,
1628                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
1629         assertEquals("Only GESTURE_PINCH_END should have been sent",
1630                 7, mMockMotionEventDelegate.mGestureTypeList.size());
1631
1632         event = MotionEvent.obtain(
1633                 eventTime, eventTime, MotionEvent.ACTION_UP,
1634                 1, new PointerProperties[] { pp0 }, new PointerCoords[] { pc0 },
1635                 0, 0, 1.0f, 1.0f, 0, 0, InputDevice.SOURCE_CLASS_POINTER, 0);
1636         mGestureHandler.onTouchEvent(event);
1637         assertEquals(GestureEventType.SCROLL_END,
1638                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
1639         assertEquals("Only GESTURE_SCROLL_END should have been sent",
1640                 8, mMockMotionEventDelegate.mGestureTypeList.size());
1641     }
1642
1643     /**
1644      * Verify that the timer of LONG_PRESS will be cancelled when scrolling begins so
1645      * LONG_PRESS and LONG_TAP won't be triggered.
1646      *
1647      * @throws Exception
1648      */
1649     @SmallTest
1650     @Feature({"Gestures"})
1651     public void testGesturesCancelledAfterLongPressCausesLostFocus() throws Exception {
1652         final long downTime = SystemClock.uptimeMillis();
1653         final long eventTime = SystemClock.uptimeMillis();
1654
1655         getInstrumentation().runOnMainSync(new Runnable() {
1656             @Override
1657             public void run() {
1658                 setUp();
1659                 mGestureHandler = new ContentViewGestureHandler(
1660                         getInstrumentation().getTargetContext(), mMockMotionEventDelegate);
1661                 MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
1662                 assertTrue(mGestureHandler.onTouchEvent(event));
1663             }
1664         });
1665
1666         final long longPressTimeoutMs = ViewConfiguration.getLongPressTimeout()
1667                 + ViewConfiguration.getTapTimeout() + 10;
1668         assertTrue(mMockMotionEventDelegate.mLongPressCalled.await(
1669                 ScalableTimeout.scaleTimeout(longPressTimeoutMs), TimeUnit.MILLISECONDS));
1670
1671         assertEquals("A LONG_PRESS gesture should have been sent",
1672                 GestureEventType.LONG_PRESS,
1673                         mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
1674
1675         mGestureHandler.onWindowFocusLost();
1676
1677         assertEquals("The LONG_PRESS should have been cancelled by loss of focus",
1678                 GestureEventType.TAP_CANCEL,
1679                         mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
1680
1681         MotionEvent event = motionEvent(
1682                 MotionEvent.ACTION_UP, downTime, eventTime + longPressTimeoutMs);
1683         mGestureHandler.onTouchEvent(event);
1684         assertFalse("Tap cancellation should have prevented the LONG_TAP",
1685                 mMockMotionEventDelegate.mGestureTypeList.contains(
1686                         GestureEventType.LONG_TAP));
1687     }
1688
1689     /**
1690      * Verify that ignoring the remaining touch sequence triggers proper touch and gesture
1691      * cancellation.
1692      *
1693      * @throws Exception
1694      */
1695     @SmallTest
1696     @Feature({"Gestures"})
1697     public void testSetIgnoreRemainingTouchEvents() throws Exception {
1698         final long downTime = SystemClock.uptimeMillis();
1699         final long eventTime = SystemClock.uptimeMillis();
1700
1701         mGestureHandler.setIgnoreRemainingTouchEvents();
1702         assertTrue("If there was no active touch sequence, ignoring it should be a no-op",
1703                 mMockMotionEventDelegate.mGestureTypeList.isEmpty());
1704         assertEquals("No MotionEvents should have been generated",
1705                 -1, mMockMotionEventDelegate.mMostRecentMotionEventAction);
1706
1707         MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
1708         assertTrue(mGestureHandler.onTouchEvent(event));
1709         assertEquals(GestureEventType.TAP_DOWN,
1710                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
1711
1712         mGestureHandler.setIgnoreRemainingTouchEvents();
1713         assertEquals("The TAP_DOWN should have been cancelled",
1714                 GestureEventType.TAP_CANCEL,
1715                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
1716         assertEquals("An ACTION_CANCEL MotionEvent should have been inserted",
1717                 MotionEvent.ACTION_CANCEL, mMockMotionEventDelegate.mMostRecentMotionEventAction);
1718
1719         // Subsequent MotionEvent's are dropped until ACTION_DOWN.
1720         event = motionEvent(MotionEvent.ACTION_MOVE, downTime, eventTime + 5);
1721         assertFalse(mGestureHandler.onTouchEvent(event));
1722
1723         event = motionEvent(MotionEvent.ACTION_UP, downTime, eventTime + 10);
1724         assertFalse(mGestureHandler.onTouchEvent(event));
1725
1726         event = motionEvent(MotionEvent.ACTION_DOWN, downTime + 15, downTime + 15);
1727         assertTrue(mGestureHandler.onTouchEvent(event));
1728         assertEquals(GestureEventType.TAP_DOWN,
1729                 mMockMotionEventDelegate.mMostRecentGestureEvent.mType);
1730     }
1731 }