/**
   * Verify that LONG_TAP is triggered after LongPress followed by an UP.
   *
   * @throws Exception
   */
  @SmallTest
  @Feature({"Gestures"})
  public void testGestureLongTap() throws Exception {
    final long downTime = SystemClock.uptimeMillis();
    final long eventTime = SystemClock.uptimeMillis();

    GestureRecordingMotionEventDelegate mockDelegate = new GestureRecordingMotionEventDelegate();
    mGestureHandler =
        new ContentViewGestureHandler(
            getInstrumentation().getTargetContext(),
            mockDelegate,
            new MockZoomManager(getInstrumentation().getTargetContext(), null));
    mLongPressDetector = mGestureHandler.getLongPressDetector();

    MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
    assertTrue(mGestureHandler.onTouchEvent(event));
    assertNotNull(mockDelegate.getMostRecentGestureEvent());
    assertTrue("Should have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());

    mLongPressDetector.sendLongPressGestureForTest();

    assertEquals(
        "A LONG_PRESS gesture should have been sent",
        ContentViewGestureHandler.GESTURE_LONG_PRESS,
        mockDelegate.mMostRecentGestureEvent.mType);

    event = motionEvent(MotionEvent.ACTION_UP, downTime, eventTime + 1000);
    assertTrue(mGestureHandler.onTouchEvent(event));
    assertEquals(
        "A LONG_TAP gesture should have been sent",
        ContentViewGestureHandler.GESTURE_LONG_TAP,
        mockDelegate.mMostRecentGestureEvent.mType);
  }
  /**
   * Verify that the timer of LONG_PRESS will be cancelled when scrolling begins so LONG_PRESS and
   * LONG_TAP won't be triggered.
   *
   * @throws Exception
   */
  @SmallTest
  @Feature({"Gestures"})
  public void testLongPressAndTapCancelWhenScrollBegins() throws Exception {
    final long downTime = SystemClock.uptimeMillis();
    final long eventTime = SystemClock.uptimeMillis();

    GestureRecordingMotionEventDelegate mockDelegate = new GestureRecordingMotionEventDelegate();
    mGestureHandler =
        new ContentViewGestureHandler(
            getInstrumentation().getTargetContext(),
            mockDelegate,
            new MockZoomManager(getInstrumentation().getTargetContext(), null));
    mLongPressDetector = mGestureHandler.getLongPressDetector();

    MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
    assertTrue(mGestureHandler.onTouchEvent(event));
    assertNotNull(mockDelegate.getMostRecentGestureEvent());
    assertTrue("Should have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
    event =
        MotionEvent.obtain(
            downTime,
            eventTime + 5,
            MotionEvent.ACTION_MOVE,
            FAKE_COORD_X * 5,
            FAKE_COORD_Y * 5,
            0);
    assertTrue(mGestureHandler.onTouchEvent(event));
    event =
        MotionEvent.obtain(
            downTime,
            eventTime + 10,
            MotionEvent.ACTION_MOVE,
            FAKE_COORD_X * 10,
            FAKE_COORD_Y * 10,
            0);
    assertTrue(mGestureHandler.onTouchEvent(event));
    assertTrue("Should not have a pending LONG_PRESS", !mLongPressDetector.hasPendingMessage());

    // No LONG_TAP because LONG_PRESS timer is cancelled.
    assertFalse(
        "No LONG_PRESS should be sent",
        mockDelegate.mGestureTypeList.contains(ContentViewGestureHandler.GESTURE_LONG_PRESS));
    assertFalse(
        "No LONG_TAP should be sent",
        mockDelegate.mGestureTypeList.contains(ContentViewGestureHandler.GESTURE_LONG_TAP));
  }
  /**
   * Verify that a DOWN followed shortly by an UP will trigger a single tap.
   *
   * @throws Exception
   */
  @SmallTest
  @Feature({"Gestures"})
  public void testGestureSingleClick() throws Exception {
    final long downTime = SystemClock.uptimeMillis();
    final long eventTime = SystemClock.uptimeMillis();

    MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);

    assertFalse(mGestureHandler.onTouchEvent(event));
    assertTrue("Should have a pending gesture", mMockGestureDetector.mLastEvent != null);
    assertTrue("Should have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());

    event = motionEvent(MotionEvent.ACTION_UP, downTime, eventTime + 10);
    mLongPressDetector.cancelLongPressIfNeeded(event);
    assertTrue("Should not have a pending LONG_PRESS", !mLongPressDetector.hasPendingMessage());
    assertTrue(mGestureHandler.onTouchEvent(event));
    // Synchronous, no need to wait.
    assertTrue("Should have a single tap", mMockListener.mLastSingleTap != null);
  }
  /**
   * Verify that a recent show pressed state gesture is canceled when double tap occurs.
   *
   * @throws Exception
   */
  @SmallTest
  @Feature({"Gestures"})
  public void testShowPressCancelOnDoubleTap() throws Exception {
    final long downTime = SystemClock.uptimeMillis();
    final long eventTime = SystemClock.uptimeMillis();

    GestureRecordingMotionEventDelegate mockDelegate = new GestureRecordingMotionEventDelegate();
    mGestureHandler =
        new ContentViewGestureHandler(
            getInstrumentation().getTargetContext(),
            mockDelegate,
            new MockZoomManager(getInstrumentation().getTargetContext(), null));
    mLongPressDetector =
        new LongPressDetector(getInstrumentation().getTargetContext(), mGestureHandler);

    MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);

    assertTrue(mGestureHandler.onTouchEvent(event));
    assertFalse("Should not have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());

    mGestureHandler.sendShowPressedStateGestureForTesting();

    assertEquals(
        "A show pressed state event should have been sent",
        ContentViewGestureHandler.GESTURE_SHOW_PRESSED_STATE,
        mockDelegate.mMostRecentGestureEvent.mType);
    assertEquals(
        "Only flingCancel and showPressedState should have been sent",
        2,
        mockDelegate.mGestureTypeList.size());

    event =
        MotionEvent.obtain(
            downTime, eventTime + 5, MotionEvent.ACTION_UP, FAKE_COORD_X, FAKE_COORD_Y, 0);
    mGestureHandler.onTouchEvent(event);
    assertEquals("The first tap should not do anything", 2, mockDelegate.mGestureTypeList.size());

    event =
        MotionEvent.obtain(
            eventTime + 10, eventTime + 10, MotionEvent.ACTION_DOWN, FAKE_COORD_X, FAKE_COORD_Y, 0);
    assertTrue(mGestureHandler.onTouchEvent(event));
    assertEquals(
        "A double tap should have occurred",
        ContentViewGestureHandler.GESTURE_DOUBLE_TAP,
        mockDelegate.mMostRecentGestureEvent.mType);
    assertTrue(
        "A show press cancel event should have been sent",
        mockDelegate.mGestureTypeList.contains(
            ContentViewGestureHandler.GESTURE_SHOW_PRESS_CANCEL));
    assertEquals(
        "Only flingCancel, showPressedState,"
            + "flingCancel, showPressCancel and doubleTap should have been sent",
        5,
        mockDelegate.mGestureTypeList.size());
  }
  /**
   * Verifies that a single tap doesn't cause a long press event to be sent.
   *
   * @throws Exception
   */
  @SmallTest
  @Feature({"Gestures"})
  public void testNoLongPressIsSentForSingleTapOutOfTouchHandler() throws Exception {
    final long downTime = SystemClock.uptimeMillis();
    final long eventTime = SystemClock.uptimeMillis();

    MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);

    mGestureHandler.hasTouchEventHandlers(true);

    assertTrue(mGestureHandler.onTouchEvent(event));
    assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
    assertTrue("Should not have a pending gesture", mMockGestureDetector.mLastEvent == null);
    assertFalse("Should not have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());

    event = motionEvent(MotionEvent.ACTION_UP, downTime, eventTime + 5);
    assertTrue(mGestureHandler.onTouchEvent(event));
    assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());

    mGestureHandler.confirmTouchEvent(ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NOT_CONSUMED);

    assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
    assertEquals(
        "The down touch event should have been sent to the Gesture Detector",
        event.getDownTime(),
        mMockGestureDetector.mLastEvent.getEventTime());
    assertEquals(
        "The next event should be ACTION_UP",
        MotionEvent.ACTION_UP,
        mGestureHandler.peekFirstInPendingMotionEventsForTesting().getActionMasked());

    mGestureHandler.confirmTouchEvent(ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NOT_CONSUMED);

    assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
    assertEquals(
        "The up touch event should have been sent to the Gesture Detector",
        event.getEventTime(),
        mMockGestureDetector.mLastEvent.getEventTime());

    assertFalse("Should not have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
  }
  /**
   * Verify that a DOWN followed by a MOVE will trigger fling (but not LONG).
   *
   * @throws Exception
   */
  @SmallTest
  @Feature({"Gestures"})
  public void testGestureFlingAndCancelLongClick() throws Exception {
    final long downTime = SystemClock.uptimeMillis();
    final long eventTime = SystemClock.uptimeMillis();

    MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);

    assertFalse(mGestureHandler.onTouchEvent(event));
    assertTrue("Should have a pending gesture", mMockGestureDetector.mLastEvent != null);
    assertTrue("Should have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());

    event =
        MotionEvent.obtain(
            downTime,
            eventTime + 5,
            MotionEvent.ACTION_MOVE,
            FAKE_COORD_X * 10,
            FAKE_COORD_Y * 10,
            0);
    mLongPressDetector.cancelLongPressIfNeeded(event);
    assertTrue("Should not have a pending LONG_PRESS", !mLongPressDetector.hasPendingMessage());
    assertTrue(mGestureHandler.onTouchEvent(event));

    event =
        MotionEvent.obtain(
            downTime,
            eventTime + 10,
            MotionEvent.ACTION_UP,
            FAKE_COORD_X * 10,
            FAKE_COORD_Y * 10,
            0);
    assertTrue(mGestureHandler.onTouchEvent(event));

    // Synchronous, no need to wait.
    assertTrue("Should have a fling", mMockListener.mLastFling1 != null);
    assertTrue("Should not have a long press", mMockListener.mLastLongPress == null);
  }
  /**
   * Verify that a recent show pressed state gesture is canceled when scrolling begins.
   *
   * @throws Exception
   */
  @SmallTest
  @Feature({"Gestures"})
  public void testShowPressCancelWhenScrollBegins() throws Exception {
    final long downTime = SystemClock.uptimeMillis();
    final long eventTime = SystemClock.uptimeMillis();

    GestureRecordingMotionEventDelegate mockDelegate = new GestureRecordingMotionEventDelegate();
    mGestureHandler =
        new ContentViewGestureHandler(
            getInstrumentation().getTargetContext(),
            mockDelegate,
            new MockZoomManager(getInstrumentation().getTargetContext(), null));
    mLongPressDetector =
        new LongPressDetector(getInstrumentation().getTargetContext(), mGestureHandler);

    MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);

    assertTrue(mGestureHandler.onTouchEvent(event));
    assertFalse("Should not have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());

    mGestureHandler.sendShowPressedStateGestureForTesting();

    assertEquals(
        "A show pressed state event should have been sent",
        ContentViewGestureHandler.GESTURE_SHOW_PRESSED_STATE,
        mockDelegate.mMostRecentGestureEvent.mType);
    assertEquals(
        "Only flingCancel and showPressedState should have been sent",
        2,
        mockDelegate.mGestureTypeList.size());

    event =
        MotionEvent.obtain(
            downTime,
            eventTime + 10,
            MotionEvent.ACTION_MOVE,
            FAKE_COORD_X * 5,
            FAKE_COORD_Y * 5,
            0);
    assertTrue(mGestureHandler.onTouchEvent(event));

    assertEquals(
        "We should have started scrolling",
        ContentViewGestureHandler.GESTURE_SCROLL_BY,
        mockDelegate.mMostRecentGestureEvent.mType);
    assertTrue(
        "A show press cancel event should have been sent",
        mockDelegate.mGestureTypeList.contains(
            ContentViewGestureHandler.GESTURE_SHOW_PRESS_CANCEL));
    assertEquals(
        "Only flingCancel, showPressedState,"
            + "showPressCancel, scrollBegin and scrollBy should have been sent",
        5,
        mockDelegate.mGestureTypeList.size());

    event =
        MotionEvent.obtain(
            downTime,
            eventTime + 15,
            MotionEvent.ACTION_UP,
            FAKE_COORD_X * 10,
            FAKE_COORD_Y * 10,
            0);
    assertTrue(mGestureHandler.onTouchEvent(event));
    assertEquals(
        "We should have started flinging",
        ContentViewGestureHandler.GESTURE_FLING_START,
        mockDelegate.mMostRecentGestureEvent.mType);
    assertTrue(
        "A scroll end event should not have been sent",
        !mockDelegate.mGestureTypeList.contains(ContentViewGestureHandler.GESTURE_SCROLL_END));
    assertEquals(
        "The last up should have caused flingCancel and flingStart to be sent",
        7,
        mockDelegate.mGestureTypeList.size());
  }
  /**
   * Verify that when a touch event handler is registered the touch events stop getting queued after
   * we received INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS.
   *
   * @throws Exception
   */
  @SmallTest
  @Feature({"Gestures"})
  public void testFlingOutOfTouchHandler() throws Exception {
    final long downTime = SystemClock.uptimeMillis();
    final long eventTime = SystemClock.uptimeMillis();

    MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);

    mGestureHandler.hasTouchEventHandlers(true);

    assertTrue(mGestureHandler.onTouchEvent(event));
    assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
    assertTrue("Should not have a pending gesture", mMockGestureDetector.mLastEvent == null);
    assertFalse("Should not have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());

    mGestureHandler.confirmTouchEvent(
        ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
    assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
    assertEquals(
        "The down touch event should have been sent to the Gesture Detector",
        event.getEventTime(),
        mMockGestureDetector.mLastEvent.getEventTime());
    assertEquals(
        "The down touch event should have been sent to the Gesture Detector",
        MotionEvent.ACTION_DOWN,
        mMockGestureDetector.mLastEvent.getActionMasked());

    event =
        MotionEvent.obtain(
            downTime,
            eventTime + 5,
            MotionEvent.ACTION_MOVE,
            FAKE_COORD_X * 5,
            FAKE_COORD_Y * 5,
            0);
    assertTrue(mGestureHandler.onTouchEvent(event));
    assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
    assertEquals(
        "Motion events should be going to the Gesture Detector directly",
        event.getEventTime(),
        mMockGestureDetector.mLastEvent.getEventTime());
    assertEquals(
        "Motion events should be going to the Gesture Detector directly",
        MotionEvent.ACTION_MOVE,
        mMockGestureDetector.mLastEvent.getActionMasked());

    event =
        MotionEvent.obtain(
            downTime, eventTime + 10, MotionEvent.ACTION_UP, FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
    assertTrue(mGestureHandler.onTouchEvent(event));
    assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
    assertEquals(
        "Motion events should be going to the Gesture Detector directly",
        event.getEventTime(),
        mMockGestureDetector.mLastEvent.getEventTime());
    assertEquals(
        "Motion events should be going to the Gesture Detector directly",
        MotionEvent.ACTION_UP,
        mMockGestureDetector.mLastEvent.getActionMasked());

    // Synchronous, no need to wait.
    assertTrue("Should have a fling", mMockListener.mLastFling1 != null);
    assertTrue("Should not have a long press", mMockListener.mLastLongPress == null);
  }
  /**
   * Verify that when a registered touch event handler return NO_CONSUMER_EXISTS for down event all
   * queue is drained until next down.
   *
   * @throws Exception
   */
  @SmallTest
  @Feature({"Gestures"})
  public void testDrainWithFlingAndClickOutofTouchHandler() throws Exception {
    final long downTime = SystemClock.uptimeMillis();
    final long eventTime = SystemClock.uptimeMillis();

    mGestureHandler =
        new ContentViewGestureHandler(
            getInstrumentation().getTargetContext(),
            new MockMotionEventDelegate(),
            new MockZoomManager(getInstrumentation().getTargetContext(), null));
    mLongPressDetector =
        new LongPressDetector(getInstrumentation().getTargetContext(), mGestureHandler);

    MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);

    mGestureHandler.hasTouchEventHandlers(true);

    assertTrue(mGestureHandler.onTouchEvent(event));
    assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
    assertFalse("Should not have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());

    event =
        MotionEvent.obtain(
            downTime,
            eventTime + 5,
            MotionEvent.ACTION_MOVE,
            FAKE_COORD_X * 5,
            FAKE_COORD_Y * 5,
            0);
    assertTrue(mGestureHandler.onTouchEvent(event));
    assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());

    event =
        MotionEvent.obtain(
            downTime,
            eventTime + 10,
            MotionEvent.ACTION_MOVE,
            FAKE_COORD_X * 10,
            FAKE_COORD_Y * 10,
            0);
    assertTrue(mGestureHandler.onTouchEvent(event));
    assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());

    event =
        MotionEvent.obtain(
            downTime,
            eventTime + 15,
            MotionEvent.ACTION_UP,
            FAKE_COORD_X * 10,
            FAKE_COORD_Y * 10,
            0);
    assertTrue(mGestureHandler.onTouchEvent(event));
    assertEquals(3, mGestureHandler.getNumberOfPendingMotionEventsForTesting());

    event = motionEvent(MotionEvent.ACTION_DOWN, eventTime + 20, eventTime + 20);
    assertTrue(mGestureHandler.onTouchEvent(event));
    assertEquals(4, mGestureHandler.getNumberOfPendingMotionEventsForTesting());

    event =
        MotionEvent.obtain(
            downTime, eventTime + 20, MotionEvent.ACTION_UP, FAKE_COORD_X, FAKE_COORD_Y, 0);
    assertTrue(mGestureHandler.onTouchEvent(event));
    assertEquals(5, mGestureHandler.getNumberOfPendingMotionEventsForTesting());

    mGestureHandler.confirmTouchEvent(
        ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
    assertEquals(
        "The queue should have been drained until first down since no consumer exists",
        2,
        mGestureHandler.getNumberOfPendingMotionEventsForTesting());
    assertEquals(
        MotionEvent.ACTION_DOWN,
        mGestureHandler.peekFirstInPendingMotionEventsForTesting().getActionMasked());

    mGestureHandler.confirmTouchEvent(
        ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
    assertEquals(
        "The queue should have been drained",
        0,
        mGestureHandler.getNumberOfPendingMotionEventsForTesting());
  }
  /**
   * Verify that after a touch event handlers starts handling a gesture, even though some event in
   * the middle of the gesture returns with NOT_CONSUMED, we don't send that to the gesture detector
   * to avoid falling to a faulty state.
   *
   * @throws Exception
   */
  @SmallTest
  @Feature({"Gestures"})
  public void testFlingOnTouchHandlerWithOneEventNotConsumed() throws Exception {
    final long downTime = SystemClock.uptimeMillis();
    final long eventTime = SystemClock.uptimeMillis();

    MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);

    mGestureHandler.hasTouchEventHandlers(true);

    assertTrue(mGestureHandler.onTouchEvent(event));
    assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
    assertTrue("Should not have a pending gesture", mMockGestureDetector.mLastEvent == null);
    assertFalse("Should not have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());

    event =
        MotionEvent.obtain(
            downTime,
            eventTime + 5,
            MotionEvent.ACTION_MOVE,
            FAKE_COORD_X * 5,
            FAKE_COORD_Y * 5,
            0);
    assertTrue(mGestureHandler.onTouchEvent(event));
    assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());

    event =
        MotionEvent.obtain(
            downTime,
            eventTime + 10,
            MotionEvent.ACTION_MOVE,
            FAKE_COORD_X * 10,
            FAKE_COORD_Y * 10,
            0);
    assertTrue(mGestureHandler.onTouchEvent(event));
    assertEquals(
        "We should have coalesced move events into one",
        2,
        mGestureHandler.getNumberOfPendingMotionEventsForTesting());

    event =
        MotionEvent.obtain(
            downTime,
            eventTime + 15,
            MotionEvent.ACTION_UP,
            FAKE_COORD_X * 10,
            FAKE_COORD_Y * 10,
            0);
    assertTrue(mGestureHandler.onTouchEvent(event));
    assertEquals(3, mGestureHandler.getNumberOfPendingMotionEventsForTesting());

    mGestureHandler.confirmTouchEvent(ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_CONSUMED);
    assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
    assertEquals(
        MotionEvent.ACTION_MOVE,
        mGestureHandler.peekFirstInPendingMotionEventsForTesting().getActionMasked());

    mGestureHandler.confirmTouchEvent(ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
    assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
    assertEquals(
        MotionEvent.ACTION_UP,
        mGestureHandler.peekFirstInPendingMotionEventsForTesting().getActionMasked());
    assertTrue(
        "Even though the last event was not consumed by JavaScript,"
            + "it shouldn't have been sent to the Gesture Detector",
        mMockGestureDetector.mLastEvent == null);

    mGestureHandler.confirmTouchEvent(ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_CONSUMED);
    assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());

    // Synchronous, no need to wait.
    assertTrue("Should not have a fling", mMockListener.mLastFling1 == null);
    assertTrue("Should not have a long press", mMockListener.mLastLongPress == null);
  }