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