blob: ba6617175fd14ad6e841453432ccb7a9e3190612 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.content.browser;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.os.SystemClock;
import android.test.InstrumentationTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
import android.view.MotionEvent;
import android.view.MotionEvent.PointerCoords;
import android.view.MotionEvent.PointerProperties;
import android.view.ViewConfiguration;
import org.chromium.base.test.util.Feature;
import org.chromium.content.browser.ContentViewGestureHandler.MotionEventDelegate;
import org.chromium.content.browser.third_party.GestureDetector;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
/**
* Test suite for ContentViewGestureHandler.
*/
public class ContentViewGestureHandlerTest extends InstrumentationTestCase {
private static final int FAKE_COORD_X = 42;
private static final int FAKE_COORD_Y = 24;
private static final String TAG = "ContentViewGestureHandler";
private MockListener mMockListener;
private MockMotionEventDelegate mMockMotionEventDelegate;
private MockGestureDetector mMockGestureDetector;
private ContentViewGestureHandler mGestureHandler;
private LongPressDetector mLongPressDetector;
static class MockListener extends GestureDetector.SimpleOnGestureListener {
MotionEvent mLastLongPress;
MotionEvent mLastSingleTap;
MotionEvent mLastFling1;
CountDownLatch mLongPressCalled;
public MockListener() {
mLongPressCalled = new CountDownLatch(1);
}
@Override
public void onLongPress(MotionEvent e) {
mLastLongPress = MotionEvent.obtain(e);
mLongPressCalled.countDown();
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
mLastSingleTap = e;
return true;
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
mLastSingleTap = e;
return true;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return true;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
mLastFling1 = e1;
return true;
}
}
static class MockGestureDetector extends GestureDetector {
MotionEvent mLastEvent;
public MockGestureDetector(Context context, OnGestureListener listener) {
super(context, listener);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
mLastEvent = MotionEvent.obtain(ev);
return super.onTouchEvent(ev);
}
}
static class MockMotionEventDelegate implements MotionEventDelegate {
private ContentViewGestureHandler mSynchronousConfirmTarget;
private int mSynchronousConfirmAckResult;
public int mLastTouchAction;
public int mLastGestureType;
@Override
public boolean sendTouchEvent(long timeMs, int action, TouchPoint[] pts) {
mLastTouchAction = action;
if (mSynchronousConfirmTarget != null) {
mSynchronousConfirmTarget.confirmTouchEvent(mSynchronousConfirmAckResult);
}
return true;
}
@Override
public boolean sendGesture(int type, long timeMs, int x, int y,
boolean lastInputEventForVSync, Bundle extraParams) {
Log.i(TAG,"Gesture event received with type id " + type);
mLastGestureType = type;
return true;
}
@Override
public boolean didUIStealScroll(float x, float y) {
// Not implemented.
return false;
}
@Override
public void invokeZoomPicker() {
// Not implemented.
}
@Override
public boolean hasFixedPageScale() {
return false;
}
public void enableSynchronousConfirmTouchEvent(
ContentViewGestureHandler handler, int ackResult) {
mSynchronousConfirmTarget = handler;
mSynchronousConfirmAckResult = ackResult;
}
public void disableSynchronousConfirmTouchEvent() {
mSynchronousConfirmTarget = null;
}
}
static class MockZoomManager extends ZoomManager {
MockZoomManager(Context context, ContentViewCore contentViewCore) {
super(context, contentViewCore);
}
@Override
public boolean processTouchEvent(MotionEvent event) {
return false;
}
}
static private MotionEvent motionEvent(int action, long downTime, long eventTime) {
return MotionEvent.obtain(downTime, eventTime, action, FAKE_COORD_X, FAKE_COORD_Y, 0);
}
@Override
public void setUp() {
mMockListener = new MockListener();
mMockGestureDetector = new MockGestureDetector(
getInstrumentation().getTargetContext(), mMockListener);
mMockMotionEventDelegate = new MockMotionEventDelegate();
mGestureHandler = new ContentViewGestureHandler(
getInstrumentation().getTargetContext(), mMockMotionEventDelegate,
new MockZoomManager(getInstrumentation().getTargetContext(), null),
ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC);
mLongPressDetector = new LongPressDetector(
getInstrumentation().getTargetContext(), mGestureHandler);
mGestureHandler.setTestDependencies(
mLongPressDetector, mMockGestureDetector, mMockListener);
TouchPoint.initializeConstantsForTesting();
}
/**
* 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 when a touch event handler is registered the touch events are queued
* and sent in order.
* @throws Exception
*/
@SmallTest
@Feature({"Gestures"})
public void testFlingOnTouchHandler() 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_NOT_CONSUMED);
assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
assertEquals(MotionEvent.ACTION_MOVE,
mGestureHandler.peekFirstInPendingMotionEventsForTesting().getActionMasked());
assertFalse("Pending LONG_PRESS should have been canceled",
mLongPressDetector.hasPendingMessage());
mGestureHandler.confirmTouchEvent(ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_CONSUMED);
assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
assertEquals(MotionEvent.ACTION_UP,
mGestureHandler.peekFirstInPendingMotionEventsForTesting().getActionMasked());
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);
}
/**
* Verify the behavior of touch event timeout handler.
* @throws Exception
*/
@SmallTest
@Feature({"Gestures"})
public void testTouchEventTimeoutHandler() throws Exception {
final long downTime = SystemClock.uptimeMillis();
final long eventTime = SystemClock.uptimeMillis();
mGestureHandler.hasTouchEventHandlers(true);
MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
assertTrue(mGestureHandler.onTouchEvent(event));
assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
// Queue a touch move event.
event = MotionEvent.obtain(
downTime, eventTime + 50, MotionEvent.ACTION_MOVE,
FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
assertTrue(mGestureHandler.onTouchEvent(event));
assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
mGestureHandler.mockTouchEventTimeout();
// On timeout, the pending queue should be cleared.
assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
// No new touch events should be sent to the touch handler before the timed-out event
// gets ACK'ed.
event = MotionEvent.obtain(
downTime, eventTime + 200, MotionEvent.ACTION_MOVE,
FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
mGestureHandler.onTouchEvent(event);
assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
// When the timed-out event gets ACK'ed, a cancel event should be sent.
mGestureHandler.confirmTouchEvent(ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_CONSUMED);
assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
assertEquals(TouchPoint.TOUCH_EVENT_TYPE_CANCEL, mMockMotionEventDelegate.mLastTouchAction);
// No new touch events should be sent to the touch handler before the cancel event
// gets ACK'ed.
event = MotionEvent.obtain(
downTime, eventTime + 300, MotionEvent.ACTION_UP,
FAKE_COORD_X * 20, FAKE_COORD_Y * 20, 0);
mGestureHandler.onTouchEvent(event);
assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
mGestureHandler.confirmTouchEvent(
ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
// After cancel event is ACK'ed, the handler should return to normal state.
event = MotionEvent.obtain(
downTime + 400, eventTime + 400, MotionEvent.ACTION_DOWN,
FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
assertTrue(mGestureHandler.onTouchEvent(event));
assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
mGestureHandler.confirmTouchEvent(ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_CONSUMED);
assertEquals(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);
}
/**
* 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),
ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC);
mLongPressDetector = new LongPressDetector(
getInstrumentation().getTargetContext(), mGestureHandler);
mGestureHandler.hasTouchEventHandlers(true);
MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
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 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);
}
/**
* 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 for a normal scroll the following events are sent:
* - GESTURE_SCROLL_START
* - GESTURE_SCROLL_BY
* - GESTURE_SCROLL_END
* @throws Exception
*/
@SmallTest
@Feature({"Gestures"})
public void testScrollEventActionUpSequence() throws Exception {
checkScrollEventSequenceForEndActionType(MotionEvent.ACTION_UP);
}
/**
* Verify that for a cancelled scroll the following events are sent:
* - GESTURE_SCROLL_START
* - GESTURE_SCROLL_BY
* - GESTURE_SCROLL_END
* @throws Exception
*/
@SmallTest
@Feature({"Gestures"})
public void testScrollEventActionCancelSequence() throws Exception {
checkScrollEventSequenceForEndActionType(MotionEvent.ACTION_CANCEL);
}
private void checkScrollEventSequenceForEndActionType(int endActionType) throws Exception {
final long downTime = SystemClock.uptimeMillis();
final long eventTime = SystemClock.uptimeMillis();
final int scrollToX = FAKE_COORD_X + 100;
final int scrollToY = FAKE_COORD_Y + 100;
GestureRecordingMotionEventDelegate mockDelegate =
new GestureRecordingMotionEventDelegate();
mGestureHandler = new ContentViewGestureHandler(
getInstrumentation().getTargetContext(), mockDelegate, mMockZoomManager,
ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC);
MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
assertTrue(mGestureHandler.onTouchEvent(event));
event = MotionEvent.obtain(
downTime, eventTime + 1000, MotionEvent.ACTION_MOVE, scrollToX, scrollToY, 0);
assertTrue(mGestureHandler.onTouchEvent(event));
assertTrue(mGestureHandler.isNativeScrolling());
assertTrue("A scrollStart event should have been sent",
mockDelegate.mGestureTypeList.contains(
ContentViewGestureHandler.GESTURE_SCROLL_START));
assertEquals("We should have started scrolling",
ContentViewGestureHandler.GESTURE_SCROLL_BY,
mockDelegate.mMostRecentGestureEvent.mType);
assertEquals("Only scrollBegin and scrollBy should have been sent",
2, mockDelegate.mGestureTypeList.size());
event = MotionEvent.obtain(
downTime, eventTime + 1000, endActionType, scrollToX, scrollToY, 0);
mGestureHandler.onTouchEvent(event);
assertFalse(mGestureHandler.isNativeScrolling());
assertTrue("A scrollEnd event should have been sent",
mockDelegate.mGestureTypeList.contains(
ContentViewGestureHandler.GESTURE_SCROLL_END));
assertEquals("We should have stopped scrolling",
ContentViewGestureHandler.GESTURE_SCROLL_END,
mockDelegate.mMostRecentGestureEvent.mType);
assertEquals("Only scrollBegin and scrollBy and scrollEnd should have been sent",
3, mockDelegate.mGestureTypeList.size());
}
/**
* Verify that for a normal fling (fling after scroll) the following events are sent:
* - GESTURE_SCROLL_BEGIN
* - GESTURE_FLING_START
* and GESTURE_FLING_CANCEL is sent on the next touch.
* @throws Exception
*/
@SmallTest
@Feature({"Gestures"})
public void testFlingEventSequence() 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),
ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC);
MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
assertTrue(mGestureHandler.onTouchEvent(event));
event = MotionEvent.obtain(
downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
assertTrue(mGestureHandler.onTouchEvent(event));
assertTrue(mGestureHandler.isNativeScrolling());
assertTrue("A scrollStart event should have been sent",
mockDelegate.mGestureTypeList.contains(
ContentViewGestureHandler.GESTURE_SCROLL_START));
assertEquals("We should have started scrolling",
ContentViewGestureHandler.GESTURE_SCROLL_BY,
mockDelegate.mMostRecentGestureEvent.mType);
assertEquals("Only scrollBegin and scrollBy should have been sent",
2, 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));
assertFalse(mGestureHandler.isNativeScrolling());
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 flingStart to be sent",
3, mockDelegate.mGestureTypeList.size());
event = motionEvent(MotionEvent.ACTION_DOWN, downTime + 50, downTime + 50);
assertTrue(mGestureHandler.onTouchEvent(event));
assertTrue("A flingCancel should have been sent",
mockDelegate.mGestureTypeList.contains(
ContentViewGestureHandler.GESTURE_FLING_CANCEL));
assertEquals("Only flingCancel should have been sent",
4, mockDelegate.mGestureTypeList.size());
}
/**
* Verify that a show pressed state gesture followed by a long press followed by
* the focus
* loss in the window due to context menu cancels show pressed.
* @throws Exception
*/
@SmallTest
@Feature({"Gestures"})
public void testShowPressCancelOnWindowFocusLost() throws Exception {
final long time = SystemClock.uptimeMillis();
GestureRecordingMotionEventDelegate mockDelegate =
new GestureRecordingMotionEventDelegate();
mGestureHandler = new ContentViewGestureHandler(
getInstrumentation().getTargetContext(), mockDelegate,
new MockZoomManager(getInstrumentation().getTargetContext(), null),
ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC);
mLongPressDetector = new LongPressDetector(
getInstrumentation().getTargetContext(), mGestureHandler);
mGestureHandler.setTestDependencies(mLongPressDetector, null, null);
MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, time, time);
mGestureHandler.onTouchEvent(event);
mGestureHandler.sendShowPressedStateGestureForTesting();
assertEquals("A show pressed state event should have been sent",
ContentViewGestureHandler.GESTURE_SHOW_PRESSED_STATE,
mockDelegate.mMostRecentGestureEvent.mType);
assertEquals("Only showPressedState should have been sent",
1, mockDelegate.mGestureTypeList.size());
mLongPressDetector.startLongPressTimerIfNeeded(event);
mLongPressDetector.sendLongPressGestureForTest();
assertEquals("Only should have sent only LONG_PRESS event",
2, mockDelegate.mGestureTypeList.size());
assertEquals("Should have a long press event next",
ContentViewGestureHandler.GESTURE_LONG_PRESS,
mockDelegate.mGestureTypeList.get(1).intValue());
// The long press triggers window focus loss by opening a context menu
mGestureHandler.onWindowFocusLost();
assertEquals("Only should have sent only GESTURE_SHOW_PRESS_CANCEL event",
3, mockDelegate.mGestureTypeList.size());
assertEquals("Should have a long press event next",
ContentViewGestureHandler.GESTURE_SHOW_PRESS_CANCEL,
mockDelegate.mGestureTypeList.get(2).intValue());
}
/**
* 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),
ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC);
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 showPressedState should have been sent",
1, 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 showPressedState, showPressCancel, scrollBegin and scrollBy" +
" should have been sent",
4, 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 flingStart to be sent",
5, mockDelegate.mGestureTypeList.size());
}
/**
* Verify that double tap is correctly handled including the recent show pressed state gesture
* cancellation.
* @throws Exception
*/
@SmallTest
@Feature({"Gestures"})
public void testDoubleTap() 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),
ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC);
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("GESTURE_SHOW_PRESSED_STATE should have been sent",
ContentViewGestureHandler.GESTURE_SHOW_PRESSED_STATE,
mockDelegate.mMostRecentGestureEvent.mType);
assertEquals("Only GESTURE_SHOW_PRESSED_STATE should have been sent",
1, mockDelegate.mGestureTypeList.size());
event = MotionEvent.obtain(
downTime, eventTime + 5, MotionEvent.ACTION_UP,
FAKE_COORD_X, FAKE_COORD_Y, 0);
mGestureHandler.onTouchEvent(event);
assertEquals("A GESTURE_SINGLE_TAP_UNCONFIRMED event should have been sent",
ContentViewGestureHandler.GESTURE_SINGLE_TAP_UNCONFIRMED,
mockDelegate.mMostRecentGestureEvent.mType);
assertEquals("Only GESTURE_SHOW_PRESSED_STATE and " +
"GESTURE_SINGLE_TAP_UNCONFIRMED should have been sent",
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 GESTURE_SINGLE_TAP_UNCONFIRMED event should have been sent ",
ContentViewGestureHandler.GESTURE_SHOW_PRESS_CANCEL,
mockDelegate.mMostRecentGestureEvent.mType);
assertEquals("Only GESTURE_SHOW_PRESSED_STATE, " +
"GESTURE_SINGLE_TAP_UNCONFIRMED, and " +
"GESTURE_SHOW_PRESS_CANCEL should have been sent",
3, mockDelegate.mGestureTypeList.size());
// Moving a very small amount of distance should not trigger the double tap drag zoom mode.
event = MotionEvent.obtain(
eventTime + 10, eventTime + 10, MotionEvent.ACTION_MOVE,
FAKE_COORD_X, FAKE_COORD_Y + 1, 0);
assertTrue(mGestureHandler.onTouchEvent(event));
assertEquals("Only GESTURE_SHOW_PRESSED_STATE, " +
"GESTURE_SINGLE_TAP_UNCONFIRMED, and " +
"GESTURE_SHOW_PRESS_CANCEL should have been sent",
3, mockDelegate.mGestureTypeList.size());
event = MotionEvent.obtain(
eventTime + 10, eventTime + 15, MotionEvent.ACTION_UP,
FAKE_COORD_X, FAKE_COORD_Y + 1, 0);
assertTrue(mGestureHandler.onTouchEvent(event));
assertEquals("A double tap should have occurred",
ContentViewGestureHandler.GESTURE_DOUBLE_TAP,
mockDelegate.mMostRecentGestureEvent.mType);
assertEquals("Only GESTURE_SHOW_PRESSED_STATE, " +
"GESTURE_SINGLE_TAP_UNCONFIRMED, " +
"GESTURE_SHOW_PRESS_CANCEL, and " +
"GESTURE_DOUBLE_TAP should have been sent",
4, mockDelegate.mGestureTypeList.size());
}
/**
* Verify that double tap drag zoom feature is correctly executed.
* @throws Exception
*/
@SmallTest
@Feature({"Gestures"})
public void testDoubleTapDragZoom() throws Exception {
final long downTime1 = SystemClock.uptimeMillis();
final long downTime2 = downTime1 + 100;
GestureRecordingMotionEventDelegate mockDelegate =
new GestureRecordingMotionEventDelegate();
mGestureHandler = new ContentViewGestureHandler(
getInstrumentation().getTargetContext(), mockDelegate,
new MockZoomManager(getInstrumentation().getTargetContext(), null),
ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC);
mLongPressDetector = new LongPressDetector(
getInstrumentation().getTargetContext(), mGestureHandler);
MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime1, downTime1);
assertTrue(mGestureHandler.onTouchEvent(event));
assertFalse("Should not have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
mGestureHandler.sendShowPressedStateGestureForTesting();
assertEquals("GESTURE_SHOW_PRESSED_STATE should have been sent",
ContentViewGestureHandler.GESTURE_SHOW_PRESSED_STATE,
mockDelegate.mMostRecentGestureEvent.mType);
assertEquals("Only GESTURE_SHOW_PRESSED_STATE should have been sent",
1, mockDelegate.mGestureTypeList.size());
event = MotionEvent.obtain(
downTime1, downTime1 + 5, MotionEvent.ACTION_UP,
FAKE_COORD_X, FAKE_COORD_Y, 0);
mGestureHandler.onTouchEvent(event);
assertEquals("A GESTURE_SINGLE_TAP_UNCONFIRMED event should have been sent",
ContentViewGestureHandler.GESTURE_SINGLE_TAP_UNCONFIRMED,
mockDelegate.mMostRecentGestureEvent.mType);
assertEquals("Only GESTURE_SHOW_PRESSED_STATE and " +
"GESTURE_TAB_UNCONFIRMED " +
"should have been sent",
2, mockDelegate.mGestureTypeList.size());
event = MotionEvent.obtain(
downTime2, downTime2, MotionEvent.ACTION_DOWN,
FAKE_COORD_X, FAKE_COORD_Y, 0);
assertTrue(mGestureHandler.onTouchEvent(event));
assertEquals("GESTURE_SHOW_PRESS_CANCEL should have been sent",
ContentViewGestureHandler.GESTURE_SHOW_PRESS_CANCEL,
mockDelegate.mMostRecentGestureEvent.mType);
assertEquals("Only GESTURE_SHOW_PRESSED_STATE, " +
"GESTURE_TAB_UNCONFIRMED, and " +
"GESTURE_SHOW_PRESS_CANCEL should have been sent",
3, mockDelegate.mGestureTypeList.size());
event = MotionEvent.obtain(
downTime2, downTime2 + 5, MotionEvent.ACTION_MOVE,
FAKE_COORD_X, FAKE_COORD_Y + 100, 0);
assertTrue(mGestureHandler.onTouchEvent(event));
assertTrue("GESTURE_SCROLL_START should have been sent",
mockDelegate.mGestureTypeList.contains(
ContentViewGestureHandler.GESTURE_SCROLL_START));
assertEquals("GESTURE_PINCH_BEGIN should have been sent",
ContentViewGestureHandler.GESTURE_PINCH_BEGIN,
mockDelegate.mMostRecentGestureEvent.mType);
assertEquals("Only GESTURE_SHOW_PRESSED_STATE, " +
"GESTURE_TAB_UNCONFIRMED," +
"GESTURE_SHOW_PRESS_CANCEL, " +
"GESTURE_SCROLL_START, and " +
"GESTURE_PINCH_BEGIN should have been sent",
5, mockDelegate.mGestureTypeList.size());
event = MotionEvent.obtain(
downTime2, downTime2 + 10, MotionEvent.ACTION_MOVE,
FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
assertTrue(mGestureHandler.onTouchEvent(event));
assertTrue("GESTURE_SCROLL_BY should have been sent",
mockDelegate.mGestureTypeList.contains(
ContentViewGestureHandler.GESTURE_SCROLL_BY));
assertEquals("GESTURE_PINCH_BY should have been sent",
ContentViewGestureHandler.GESTURE_PINCH_BY,
mockDelegate.mMostRecentGestureEvent.mType);
assertEquals("Only GESTURE_SHOW_PRESSED_STATE, " +
"GESTURE_TAB_UNCONFIRMED," +
"GESTURE_SHOW_PRESS_CANCEL, " +
"GESTURE_SCROLL_START," +
"GESTURE_PINCH_BEGIN, " +
"GESTURE_SCROLL_BY, and " +
"GESTURE_PINCH_BY should have been sent",
7, mockDelegate.mGestureTypeList.size());
event = MotionEvent.obtain(
downTime2, downTime2 + 15, MotionEvent.ACTION_UP,
FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
assertTrue(mGestureHandler.onTouchEvent(event));
assertTrue("GESTURE_PINCH_END should have been sent",
mockDelegate.mGestureTypeList.contains(
ContentViewGestureHandler.GESTURE_PINCH_END));
assertEquals("GESTURE_SCROLL_END should have been sent",
ContentViewGestureHandler.GESTURE_SCROLL_END,
mockDelegate.mMostRecentGestureEvent.mType);
assertEquals("Only GESTURE_SHOW_PRESSED_STATE, " +
"GESTURE_TAB_UNCONFIRMED," +
"GESTURE_SHOW_PRESS_CANCEL, " +
"GESTURE_SCROLL_START," +
"GESTURE_PINCH_BEGIN, " +
"GESTURE_SCROLL_BY," +
"GESTURE_PINCH_BY, " +
"GESTURE_PINCH_END, and " +
"GESTURE_SCROLL_END should have been sent",
9, mockDelegate.mGestureTypeList.size());
}
/**
* Verify that double tap drag zoom feature is not invoked
* when it is disabled..
* @throws Exception
*/
@SmallTest
@Feature({"Gestures"})
public void testDoubleTapDragZoomNothingWhenDisabled() throws Exception {
final long downTime1 = SystemClock.uptimeMillis();
final long downTime2 = downTime1 + 100;
GestureRecordingMotionEventDelegate mockDelegate =
new GestureRecordingMotionEventDelegate();
mGestureHandler = new ContentViewGestureHandler(
getInstrumentation().getTargetContext(), mockDelegate,
new MockZoomManager(getInstrumentation().getTargetContext(), null),
ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC);
mGestureHandler.updateDoubleTapDragSupport(false);
MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime1, downTime1);
assertTrue(mGestureHandler.onTouchEvent(event));
event = MotionEvent.obtain(
downTime1, downTime1 + 5, MotionEvent.ACTION_UP,
FAKE_COORD_X, FAKE_COORD_Y, 0);
mGestureHandler.onTouchEvent(event);
event = MotionEvent.obtain(
downTime2, downTime2, MotionEvent.ACTION_DOWN,
FAKE_COORD_X, FAKE_COORD_Y, 0);
assertTrue(mGestureHandler.onTouchEvent(event));
event = MotionEvent.obtain(
downTime2, downTime2 + 5, MotionEvent.ACTION_MOVE,
FAKE_COORD_X, FAKE_COORD_Y + 100, 0);
// As double tap and drag to zoom is disabled, we won't handle
// the move event.
assertFalse(mGestureHandler.onTouchEvent(event));
assertFalse("No GESTURE_SCROLL_START should have been sent",
mockDelegate.mGestureTypeList.contains(
ContentViewGestureHandler.GESTURE_SCROLL_START));
assertTrue("No GESTURE_PINCH_BEGIN should have been sent",
ContentViewGestureHandler.GESTURE_PINCH_BEGIN !=
mockDelegate.mMostRecentGestureEvent.mType);
event = MotionEvent.obtain(
downTime2, downTime2 + 10, MotionEvent.ACTION_MOVE,
FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
assertFalse(mGestureHandler.onTouchEvent(event));
assertFalse("No GESTURE_SCROLL_BY should have been sent",
mockDelegate.mGestureTypeList.contains(
ContentViewGestureHandler.GESTURE_SCROLL_BY));
assertTrue("No GESTURE_PINCH_BY should have been sent",
ContentViewGestureHandler.GESTURE_PINCH_BY !=
mockDelegate.mMostRecentGestureEvent.mType);
event = MotionEvent.obtain(
downTime2, downTime2 + 15, MotionEvent.ACTION_UP,
FAKE_COORD_X, FAKE_COORD_Y + 200, 0);
assertFalse(mGestureHandler.onTouchEvent(event));
assertFalse("No GESTURE_PINCH_END should have been sent",
mockDelegate.mGestureTypeList.contains(
ContentViewGestureHandler.GESTURE_PINCH_END));
}
/**
* Mock MotionEventDelegate that remembers the most recent gesture event.
*/
static class GestureRecordingMotionEventDelegate implements MotionEventDelegate {
static class GestureEvent {
private final int mType;
private final long mTimeMs;
private final int mX;
private final int mY;
private final Bundle mExtraParams;
public GestureEvent(int type, long timeMs, int x, int y, Bundle extraParams) {
mType = type;
mTimeMs = timeMs;
mX = x;
mY = y;
mExtraParams = extraParams;
}
public int getType() {
return mType;
}
public long getTimeMs() {
return mTimeMs;
}
public int getX() {
return mX;
}
public int getY() {
return mY;
}
public Bundle getExtraParams() {
return mExtraParams;
}
};
private GestureEvent mMostRecentGestureEvent;
private boolean mMostRecentGestureEventWasLastForVSync;
private final ArrayList<Integer> mGestureTypeList = new ArrayList<Integer>();
@Override
public boolean sendTouchEvent(long timeMs, int action, TouchPoint[] pts) {
// Not implemented.
return false;
}
@Override
public boolean sendGesture(int type, long timeMs, int x, int y,
boolean lastInputEventForVSync, Bundle extraParams) {
mMostRecentGestureEvent = new GestureEvent(type, timeMs, x, y, extraParams);
mMostRecentGestureEventWasLastForVSync = lastInputEventForVSync;
mGestureTypeList.add(mMostRecentGestureEvent.mType);
return true;
}
@Override
public boolean didUIStealScroll(float x, float y) {
// Not implemented.
return false;
}
@Override
public void invokeZoomPicker() {
// Not implemented.
}
@Override
public boolean hasFixedPageScale() {
return false;
}
public GestureEvent getMostRecentGestureEvent() {
return mMostRecentGestureEvent;
}
public boolean mostRecentGestureEventForLastForVSync() {
return mMostRecentGestureEventWasLastForVSync;
}
}
/**
* Verify that the first event sent while the page is scrolling will be
* converted to a touchcancel. The touchcancel event should stay in the
* pending queue. Acking the touchcancel event will consume all the touch
* events of the current session.
*/
@SmallTest
@Feature({"Gestures"})
public void testTouchEventsCanceledWhileScrolling() {
final int deltaY = 84;
final long downTime = SystemClock.uptimeMillis();
MockMotionEventDelegate delegate = new MockMotionEventDelegate();
ContentViewGestureHandler gestureHandler = new ContentViewGestureHandler(
getInstrumentation().getTargetContext(), delegate,
new MockZoomManager(getInstrumentation().getTargetContext(), null),
ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC);
gestureHandler.hasTouchEventHandlers(true);
MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
assertTrue(gestureHandler.onTouchEvent(event));
gestureHandler.confirmTouchEvent(
ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
event = MotionEvent.obtain(
downTime, downTime + 5, MotionEvent.ACTION_MOVE,
FAKE_COORD_X, FAKE_COORD_Y - deltaY / 2, 0);
assertTrue(gestureHandler.onTouchEvent(event));
gestureHandler.confirmTouchEvent(
ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
// This event will be converted to touchcancel and put into the pending
// queue.
event = MotionEvent.obtain(
downTime, downTime + 10, MotionEvent.ACTION_MOVE,
FAKE_COORD_X, FAKE_COORD_Y - deltaY, 0);
assertTrue(gestureHandler.onTouchEvent(event));
assertEquals(1, gestureHandler.getNumberOfPendingMotionEventsForTesting());
assertTrue(gestureHandler.isEventCancelledForTesting(
gestureHandler.peekFirstInPendingMotionEventsForTesting()));
MotionEvent canceledEvent = gestureHandler.peekFirstInPendingMotionEventsForTesting();
event = motionEvent(MotionEvent.ACTION_POINTER_DOWN, downTime + 15, downTime + 15);
assertTrue(gestureHandler.onTouchEvent(event));
assertEquals(2, gestureHandler.getNumberOfPendingMotionEventsForTesting());
// Acking the touchcancel will drain all the events, and clear the event previously marked
// for cancellation.
gestureHandler.confirmTouchEvent(
ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
assertEquals(0, gestureHandler.getNumberOfPendingMotionEventsForTesting());
assertFalse(gestureHandler.isEventCancelledForTesting(canceledEvent));
// Note: This check relies on an implementation detail of MotionEvent, namely, that the last
// event recycled is the the first returned by MotionEvent.obtain(). Should
// MotioEvent's implementation change, this assumption will need to be rebased.
MotionEvent recycledCanceledEvent =
motionEvent(MotionEvent.ACTION_DOWN, downTime + 20 , downTime + 20);
assertSame("The canceled event should have been recycled.",
canceledEvent, recycledCanceledEvent);
}
/**
* Generate a scroll gesture and verify that the resulting scroll motion event has both absolute
* and relative position information.
*/
@SmallTest
@Feature({"Gestures"})
public void testScrollUpdateCoordinates() {
final int deltaX = 16;
final int deltaY = 84;
final long downTime = SystemClock.uptimeMillis();
GestureRecordingMotionEventDelegate delegate = new GestureRecordingMotionEventDelegate();
ContentViewGestureHandler gestureHandler = new ContentViewGestureHandler(
getInstrumentation().getTargetContext(), delegate,
new MockZoomManager(getInstrumentation().getTargetContext(), null),
ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC);
MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
assertTrue(gestureHandler.onTouchEvent(event));
// Move twice, because the first move gesture is discarded.
event = MotionEvent.obtain(
downTime, downTime + 5, MotionEvent.ACTION_MOVE,
FAKE_COORD_X - deltaX / 2, FAKE_COORD_Y - deltaY / 2, 0);
assertTrue(gestureHandler.onTouchEvent(event));
event = MotionEvent.obtain(
downTime, downTime + 10, MotionEvent.ACTION_MOVE,
FAKE_COORD_X - deltaX, FAKE_COORD_Y - deltaY, 0);
assertTrue(gestureHandler.onTouchEvent(event));
// Make sure the reported gesture event has all the expected data.
GestureRecordingMotionEventDelegate.GestureEvent gestureEvent =
delegate.getMostRecentGestureEvent();
assertNotNull(gestureEvent);
assertEquals(ContentViewGestureHandler.GESTURE_SCROLL_BY, gestureEvent.getType());
assertEquals(downTime + 10, gestureEvent.getTimeMs());
assertEquals(FAKE_COORD_X - deltaX, gestureEvent.getX());
assertEquals(FAKE_COORD_Y - deltaY, gestureEvent.getY());
Bundle extraParams = gestureEvent.getExtraParams();
assertNotNull(extraParams);
// No horizontal delta because of snapping.
assertEquals(0, extraParams.getInt(ContentViewGestureHandler.DISTANCE_X));
assertEquals(deltaY / 2, extraParams.getInt(ContentViewGestureHandler.DISTANCE_Y));
}
/**
* 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),
ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC);
mLongPressDetector = mGestureHandler.getLongPressDetector();
MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
assertTrue(mGestureHandler.onTouchEvent(event));
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));
}
/**
* Verifies that when hasTouchEventHandlers changes while in a gesture, that the pending
* queue does not grow continually.
*/
@SmallTest
@Feature({"Gestures"})
public void testHasTouchEventHandlersChangesInGesture() {
final long downTime = SystemClock.uptimeMillis();
final long eventTime = SystemClock.uptimeMillis();
MockMotionEventDelegate eventDelegate = new MockMotionEventDelegate() {
@Override
public boolean sendTouchEvent(long timeMs, int action, TouchPoint[] pts) {
if (action == TouchPoint.TOUCH_EVENT_TYPE_CANCEL) {
// Ensure the event to be cancelled is already in the pending queue.
MotionEvent canceledEvent =
mGestureHandler.peekFirstInPendingMotionEventsForTesting();
assertTrue(mGestureHandler.isEventCancelledForTesting(canceledEvent));
mGestureHandler.confirmTouchEvent(
ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
// Ensure that the canceled event has been removed from the event queue and is
// no longer scheduled for cancellation.
assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
assertFalse(mGestureHandler.isEventCancelledForTesting(canceledEvent));
}
return super.sendTouchEvent(timeMs, action, pts);
}
};
mGestureHandler = new ContentViewGestureHandler(
getInstrumentation().getTargetContext(), eventDelegate,
new MockZoomManager(getInstrumentation().getTargetContext(), null),
ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC);
mLongPressDetector = new LongPressDetector(
getInstrumentation().getTargetContext(), mGestureHandler);
MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
assertTrue(mGestureHandler.onTouchEvent(event));
assertEquals(0, 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(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
mGestureHandler.hasTouchEventHandlers(true);
event = MotionEvent.obtain(
downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
assertTrue(mGestureHandler.onTouchEvent(event));
assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
event = MotionEvent.obtain(
downTime, eventTime + 15, MotionEvent.ACTION_MOVE,
FAKE_COORD_X * 15, FAKE_COORD_Y * 15, 0);
assertTrue(mGestureHandler.onTouchEvent(event));
assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
}
/**
* 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),
ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC);
mLongPressDetector = mGestureHandler.getLongPressDetector();
MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
assertTrue(mGestureHandler.onTouchEvent(event));
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 touch slop region is removed from the first scroll delta to avoid a jump when
* starting to scroll.
* @throws Exception
*/
@SmallTest
@Feature({"Gestures"})
public void testTouchSlopRemovedFromScroll() throws Exception {
Context context = getInstrumentation().getTargetContext();
final long downTime = SystemClock.uptimeMillis();
final long eventTime = SystemClock.uptimeMillis();
final int scaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
final int scrollDelta = 5;
GestureRecordingMotionEventDelegate mockDelegate =
new GestureRecordingMotionEventDelegate();
mGestureHandler = new ContentViewGestureHandler(
context, mockDelegate,
new MockZoomManager(context, null),
ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC);
MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
assertTrue(mGestureHandler.onTouchEvent(event));
event = MotionEvent.obtain(
downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
FAKE_COORD_X, FAKE_COORD_Y + scaledTouchSlop + scrollDelta, 0);
assertTrue(mGestureHandler.onTouchEvent(event));
assertEquals("We should have started scrolling",
ContentViewGestureHandler.GESTURE_SCROLL_BY,
mockDelegate.mMostRecentGestureEvent.mType);
GestureRecordingMotionEventDelegate.GestureEvent gestureEvent =
mockDelegate.getMostRecentGestureEvent();
assertNotNull(gestureEvent);
Bundle extraParams = gestureEvent.getExtraParams();
assertEquals(0, extraParams.getInt(ContentViewGestureHandler.DISTANCE_X));
assertEquals(-scrollDelta, extraParams.getInt(ContentViewGestureHandler.DISTANCE_Y));
}
static private void sendLastScrollByEvent(ContentViewGestureHandler handler) {
final long downTime = SystemClock.uptimeMillis();
final long eventTime = SystemClock.uptimeMillis();
MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
assertTrue(handler.onTouchEvent(event));
event = MotionEvent.obtain(
downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
FAKE_COORD_X, FAKE_COORD_Y + 30, 0);
assertTrue(handler.onTouchEvent(event));
}
static private void sendLastPinchEvent(ContentViewGestureHandler handler) {
final long downTime = SystemClock.uptimeMillis();
final long eventTime = SystemClock.uptimeMillis();
handler.pinchBegin(downTime, FAKE_COORD_X, FAKE_COORD_Y);
handler.pinchBy(eventTime + 10, FAKE_COORD_X, FAKE_COORD_Y, 2);
}
/**
* Verify that certain gesture events are sent with the "last for this vsync" flag set.
* @throws Exception
*/
@SmallTest
@Feature({"Gestures"})
public void testFinalInputEventsForVSyncInterval() throws Exception {
Context context = getInstrumentation().getTargetContext();
final boolean inputEventsDeliveredAtVSync =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
GestureRecordingMotionEventDelegate mockDelegate =
new GestureRecordingMotionEventDelegate();
mGestureHandler = new ContentViewGestureHandler(
context, mockDelegate,
new MockZoomManager(context, null),
inputEventsDeliveredAtVSync ? ContentViewCore.INPUT_EVENTS_DELIVERED_AT_VSYNC :
ContentViewCore.INPUT_EVENTS_DELIVERED_IMMEDIATELY);
sendLastScrollByEvent(mGestureHandler);
assertEquals("We should have started scrolling",
ContentViewGestureHandler.GESTURE_SCROLL_BY,
mockDelegate.mMostRecentGestureEvent.mType);
if (inputEventsDeliveredAtVSync) {
assertEquals("Gesture should be last for vsync",
true,
mockDelegate.mostRecentGestureEventForLastForVSync());
} else {
assertEquals("Gesture should not be last for vsync",
false,
mockDelegate.mostRecentGestureEventForLastForVSync());
}
sendLastPinchEvent(mGestureHandler);
assertEquals("We should have started pinch-zooming",
ContentViewGestureHandler.GESTURE_PINCH_BY,
mockDelegate.mMostRecentGestureEvent.mType);
if (inputEventsDeliveredAtVSync) {
assertEquals("Gesture should be last for vsync",
true,
mockDelegate.mostRecentGestureEventForLastForVSync());
} else {
assertEquals("Gesture should not be last for vsync",
false,
mockDelegate.mostRecentGestureEventForLastForVSync());
}
}
/**
* Verify that no gesture is set with "last for this vsync" flag if vsync is not enabled for
* gesture handler
* @throws Exception
*/
@SmallTest
@Feature({"Gestures"})
public void testFinalInputEventsForVSyncIntervalWithVsyncDisabled() throws Exception {
Context context = getInstrumentation().getTargetContext();
final boolean inputEventsDeliveredAtVSync =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
// Nothing to test on OS version that does not have input batched before vsync.
if (!inputEventsDeliveredAtVSync) {
return;
}
GestureRecordingMotionEventDelegate mockDelegate =
new GestureRecordingMotionEventDelegate();
mGestureHandler = new ContentViewGestureHandler(
context, mockDelegate,
new MockZoomManager(context, null),
ContentViewCore.INPUT_EVENTS_DELIVERED_IMMEDIATELY);
sendLastScrollByEvent(mGestureHandler);
assertEquals("Gesture should not be last for vsync",
false,
mockDelegate.mostRecentGestureEventForLastForVSync());
sendLastPinchEvent(mGestureHandler);
assertEquals("Gesture should not be last for vsync",
false,
mockDelegate.mostRecentGestureEventForLastForVSync());
}
/**
* Verify that a DOWN followed shortly by an UP will trigger
* a GESTURE_SINGLE_TAP_UNCONFIRMED event immediately.
*
* @throws Exception
*/
@SmallTest
@Feature({"Gestures"})
public void testGestureEventsSingleTapUnconfirmed() 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),
ContentViewCore.INPUT_EVENTS_DELIVERED_IMMEDIATELY);
MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
assertTrue(mGestureHandler.onTouchEvent(event));
assertNull(mockDelegate.getMostRecentGestureEvent());
event = motionEvent(MotionEvent.ACTION_UP, downTime, eventTime + 10);
assertFalse(mGestureHandler.onTouchEvent(event));
assertEquals("A GESTURE_SINGLE_TAP_UNCONFIRMED gesture should have been sent",
ContentViewGestureHandler.GESTURE_SINGLE_TAP_UNCONFIRMED,
mockDelegate.mMostRecentGestureEvent.mType);
assertTrue("Should not have confirmed a single tap yet",
mMockListener.mLastSingleTap == null);
}
/**
* Verify that touch move events are properly coalesced.
* @throws Exception
*/
@SmallTest
@Feature({"Gestures"})
public void testTouchMoveCoalescing() throws Exception {
final long downTime = SystemClock.uptimeMillis();
final long eventTime = SystemClock.uptimeMillis();
mGestureHandler.hasTouchEventHandlers(true);
MotionEvent event = MotionEvent.obtain(
downTime, eventTime + 5, MotionEvent.ACTION_MOVE,
FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
assertTrue(mGestureHandler.onTouchEvent(event));
assertFalse("Should not have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
assertEquals("Initial move events should offered to javascript and added to the queue",
1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
assertEquals(TouchPoint.TOUCH_EVENT_TYPE_MOVE, mMockMotionEventDelegate.mLastTouchAction);
event = MotionEvent.obtain(
downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
assertTrue(mGestureHandler.onTouchEvent(event));
assertEquals("Move events already sent to javascript should not be coalesced",
2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
event = MotionEvent.obtain(
downTime, eventTime + 15, MotionEvent.ACTION_MOVE,
FAKE_COORD_X * 15, FAKE_COORD_Y * 15, 0);
assertTrue(mGestureHandler.onTouchEvent(event));
assertEquals("Similar pending move events should be coalesced",
2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
PointerProperties pp1 = new PointerProperties();
pp1.id = 0;
pp1.toolType = MotionEvent.TOOL_TYPE_FINGER;
PointerProperties pp2 = new PointerProperties();
pp2.id = 1;
pp2.toolType = MotionEvent.TOOL_TYPE_FINGER;
PointerProperties[] properties = new PointerProperties[] { pp1, pp2 };
PointerCoords pc1 = new PointerCoords();
pc1.x = FAKE_COORD_X * 10;
pc1.y = FAKE_COORD_Y * 10;
pc1.pressure = 1;
pc1.size = 1;
PointerCoords pc2 = new PointerCoords();
pc2.x = FAKE_COORD_X * 15;
pc2.y = FAKE_COORD_Y * 15;
pc2.pressure = 1;
pc2.size = 1;
PointerCoords[] coords = new PointerCoords[] { pc1, pc2 };
event = MotionEvent.obtain(
downTime, eventTime + 20, MotionEvent.ACTION_MOVE,
2, properties, coords, 0, 0, 1.0f, 1.0f, 0, 0, 0, 0);
assertTrue(mGestureHandler.onTouchEvent(event));
assertEquals("Move events with different pointer counts should not be coalesced",
3, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
event = MotionEvent.obtain(
downTime, eventTime + 25, MotionEvent.ACTION_MOVE,
2, properties, coords, 0, 0, 1.0f, 1.0f, 0, 0, 0, 0);
assertTrue(mGestureHandler.onTouchEvent(event));
assertEquals("Move events with similar pointer counts should be coalesced",
3, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
assertTrue(mGestureHandler.onTouchEvent(event));
assertEquals("Move events should not be coalesced with other events",
4, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
}
/**
* Verify that synchronous confirmTouchEvent() calls made from the MotionEventDelegate behave
* properly.
* @throws Exception
*/
@SmallTest
@Feature({"Gestures"})
public void testSynchronousConfirmTouchEvent() throws Exception {
final long downTime = SystemClock.uptimeMillis();
final long eventTime = SystemClock.uptimeMillis();
mGestureHandler.hasTouchEventHandlers(true);
mMockMotionEventDelegate.disableSynchronousConfirmTouchEvent();
// Queue an asynchronously handled event; this should schedule a touch timeout.
MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, downTime);
assertTrue(mGestureHandler.onTouchEvent(event));
assertEquals(1, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
assertTrue(mGestureHandler.hasScheduledTouchTimeoutEventForTesting());
// Queue another event; this will remain in the queue until the first event is confirmed.
event = MotionEvent.obtain(
downTime, eventTime + 5, MotionEvent.ACTION_MOVE,
FAKE_COORD_X * 10, FAKE_COORD_Y * 10, 0);
assertTrue(mGestureHandler.onTouchEvent(event));
assertEquals(2, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
// Enable synchronous event confirmation upon dispatch.
mMockMotionEventDelegate.enableSynchronousConfirmTouchEvent(
mGestureHandler, ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_CONSUMED);
// Confirm the original event; this should dispatch the second event and confirm it
// synchronously, without scheduling a touch timeout.
mGestureHandler.confirmTouchEvent(
ContentViewGestureHandler.INPUT_EVENT_ACK_STATE_CONSUMED);
assertTrue(mGestureHandler.onTouchEvent(event));
assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
assertFalse(mGestureHandler.hasScheduledTouchTimeoutEventForTesting());
assertEquals(TouchPoint.TOUCH_EVENT_TYPE_MOVE, mMockMotionEventDelegate.mLastTouchAction);
// Adding events to any empty queue will trigger synchronous dispatch and confirmation.
event = MotionEvent.obtain(
downTime, eventTime + 10, MotionEvent.ACTION_MOVE,
FAKE_COORD_X * 5, FAKE_COORD_Y * 5, 0);
assertTrue(mGestureHandler.onTouchEvent(event));
assertEquals(0, mGestureHandler.getNumberOfPendingMotionEventsForTesting());
assertFalse(mGestureHandler.hasScheduledTouchTimeoutEventForTesting());
}
}