blob: 7987d4aa7ff74f151a578fa2a73092c6f7efa82a [file] [log] [blame]
// Copyright 2014 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.
#include "ui/events/gesture_detection/gesture_provider.h"
#include <stddef.h>
#include <memory>
#include "base/location.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/event_constants.h"
#include "ui/events/gesture_detection/gesture_event_data.h"
#include "ui/events/gesture_detection/motion_event.h"
#include "ui/events/test/motion_event_test_utils.h"
#include "ui/gfx/geometry/point_f.h"
using base::TimeDelta;
using base::TimeTicks;
using ui::test::MockMotionEvent;
namespace ui {
namespace {
const float kFakeCoordX = 42.f;
const float kFakeCoordY = 24.f;
const TimeDelta kOneSecond = TimeDelta::FromSeconds(1);
const TimeDelta kOneMicrosecond = TimeDelta::FromMicroseconds(1);
const TimeDelta kDeltaTimeForFlingSequences = TimeDelta::FromMilliseconds(5);
const float kMockTouchRadius = MockMotionEvent::TOUCH_MAJOR / 2;
const float kMaxTwoFingerTapSeparation = 300;
GestureProvider::Config CreateDefaultConfig() {
GestureProvider::Config sConfig;
// The longpress timeout is non-zero only to indicate ordering with respect to
// the showpress timeout.
sConfig.gesture_detector_config.showpress_timeout = base::TimeDelta();
sConfig.gesture_detector_config.longpress_timeout = kOneMicrosecond;
// A valid doubletap timeout should always be non-zero. The value is used not
// only to trigger the timeout that confirms the tap event, but also to gate
// whether the second tap is in fact a double-tap (using a strict inequality
// between times for the first up and the second down events). We use 4
// microseconds simply to allow several intermediate events to occur before
// the second tap at microsecond intervals.
sConfig.gesture_detector_config.double_tap_timeout = kOneMicrosecond * 4;
sConfig.gesture_detector_config.double_tap_min_time = kOneMicrosecond * 2;
return sConfig;
}
gfx::RectF BoundsForSingleMockTouchAtLocation(float x, float y) {
float diameter = MockMotionEvent::TOUCH_MAJOR;
return gfx::RectF(x - diameter / 2, y - diameter / 2, diameter, diameter);
}
} // namespace
class GestureProviderTest : public testing::Test, public GestureProviderClient {
public:
GestureProviderTest() {}
~GestureProviderTest() override {}
static MockMotionEvent ObtainMotionEvent(base::TimeTicks event_time,
MotionEvent::Action action,
float x,
float y) {
return MockMotionEvent(action, event_time, x, y);
}
static MockMotionEvent ObtainMotionEvent(base::TimeTicks event_time,
MotionEvent::Action action,
float x0,
float y0,
float x1,
float y1) {
return MockMotionEvent(action, event_time, x0, y0, x1, y1);
}
static MockMotionEvent ObtainMotionEvent(base::TimeTicks event_time,
MotionEvent::Action action,
float x0,
float y0,
float x1,
float y1,
float x2,
float y2) {
return MockMotionEvent(action, event_time, x0, y0, x1, y1, x2, y2);
}
static MockMotionEvent ObtainMotionEvent(
base::TimeTicks event_time,
MotionEvent::Action action,
const std::vector<gfx::PointF>& positions) {
switch (positions.size()) {
case 1:
return MockMotionEvent(
action, event_time, positions[0].x(), positions[0].y());
case 2:
return MockMotionEvent(action,
event_time,
positions[0].x(),
positions[0].y(),
positions[1].x(),
positions[1].y());
case 3:
return MockMotionEvent(action,
event_time,
positions[0].x(),
positions[0].y(),
positions[1].x(),
positions[1].y(),
positions[2].x(),
positions[2].y());
default:
CHECK(false) << "MockMotionEvent only supports 1-3 pointers";
return MockMotionEvent();
}
}
static MockMotionEvent ObtainMotionEvent(base::TimeTicks event_time,
MotionEvent::Action action) {
return ObtainMotionEvent(event_time, action, kFakeCoordX, kFakeCoordY);
}
// Test
void SetUp() override { SetUpWithConfig(GetDefaultConfig()); }
void TearDown() override {
gestures_.clear();
gesture_provider_.reset();
}
// GestureProviderClient
void OnGestureEvent(const GestureEventData& gesture) override {
EXPECT_EQ(GestureDeviceType::DEVICE_TOUCHSCREEN,
gesture.details.device_type());
if (gesture.type() == ET_GESTURE_SCROLL_BEGIN)
active_scroll_begin_event_.reset(new GestureEventData(gesture));
gestures_.push_back(gesture);
}
void SetUpWithConfig(const GestureProvider::Config& config) {
gesture_provider_.reset(new GestureProvider(config, this));
gesture_provider_->SetMultiTouchZoomSupportEnabled(false);
}
void ResetGestureDetection() {
gesture_provider_->ResetDetection();
gestures_.clear();
}
bool CancelActiveTouchSequence() {
if (!gesture_provider_->current_down_event())
return false;
return gesture_provider_->OnTouchEvent(
*gesture_provider_->current_down_event()->Cancel());
}
bool HasReceivedGesture(EventType type) const {
for (size_t i = 0; i < gestures_.size(); ++i) {
if (gestures_[i].type() == type)
return true;
}
return false;
}
const GestureEventData& GetMostRecentGestureEvent() const {
EXPECT_FALSE(gestures_.empty());
return gestures_.back();
}
EventType GetMostRecentGestureEventType() const {
EXPECT_FALSE(gestures_.empty());
return gestures_.back().type();
}
size_t GetReceivedGestureCount() const { return gestures_.size(); }
const GestureEventData& GetReceivedGesture(size_t index) const {
EXPECT_LT(index, GetReceivedGestureCount());
return gestures_[index];
}
const GestureEventData* GetActiveScrollBeginEvent() const {
return active_scroll_begin_event_ ? active_scroll_begin_event_.get() : NULL;
}
const GestureProvider::Config& GetDefaultConfig() const {
static GestureProvider::Config sConfig = CreateDefaultConfig();
return sConfig;
}
float GetTouchSlop() const {
return GetDefaultConfig().gesture_detector_config.touch_slop;
}
float GetMinScalingSpan() const {
return GetDefaultConfig().scale_gesture_detector_config.min_scaling_span;
}
float GetMinSwipeVelocity() const {
return GetDefaultConfig().gesture_detector_config.minimum_swipe_velocity;
}
base::TimeDelta GetLongPressTimeout() const {
return GetDefaultConfig().gesture_detector_config.longpress_timeout;
}
base::TimeDelta GetShowPressTimeout() const {
return GetDefaultConfig().gesture_detector_config.showpress_timeout;
}
base::TimeDelta GetDoubleTapTimeout() const {
return GetDefaultConfig().gesture_detector_config.double_tap_timeout;
}
base::TimeDelta GetDoubleTapMinTime() const {
return GetDefaultConfig().gesture_detector_config.double_tap_min_time;
}
base::TimeDelta GetValidDoubleTapDelay() const {
return (GetDoubleTapTimeout() + GetDoubleTapMinTime()) / 2;
}
void EnableBeginEndTypes() {
GestureProvider::Config config = GetDefaultConfig();
config.gesture_begin_end_types_enabled = true;
SetUpWithConfig(config);
}
void EnableSwipe() {
GestureProvider::Config config = GetDefaultConfig();
config.gesture_detector_config.swipe_enabled = true;
SetUpWithConfig(config);
}
void EnableTwoFingerTap(float max_distance_for_two_finger_tap,
base::TimeDelta two_finger_tap_timeout) {
GestureProvider::Config config = GetDefaultConfig();
config.gesture_detector_config.two_finger_tap_enabled = true;
config.gesture_detector_config.two_finger_tap_max_separation =
max_distance_for_two_finger_tap;
config.gesture_detector_config.two_finger_tap_timeout =
two_finger_tap_timeout;
SetUpWithConfig(config);
}
void SetMinPinchUpdateSpanDelta(float min_pinch_update_span_delta) {
GestureProvider::Config config = GetDefaultConfig();
config.scale_gesture_detector_config.min_pinch_update_span_delta =
min_pinch_update_span_delta;
SetUpWithConfig(config);
}
void SetMinMaxGestureBoundsLength(float min_gesture_bound_length,
float max_gesture_bound_length) {
GestureProvider::Config config = GetDefaultConfig();
config.min_gesture_bounds_length = min_gesture_bound_length;
config.max_gesture_bounds_length = max_gesture_bound_length;
SetUpWithConfig(config);
}
void SetShowPressAndLongPressTimeout(base::TimeDelta showpress_timeout,
base::TimeDelta longpress_timeout) {
GestureProvider::Config config = GetDefaultConfig();
config.gesture_detector_config.showpress_timeout = showpress_timeout;
config.gesture_detector_config.longpress_timeout = longpress_timeout;
SetUpWithConfig(config);
}
void SetSingleTapRepeatInterval(int repeat_interval) {
GestureProvider::Config config = GetDefaultConfig();
config.gesture_detector_config.single_tap_repeat_interval = repeat_interval;
SetUpWithConfig(config);
}
bool HasDownEvent() const { return gesture_provider_->current_down_event(); }
protected:
void CheckScrollEventSequenceForEndActionType(
MotionEvent::Action end_action_type) {
base::TimeTicks event_time = base::TimeTicks::Now();
const float scroll_to_x = kFakeCoordX + 100;
const float scroll_to_y = kFakeCoordY + 100;
int motion_event_id = 3;
int motion_event_flags = EF_SHIFT_DOWN | EF_CAPS_LOCK_ON;
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
event.SetPrimaryPointerId(motion_event_id);
event.set_flags(motion_event_flags);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(motion_event_flags, GetMostRecentGestureEvent().flags);
event = ObtainMotionEvent(event_time + kOneSecond,
MotionEvent::ACTION_MOVE,
scroll_to_x,
scroll_to_y);
event.SetToolType(0, MotionEvent::TOOL_TYPE_FINGER);
event.SetPrimaryPointerId(motion_event_id);
event.set_flags(motion_event_flags);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_TRUE(gesture_provider_->IsScrollInProgress());
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN));
EXPECT_EQ(motion_event_id, GetMostRecentGestureEvent().motion_event_id);
EXPECT_EQ(motion_event_flags, GetMostRecentGestureEvent().flags);
EXPECT_EQ(event.GetToolType(0),
GetMostRecentGestureEvent().primary_tool_type);
EXPECT_EQ(ET_GESTURE_SCROLL_UPDATE, GetMostRecentGestureEventType());
EXPECT_EQ(BoundsForSingleMockTouchAtLocation(scroll_to_x, scroll_to_y),
GetMostRecentGestureEvent().details.bounding_box_f());
ASSERT_EQ(3U, GetReceivedGestureCount()) << "Only TapDown, "
"ScrollBegin and ScrollBy "
"should have been sent";
EXPECT_EQ(ET_GESTURE_SCROLL_BEGIN, GetReceivedGesture(1).type());
EXPECT_EQ(motion_event_id, GetReceivedGesture(1).motion_event_id);
EXPECT_EQ(event_time + kOneSecond, GetReceivedGesture(1).time)
<< "ScrollBegin should have the time of the ACTION_MOVE";
event = ObtainMotionEvent(
event_time + kOneSecond, end_action_type, scroll_to_x, scroll_to_y);
event.SetToolType(0, MotionEvent::TOOL_TYPE_FINGER);
event.SetPrimaryPointerId(motion_event_id);
gesture_provider_->OnTouchEvent(event);
EXPECT_FALSE(gesture_provider_->IsScrollInProgress());
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SCROLL_END));
EXPECT_EQ(ET_GESTURE_SCROLL_END, GetMostRecentGestureEventType());
EXPECT_EQ(motion_event_id, GetMostRecentGestureEvent().motion_event_id);
EXPECT_EQ(event.GetToolType(0),
GetMostRecentGestureEvent().primary_tool_type);
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
EXPECT_EQ(BoundsForSingleMockTouchAtLocation(scroll_to_x, scroll_to_y),
GetMostRecentGestureEvent().details.bounding_box_f());
}
void OneFingerSwipe(float vx, float vy) {
std::vector<gfx::Vector2dF> velocities;
velocities.push_back(gfx::Vector2dF(vx, vy));
MultiFingerSwipe(velocities);
}
void TwoFingerSwipe(float vx0, float vy0, float vx1, float vy1) {
std::vector<gfx::Vector2dF> velocities;
velocities.push_back(gfx::Vector2dF(vx0, vy0));
velocities.push_back(gfx::Vector2dF(vx1, vy1));
MultiFingerSwipe(velocities);
}
void ThreeFingerSwipe(float vx0,
float vy0,
float vx1,
float vy1,
float vx2,
float vy2) {
std::vector<gfx::Vector2dF> velocities;
velocities.push_back(gfx::Vector2dF(vx0, vy0));
velocities.push_back(gfx::Vector2dF(vx1, vy1));
velocities.push_back(gfx::Vector2dF(vx2, vy2));
MultiFingerSwipe(velocities);
}
void MultiFingerSwipe(std::vector<gfx::Vector2dF> velocities) {
ASSERT_GT(velocities.size(), 0U);
base::TimeTicks event_time = base::TimeTicks::Now();
std::vector<gfx::PointF> positions(velocities.size());
for (size_t i = 0; i < positions.size(); ++i)
positions[i] = gfx::PointF(kFakeCoordX * (i + 1), kFakeCoordY * (i + 1));
float dt = kDeltaTimeForFlingSequences.InSecondsF();
// Each pointer down should be a separate event.
for (size_t i = 0; i < positions.size(); ++i) {
const size_t pointer_count = i + 1;
std::vector<gfx::PointF> event_positions(pointer_count);
event_positions.assign(positions.begin(),
positions.begin() + pointer_count);
MockMotionEvent event =
ObtainMotionEvent(event_time,
pointer_count > 1 ? MotionEvent::ACTION_POINTER_DOWN
: MotionEvent::ACTION_DOWN,
event_positions);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
}
for (size_t i = 0; i < positions.size(); ++i)
positions[i] += gfx::ScaleVector2d(velocities[i], dt);
MockMotionEvent event =
ObtainMotionEvent(event_time + kDeltaTimeForFlingSequences,
MotionEvent::ACTION_MOVE,
positions);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
for (size_t i = 0; i < positions.size(); ++i)
positions[i] += gfx::ScaleVector2d(velocities[i], dt);
event = ObtainMotionEvent(event_time + 2 * kDeltaTimeForFlingSequences,
MotionEvent::ACTION_MOVE,
positions);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time + 2 * kDeltaTimeForFlingSequences,
MotionEvent::ACTION_POINTER_UP,
positions);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
}
static void RunTasksAndWait(base::TimeDelta delay) {
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, base::MessageLoop::QuitWhenIdleClosure(), delay);
base::RunLoop().Run();
}
std::vector<GestureEventData> gestures_;
std::unique_ptr<GestureProvider> gesture_provider_;
std::unique_ptr<GestureEventData> active_scroll_begin_event_;
base::MessageLoopForUI message_loop_;
};
// Verify that a DOWN followed shortly by an UP will trigger a single tap.
TEST_F(GestureProviderTest, GestureTap) {
base::TimeTicks event_time = base::TimeTicks::Now();
int motion_event_id = 6;
int motion_event_flags = EF_CONTROL_DOWN | EF_ALT_DOWN;
gesture_provider_->SetDoubleTapSupportForPlatformEnabled(false);
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
event.SetToolType(0, MotionEvent::TOOL_TYPE_FINGER);
event.SetPrimaryPointerId(motion_event_id);
event.set_flags(motion_event_flags);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
EXPECT_EQ(event.GetToolType(0),
GetMostRecentGestureEvent().primary_tool_type);
EXPECT_EQ(motion_event_flags, GetMostRecentGestureEvent().flags);
EXPECT_EQ(BoundsForSingleMockTouchAtLocation(kFakeCoordX, kFakeCoordY),
GetMostRecentGestureEvent().details.bounding_box_f());
event = ObtainMotionEvent(event_time + kOneMicrosecond,
MotionEvent::ACTION_UP);
event.SetToolType(0, MotionEvent::TOOL_TYPE_FINGER);
event.SetPrimaryPointerId(motion_event_id);
event.set_flags(motion_event_flags);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
// Ensure tap details have been set.
EXPECT_EQ(1, GetMostRecentGestureEvent().details.tap_count());
EXPECT_EQ(event.GetToolType(0),
GetMostRecentGestureEvent().primary_tool_type);
EXPECT_EQ(motion_event_id, GetMostRecentGestureEvent().motion_event_id);
EXPECT_EQ(motion_event_flags, GetMostRecentGestureEvent().flags);
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
EXPECT_EQ(BoundsForSingleMockTouchAtLocation(kFakeCoordX, kFakeCoordY),
GetMostRecentGestureEvent().details.bounding_box_f());
}
// Verify that a DOWN followed shortly by an UP will trigger
// a ET_GESTURE_TAP_UNCONFIRMED event if double-tap is enabled.
TEST_F(GestureProviderTest, GestureTapWithDelay) {
base::TimeTicks event_time = base::TimeTicks::Now();
int motion_event_id = 6;
int motion_event_flags = EF_CONTROL_DOWN | EF_ALT_DOWN | EF_CAPS_LOCK_ON;
gesture_provider_->SetDoubleTapSupportForPlatformEnabled(true);
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
event.SetPrimaryPointerId(motion_event_id);
event.set_flags(motion_event_flags);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
EXPECT_EQ(motion_event_id, GetMostRecentGestureEvent().motion_event_id);
EXPECT_EQ(motion_event_flags, GetMostRecentGestureEvent().flags);
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
EXPECT_EQ(BoundsForSingleMockTouchAtLocation(kFakeCoordX, kFakeCoordY),
GetMostRecentGestureEvent().details.bounding_box_f());
event = ObtainMotionEvent(event_time + kOneMicrosecond,
MotionEvent::ACTION_UP);
event.SetPrimaryPointerId(motion_event_id);
event.set_flags(motion_event_flags);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_UNCONFIRMED, GetMostRecentGestureEventType());
// Ensure tap details have been set.
EXPECT_EQ(1, GetMostRecentGestureEvent().details.tap_count());
EXPECT_EQ(motion_event_id, GetMostRecentGestureEvent().motion_event_id);
EXPECT_EQ(motion_event_flags, GetMostRecentGestureEvent().flags);
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
EXPECT_EQ(BoundsForSingleMockTouchAtLocation(kFakeCoordX, kFakeCoordY),
GetMostRecentGestureEvent().details.bounding_box_f());
EXPECT_EQ(event.GetEventTime(), GetMostRecentGestureEvent().time);
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_TAP));
RunTasksAndWait(GetDoubleTapTimeout());
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_TAP));
EXPECT_EQ(motion_event_id, GetMostRecentGestureEvent().motion_event_id);
EXPECT_EQ(event.GetEventTime(), GetMostRecentGestureEvent().time);
}
// Verify that a DOWN followed by a MOVE will trigger fling (but not LONG).
TEST_F(GestureProviderTest, GestureFlingAndCancelLongPress) {
base::TimeTicks event_time = TimeTicks::Now();
base::TimeDelta delta_time = kDeltaTimeForFlingSequences;
int motion_event_id = 6;
int motion_event_flags = EF_ALT_DOWN;
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
event.SetPrimaryPointerId(motion_event_id);
event.set_flags(motion_event_flags);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
EXPECT_EQ(motion_event_id, GetMostRecentGestureEvent().motion_event_id);
EXPECT_EQ(motion_event_flags, GetMostRecentGestureEvent().flags);
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
event = ObtainMotionEvent(event_time + delta_time,
MotionEvent::ACTION_MOVE,
kFakeCoordX * 10,
kFakeCoordY * 10);
event.SetPrimaryPointerId(motion_event_id);
event.set_flags(motion_event_flags);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time + delta_time * 2,
MotionEvent::ACTION_UP,
kFakeCoordX * 10,
kFakeCoordY * 10);
event.SetPrimaryPointerId(motion_event_id);
event.set_flags(motion_event_flags);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_SCROLL_FLING_START, GetMostRecentGestureEventType());
EXPECT_EQ(motion_event_id, GetMostRecentGestureEvent().motion_event_id);
EXPECT_EQ(motion_event_flags, GetMostRecentGestureEvent().flags);
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_LONG_PRESS));
EXPECT_EQ(
BoundsForSingleMockTouchAtLocation(kFakeCoordX * 10, kFakeCoordY * 10),
GetMostRecentGestureEvent().details.bounding_box_f());
}
// Verify that for a normal scroll the following events are sent:
// - ET_GESTURE_SCROLL_BEGIN
// - ET_GESTURE_SCROLL_UPDATE
// - ET_GESTURE_SCROLL_END
TEST_F(GestureProviderTest, ScrollEventActionUpSequence) {
CheckScrollEventSequenceForEndActionType(MotionEvent::ACTION_UP);
}
// Verify that for a cancelled scroll the following events are sent:
// - ET_GESTURE_SCROLL_BEGIN
// - ET_GESTURE_SCROLL_UPDATE
// - ET_GESTURE_SCROLL_END
TEST_F(GestureProviderTest, ScrollEventActionCancelSequence) {
CheckScrollEventSequenceForEndActionType(MotionEvent::ACTION_CANCEL);
}
// Verify that for a normal fling (fling after scroll) the following events are
// sent:
// - ET_GESTURE_SCROLL_BEGIN
// - ET_SCROLL_FLING_START
TEST_F(GestureProviderTest, FlingEventSequence) {
base::TimeTicks event_time = base::TimeTicks::Now();
base::TimeDelta delta_time = kDeltaTimeForFlingSequences;
int motion_event_id = 6;
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
event.SetPrimaryPointerId(motion_event_id);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time + delta_time,
MotionEvent::ACTION_MOVE,
kFakeCoordX * 5,
kFakeCoordY * 5);
event.SetPrimaryPointerId(motion_event_id);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_TRUE(gesture_provider_->IsScrollInProgress());
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN));
EXPECT_EQ(ET_GESTURE_SCROLL_UPDATE, GetMostRecentGestureEventType());
EXPECT_EQ(motion_event_id, GetMostRecentGestureEvent().motion_event_id);
ASSERT_EQ(3U, GetReceivedGestureCount());
ASSERT_EQ(ET_GESTURE_SCROLL_BEGIN, GetReceivedGesture(1).type());
EXPECT_EQ(motion_event_id, GetReceivedGesture(1).motion_event_id);
// We don't want to take a dependency here on exactly how hints are calculated
// for a fling (eg. may depend on velocity), so just validate the direction.
int hint_x = GetReceivedGesture(1).details.scroll_x_hint();
int hint_y = GetReceivedGesture(1).details.scroll_y_hint();
EXPECT_TRUE(hint_x > 0 && hint_y > 0 && hint_x > hint_y)
<< "ScrollBegin hint should be in positive X axis";
event = ObtainMotionEvent(event_time + delta_time * 2,
MotionEvent::ACTION_UP,
kFakeCoordX * 10,
kFakeCoordY * 10);
event.SetPrimaryPointerId(motion_event_id);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_FALSE(gesture_provider_->IsScrollInProgress());
EXPECT_EQ(ET_SCROLL_FLING_START, GetMostRecentGestureEventType());
EXPECT_EQ(motion_event_id, GetMostRecentGestureEvent().motion_event_id);
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_SCROLL_END));
EXPECT_EQ(event_time + delta_time * 2, GetMostRecentGestureEvent().time)
<< "FlingStart should have the time of the ACTION_UP";
}
TEST_F(GestureProviderTest, GestureCancelledOnCancelEvent) {
const base::TimeTicks event_time = TimeTicks::Now();
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
RunTasksAndWait(GetLongPressTimeout() + GetShowPressTimeout() +
kOneMicrosecond);
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SHOW_PRESS));
EXPECT_EQ(ET_GESTURE_LONG_PRESS, GetMostRecentGestureEventType());
// A cancellation event may be triggered for a number of reasons, e.g.,
// from a context-menu-triggering long press resulting in loss of focus.
EXPECT_TRUE(CancelActiveTouchSequence());
EXPECT_FALSE(HasDownEvent());
// A final ACTION_UP should have no effect.
event = ObtainMotionEvent(event_time + kOneMicrosecond * 2,
MotionEvent::ACTION_UP);
EXPECT_FALSE(gesture_provider_->OnTouchEvent(event));
}
TEST_F(GestureProviderTest, GestureCancelledOnDetectionReset) {
const base::TimeTicks event_time = TimeTicks::Now();
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
RunTasksAndWait(GetLongPressTimeout() + GetShowPressTimeout() +
kOneMicrosecond);
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SHOW_PRESS));
EXPECT_EQ(ET_GESTURE_LONG_PRESS, GetMostRecentGestureEventType());
ResetGestureDetection();
EXPECT_FALSE(HasDownEvent());
// A final ACTION_UP should have no effect.
event = ObtainMotionEvent(event_time + kOneMicrosecond * 2,
MotionEvent::ACTION_UP);
EXPECT_FALSE(gesture_provider_->OnTouchEvent(event));
}
TEST_F(GestureProviderTest, NoTapAfterScrollBegins) {
base::TimeTicks event_time = base::TimeTicks::Now();
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
event = ObtainMotionEvent(event_time + kOneMicrosecond,
MotionEvent::ACTION_MOVE,
kFakeCoordX + 50,
kFakeCoordY + 50);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_SCROLL_UPDATE, GetMostRecentGestureEventType());
event = ObtainMotionEvent(event_time + kOneSecond,
MotionEvent::ACTION_UP,
kFakeCoordX + 50,
kFakeCoordY + 50);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_SCROLL_END, GetMostRecentGestureEventType());
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_LONG_TAP));
}
TEST_F(GestureProviderTest, DoubleTap) {
base::TimeTicks event_time = base::TimeTicks::Now();
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
event = ObtainMotionEvent(event_time + kOneMicrosecond,
MotionEvent::ACTION_UP,
kFakeCoordX,
kFakeCoordY);
gesture_provider_->OnTouchEvent(event);
EXPECT_EQ(ET_GESTURE_TAP_UNCONFIRMED, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
event_time += GetValidDoubleTapDelay();
event = ObtainMotionEvent(event_time,
MotionEvent::ACTION_DOWN,
kFakeCoordX,
kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
// Moving a very small amount of distance should not trigger the double tap
// drag zoom mode.
event = ObtainMotionEvent(event_time + kOneMicrosecond,
MotionEvent::ACTION_MOVE,
kFakeCoordX,
kFakeCoordY + 1);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
event = ObtainMotionEvent(event_time + kOneMicrosecond * 2,
MotionEvent::ACTION_UP,
kFakeCoordX,
kFakeCoordY + 1);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
const GestureEventData& double_tap = GetMostRecentGestureEvent();
EXPECT_EQ(ET_GESTURE_DOUBLE_TAP, double_tap.type());
// Ensure tap details have been set.
EXPECT_EQ(10, double_tap.details.bounding_box().width());
EXPECT_EQ(10, double_tap.details.bounding_box().height());
EXPECT_EQ(1, double_tap.details.tap_count());
}
TEST_F(GestureProviderTest, DoubleTapDragZoomBasic) {
const base::TimeTicks down_time_1 = TimeTicks::Now();
const base::TimeTicks down_time_2 = down_time_1 + GetValidDoubleTapDelay();
MockMotionEvent event =
ObtainMotionEvent(down_time_1, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(down_time_1 + kOneMicrosecond,
MotionEvent::ACTION_UP,
kFakeCoordX,
kFakeCoordY);
gesture_provider_->OnTouchEvent(event);
EXPECT_EQ(ET_GESTURE_TAP_UNCONFIRMED, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
event = ObtainMotionEvent(
down_time_2, MotionEvent::ACTION_DOWN, kFakeCoordX, kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
event = ObtainMotionEvent(down_time_2 + kOneMicrosecond,
MotionEvent::ACTION_MOVE,
kFakeCoordX,
kFakeCoordY + 100);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN));
ASSERT_EQ(ET_GESTURE_PINCH_BEGIN, GetMostRecentGestureEventType());
EXPECT_EQ(BoundsForSingleMockTouchAtLocation(kFakeCoordX, kFakeCoordY + 100),
GetMostRecentGestureEvent().details.bounding_box_f());
event = ObtainMotionEvent(down_time_2 + kOneMicrosecond * 2,
MotionEvent::ACTION_MOVE,
kFakeCoordX,
kFakeCoordY + 200);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
ASSERT_EQ(ET_GESTURE_PINCH_UPDATE, GetMostRecentGestureEventType());
EXPECT_LT(1.f, GetMostRecentGestureEvent().details.scale());
EXPECT_EQ(BoundsForSingleMockTouchAtLocation(kFakeCoordX, kFakeCoordY + 200),
GetMostRecentGestureEvent().details.bounding_box_f());
event = ObtainMotionEvent(down_time_2 + kOneMicrosecond * 3,
MotionEvent::ACTION_MOVE,
kFakeCoordX,
kFakeCoordY + 100);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
ASSERT_EQ(ET_GESTURE_PINCH_UPDATE, GetMostRecentGestureEventType());
EXPECT_GT(1.f, GetMostRecentGestureEvent().details.scale());
EXPECT_EQ(BoundsForSingleMockTouchAtLocation(kFakeCoordX, kFakeCoordY + 100),
GetMostRecentGestureEvent().details.bounding_box_f());
event = ObtainMotionEvent(down_time_2 + kOneMicrosecond * 4,
MotionEvent::ACTION_UP,
kFakeCoordX,
kFakeCoordY - 200);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_PINCH_END));
EXPECT_EQ(ET_GESTURE_SCROLL_END, GetMostRecentGestureEventType());
EXPECT_EQ(BoundsForSingleMockTouchAtLocation(kFakeCoordX, kFakeCoordY - 200),
GetMostRecentGestureEvent().details.bounding_box_f());
}
// Generate a scroll gesture and verify that the resulting scroll motion event
// has both absolute and relative position information.
TEST_F(GestureProviderTest, ScrollUpdateValues) {
const float delta_x = 16;
const float delta_y = 84;
const float raw_offset_x = 17.3f;
const float raw_offset_y = 13.7f;
const base::TimeTicks event_time = TimeTicks::Now();
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
// Move twice so that we get two ET_GESTURE_SCROLL_UPDATE events and can
// compare the relative and absolute coordinates.
event = ObtainMotionEvent(event_time + kOneMicrosecond,
MotionEvent::ACTION_MOVE,
kFakeCoordX - delta_x / 2,
kFakeCoordY - delta_y / 2);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time + kOneMicrosecond * 2,
MotionEvent::ACTION_MOVE,
kFakeCoordX - delta_x,
kFakeCoordY - delta_y);
event.SetRawOffset(raw_offset_x, raw_offset_y);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
// Make sure the reported gesture event has all the expected details.
ASSERT_LT(0U, GetReceivedGestureCount());
GestureEventData gesture = GetMostRecentGestureEvent();
EXPECT_EQ(ET_GESTURE_SCROLL_UPDATE, gesture.type());
EXPECT_EQ(event_time + kOneMicrosecond * 2, gesture.time);
EXPECT_EQ(kFakeCoordX - delta_x, gesture.x);
EXPECT_EQ(kFakeCoordY - delta_y, gesture.y);
EXPECT_EQ(kFakeCoordX - delta_x + raw_offset_x, gesture.raw_x);
EXPECT_EQ(kFakeCoordY - delta_y + raw_offset_y, gesture.raw_y);
EXPECT_EQ(1, gesture.details.touch_points());
// No horizontal delta because of snapping.
EXPECT_EQ(0, gesture.details.scroll_x());
EXPECT_EQ(-delta_y / 2, gesture.details.scroll_y());
}
// Verify that fractional scroll deltas are rounded as expected and that
// fractional scrolling doesn't break scroll snapping.
TEST_F(GestureProviderTest, FractionalScroll) {
const float delta_x = 0.4f;
const float delta_y = 5.2f;
const base::TimeTicks event_time = TimeTicks::Now();
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
// Skip past the touch slop and move back.
event = ObtainMotionEvent(event_time,
MotionEvent::ACTION_MOVE,
kFakeCoordX,
kFakeCoordY + 100);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time,
MotionEvent::ACTION_MOVE);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
// Now move up slowly, mostly vertically but with a (fractional) bit of
// horizontal motion.
for (int i = 1; i <= 10; i++) {
event = ObtainMotionEvent(event_time + kOneMicrosecond * i,
MotionEvent::ACTION_MOVE,
kFakeCoordX + delta_x * i,
kFakeCoordY + delta_y * i);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
ASSERT_LT(0U, GetReceivedGestureCount());
GestureEventData gesture = GetMostRecentGestureEvent();
EXPECT_EQ(ET_GESTURE_SCROLL_UPDATE, gesture.type());
EXPECT_EQ(event_time + kOneMicrosecond * i, gesture.time);
EXPECT_EQ(1, gesture.details.touch_points());
// Verify that the event co-ordinates are still the precise values we
// supplied.
EXPECT_EQ(kFakeCoordX + delta_x * i, gesture.x);
EXPECT_FLOAT_EQ(kFakeCoordY + delta_y * i, gesture.y);
// Verify that we're scrolling vertically by the expected amount
// (modulo rounding).
EXPECT_GE(gesture.details.scroll_y(), (int)delta_y);
EXPECT_LE(gesture.details.scroll_y(), ((int)delta_y) + 1);
// And that there has been no horizontal motion at all.
EXPECT_EQ(0, gesture.details.scroll_x());
}
}
// Generate a scroll gesture and verify that the resulting scroll begin event
// has the expected hint values.
TEST_F(GestureProviderTest, ScrollBeginValues) {
const float delta_x = 13;
const float delta_y = 89;
const base::TimeTicks event_time = TimeTicks::Now();
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
// Move twice such that the first event isn't sufficient to start
// scrolling on it's own.
event = ObtainMotionEvent(event_time + kOneMicrosecond,
MotionEvent::ACTION_MOVE,
kFakeCoordX + 2,
kFakeCoordY + 1);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_FALSE(gesture_provider_->IsScrollInProgress());
event = ObtainMotionEvent(event_time + kOneMicrosecond * 2,
MotionEvent::ACTION_MOVE,
kFakeCoordX + delta_x,
kFakeCoordY + delta_y);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_TRUE(gesture_provider_->IsScrollInProgress());
const GestureEventData* scroll_begin_gesture = GetActiveScrollBeginEvent();
ASSERT_TRUE(scroll_begin_gesture);
EXPECT_EQ(delta_x, scroll_begin_gesture->details.scroll_x_hint());
EXPECT_EQ(delta_y, scroll_begin_gesture->details.scroll_y_hint());
}
// The following three tests verify that slop regions are checked for
// one and two finger scrolls. Three-finger tap doesn't exist, so,
// no slop region check is needed for three-finger scrolls.
TEST_F(GestureProviderTest, SlopRegionCheckOnOneFingerScroll) {
EnableTwoFingerTap(kMaxTwoFingerTapSeparation, base::TimeDelta());
const float scaled_touch_slop = GetTouchSlop();
base::TimeTicks event_time = base::TimeTicks::Now();
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN, 0, 0);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
// Move within slop region.
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_MOVE, 0,
scaled_touch_slop / 2);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
// Exceed slop region.
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_MOVE, 0,
2 * scaled_touch_slop);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_UP, 0,
2 * scaled_touch_slop);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetReceivedGesture(0).type());
EXPECT_EQ(ET_GESTURE_SCROLL_BEGIN, GetReceivedGesture(1).type());
EXPECT_EQ(ET_GESTURE_SCROLL_UPDATE, GetReceivedGesture(2).type());
EXPECT_EQ(ET_GESTURE_SCROLL_END, GetReceivedGesture(3).type());
EXPECT_EQ(4U, GetReceivedGestureCount());
}
TEST_F(GestureProviderTest, SlopRegionCheckOnTwoFingerScroll) {
EnableTwoFingerTap(kMaxTwoFingerTapSeparation, base::TimeDelta());
const float scaled_touch_slop = GetTouchSlop();
base::TimeTicks event_time = base::TimeTicks::Now();
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN, 0, 0);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_POINTER_DOWN, 0, 0,
kMaxTwoFingerTapSeparation / 2, 0);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
// Move within slop region: two-finger tap happens.
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_MOVE, 0,
scaled_touch_slop / 2,
kMaxTwoFingerTapSeparation / 2, 0);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_POINTER_UP, 0,
scaled_touch_slop / 2,
kMaxTwoFingerTapSeparation / 2, 0);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
// Exceed slop region: scroll.
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_POINTER_DOWN, 0,
scaled_touch_slop / 2,
kMaxTwoFingerTapSeparation / 2, 0);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(
event_time, MotionEvent::ACTION_MOVE, 0, scaled_touch_slop / 2,
kMaxTwoFingerTapSeparation / 2, 2 * scaled_touch_slop);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(
event_time, MotionEvent::ACTION_POINTER_UP, 0, scaled_touch_slop / 2,
kMaxTwoFingerTapSeparation / 2, 2 * scaled_touch_slop);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_UP, 0,
scaled_touch_slop / 2);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetReceivedGesture(0).type());
EXPECT_EQ(ET_GESTURE_TWO_FINGER_TAP, GetReceivedGesture(1).type());
EXPECT_EQ(ET_GESTURE_SCROLL_BEGIN, GetReceivedGesture(2).type());
EXPECT_EQ(ET_GESTURE_SCROLL_UPDATE, GetReceivedGesture(3).type());
EXPECT_EQ(ET_GESTURE_SCROLL_END, GetReceivedGesture(4).type());
EXPECT_EQ(5U, GetReceivedGestureCount());
}
TEST_F(GestureProviderTest, NoSlopRegionCheckOnThreeFingerScroll) {
EnableTwoFingerTap(kMaxTwoFingerTapSeparation, base::TimeDelta());
const float scaled_touch_slop = GetTouchSlop();
base::TimeTicks event_time = base::TimeTicks::Now();
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN, 0, 0);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_POINTER_DOWN, 0, 0,
kMaxTwoFingerTapSeparation / 2, 0);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_POINTER_DOWN, 0, 0,
kMaxTwoFingerTapSeparation / 2, 0,
2 * kMaxTwoFingerTapSeparation, 0);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
// Move within slop region, three-finger scroll always happens.
event = ObtainMotionEvent(
event_time, MotionEvent::ACTION_MOVE, 0, scaled_touch_slop / 2,
kMaxTwoFingerTapSeparation / 2, 0, 2 * kMaxTwoFingerTapSeparation, 0);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetReceivedGesture(0).type());
EXPECT_EQ(ET_GESTURE_SCROLL_BEGIN, GetReceivedGesture(1).type());
EXPECT_EQ(ET_GESTURE_SCROLL_UPDATE, GetReceivedGesture(2).type());
EXPECT_EQ(3U, GetReceivedGestureCount());
}
TEST_F(GestureProviderTest, ScrollStartWithSecondaryPointer) {
EnableTwoFingerTap(kMaxTwoFingerTapSeparation, base::TimeDelta());
const float scaled_touch_slop = GetTouchSlop();
base::TimeTicks event_time = base::TimeTicks::Now();
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN, 0, 0);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_POINTER_DOWN, 0, 0,
kMaxTwoFingerTapSeparation / 2, 0);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
// Wait for the two finger tap timeout, and put the second pointer down.
event_time += kOneSecond;
event.set_event_time(event_time);
event.ReleasePointAtIndex(0);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event.MovePoint(0, kMaxTwoFingerTapSeparation / 2, 2 * scaled_touch_slop);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event.ReleasePoint();
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetReceivedGesture(0).type());
EXPECT_EQ(ET_GESTURE_SCROLL_BEGIN, GetReceivedGesture(1).type());
EXPECT_EQ(ET_GESTURE_SCROLL_UPDATE, GetReceivedGesture(2).type());
EXPECT_EQ(ET_GESTURE_SCROLL_END, GetReceivedGesture(3).type());
EXPECT_EQ(4U, GetReceivedGestureCount());
}
TEST_F(GestureProviderTest, NoFlingBeforeExeedingSlopRegion) {
base::TimeTicks event_time = base::TimeTicks::Now();
base::TimeDelta delta_time = kDeltaTimeForFlingSequences;
MockMotionEvent event = ObtainMotionEvent(event_time + delta_time,
MotionEvent::ACTION_DOWN, 0, 0);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time + 2 * delta_time,
MotionEvent::ACTION_POINTER_DOWN, 0, 0, 10, 0);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
// Fast pointer movements within touch slop region.
event = ObtainMotionEvent(event_time + 3 * delta_time,
MotionEvent::ACTION_MOVE, 1, 0, 11, 0);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time + 4 * delta_time,
MotionEvent::ACTION_MOVE, 2, 0, 12, 0);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time + 5 * delta_time,
MotionEvent::ACTION_MOVE, 3, 0, 13, 0);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event.ReleasePointAtIndex(0);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event.ReleasePoint();
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
// No fling must happen even though velocity is greater than the required
// minium.
EXPECT_EQ(1U, GetReceivedGestureCount());
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetReceivedGesture(0).type());
}
TEST_F(GestureProviderTest, LongPressAndTapCancelledWhenScrollBegins) {
base::TimeTicks event_time = base::TimeTicks::Now();
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time + kOneMicrosecond,
MotionEvent::ACTION_MOVE,
kFakeCoordX * 5,
kFakeCoordY * 5);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time + kOneMicrosecond * 2,
MotionEvent::ACTION_MOVE,
kFakeCoordX * 10,
kFakeCoordY * 10);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
const base::TimeDelta long_press_timeout =
GetLongPressTimeout() + GetShowPressTimeout() + kOneMicrosecond;
RunTasksAndWait(long_press_timeout);
// No LONG_TAP as the LONG_PRESS timer is cancelled.
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_LONG_PRESS));
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_LONG_TAP));
}
// Verify that LONG_TAP is triggered after LONG_PRESS followed by an UP.
TEST_F(GestureProviderTest, GestureLongTap) {
base::TimeTicks event_time = base::TimeTicks::Now();
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
const base::TimeDelta long_press_timeout =
GetLongPressTimeout() + GetShowPressTimeout() + kOneMicrosecond;
RunTasksAndWait(long_press_timeout);
EXPECT_EQ(ET_GESTURE_LONG_PRESS, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
EXPECT_EQ(BoundsForSingleMockTouchAtLocation(kFakeCoordX, kFakeCoordY),
GetMostRecentGestureEvent().details.bounding_box_f());
event = ObtainMotionEvent(event_time + kOneSecond, MotionEvent::ACTION_UP);
gesture_provider_->OnTouchEvent(event);
EXPECT_EQ(ET_GESTURE_LONG_TAP, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
EXPECT_EQ(BoundsForSingleMockTouchAtLocation(kFakeCoordX, kFakeCoordY),
GetMostRecentGestureEvent().details.bounding_box_f());
}
TEST_F(GestureProviderTest, GestureLongPressDoesNotPreventScrolling) {
base::TimeTicks event_time = base::TimeTicks::Now();
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
const base::TimeDelta long_press_timeout =
GetLongPressTimeout() + GetShowPressTimeout() + kOneMicrosecond;
RunTasksAndWait(long_press_timeout);
EXPECT_EQ(ET_GESTURE_LONG_PRESS, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
event = ObtainMotionEvent(event_time + long_press_timeout,
MotionEvent::ACTION_MOVE,
kFakeCoordX + 100,
kFakeCoordY + 100);
gesture_provider_->OnTouchEvent(event);
EXPECT_EQ(ET_GESTURE_SCROLL_UPDATE, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN));
event = ObtainMotionEvent(event_time + long_press_timeout,
MotionEvent::ACTION_UP);
gesture_provider_->OnTouchEvent(event);
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_LONG_TAP));
}
TEST_F(GestureProviderTest, NoGestureLongPressDuringDoubleTap) {
base::TimeTicks event_time = base::TimeTicks::Now();
int motion_event_id = 6;
MockMotionEvent event = ObtainMotionEvent(
event_time, MotionEvent::ACTION_DOWN, kFakeCoordX, kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time + kOneMicrosecond,
MotionEvent::ACTION_UP,
kFakeCoordX,
kFakeCoordY);
gesture_provider_->OnTouchEvent(event);
EXPECT_EQ(ET_GESTURE_TAP_UNCONFIRMED, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
event_time += GetValidDoubleTapDelay();
event = ObtainMotionEvent(event_time,
MotionEvent::ACTION_DOWN,
kFakeCoordX,
kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
EXPECT_TRUE(gesture_provider_->IsDoubleTapInProgress());
const base::TimeDelta long_press_timeout =
GetLongPressTimeout() + GetShowPressTimeout() + kOneMicrosecond;
RunTasksAndWait(long_press_timeout);
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_LONG_PRESS));
event = ObtainMotionEvent(event_time + long_press_timeout,
MotionEvent::ACTION_MOVE,
kFakeCoordX + 20,
kFakeCoordY + 20);
event.SetPrimaryPointerId(motion_event_id);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_PINCH_BEGIN, GetMostRecentGestureEventType());
EXPECT_EQ(motion_event_id, GetMostRecentGestureEvent().motion_event_id);
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
EXPECT_TRUE(gesture_provider_->IsDoubleTapInProgress());
event = ObtainMotionEvent(event_time + long_press_timeout + kOneMicrosecond,
MotionEvent::ACTION_UP,
kFakeCoordX,
kFakeCoordY + 1);
event.SetPrimaryPointerId(motion_event_id);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_SCROLL_END, GetMostRecentGestureEventType());
EXPECT_EQ(motion_event_id, GetMostRecentGestureEvent().motion_event_id);
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
EXPECT_FALSE(gesture_provider_->IsDoubleTapInProgress());
}
// Verify that the touch slop region is removed from the first scroll delta to
// avoid a jump when starting to scroll.
TEST_F(GestureProviderTest, TouchSlopRemovedFromScroll) {
const float touch_slop = GetTouchSlop();
const float scroll_delta = 5;
base::TimeTicks event_time = base::TimeTicks::Now();
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time + kOneMicrosecond * 2,
MotionEvent::ACTION_MOVE,
kFakeCoordX,
kFakeCoordY + touch_slop + scroll_delta);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_SCROLL_UPDATE, GetMostRecentGestureEventType());
GestureEventData gesture = GetMostRecentGestureEvent();
EXPECT_EQ(0, gesture.details.scroll_x());
EXPECT_EQ(scroll_delta, gesture.details.scroll_y());
EXPECT_EQ(1, gesture.details.touch_points());
}
// Verify that movement within the touch slop region does not generate a scroll,
// and that the slop region is correct even when using fractional coordinates.
TEST_F(GestureProviderTest, NoScrollWithinTouchSlop) {
const float touch_slop = GetTouchSlop();
const float scale_factor = 2.5f;
const int touch_slop_pixels = static_cast<int>(scale_factor * touch_slop);
base::TimeTicks event_time = base::TimeTicks::Now();
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time + kOneMicrosecond * 2,
MotionEvent::ACTION_MOVE,
kFakeCoordX + touch_slop_pixels / scale_factor,
kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN));
event = ObtainMotionEvent(event_time + kOneMicrosecond * 2,
MotionEvent::ACTION_MOVE,
kFakeCoordX,
kFakeCoordY + touch_slop_pixels / scale_factor);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN));
event = ObtainMotionEvent(event_time + kOneMicrosecond * 2,
MotionEvent::ACTION_MOVE,
kFakeCoordX - touch_slop_pixels / scale_factor,
kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN));
event = ObtainMotionEvent(event_time + kOneMicrosecond * 2,
MotionEvent::ACTION_MOVE,
kFakeCoordX,
kFakeCoordY - touch_slop_pixels / scale_factor);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN));
event =
ObtainMotionEvent(event_time + kOneMicrosecond * 2,
MotionEvent::ACTION_MOVE,
kFakeCoordX,
kFakeCoordY + (touch_slop_pixels + 1.f) / scale_factor);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN));
}
TEST_F(GestureProviderTest, NoDoubleTapWhenTooRapid) {
base::TimeTicks event_time = base::TimeTicks::Now();
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
event = ObtainMotionEvent(event_time + kOneMicrosecond,
MotionEvent::ACTION_UP,
kFakeCoordX,
kFakeCoordY);
gesture_provider_->OnTouchEvent(event);
EXPECT_EQ(ET_GESTURE_TAP_UNCONFIRMED, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
// If the second tap follows the first in too short a time span, no double-tap
// will occur.
event_time += (GetDoubleTapMinTime() / 2);
event = ObtainMotionEvent(event_time,
MotionEvent::ACTION_DOWN,
kFakeCoordX,
kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
event = ObtainMotionEvent(event_time + kOneMicrosecond,
MotionEvent::ACTION_UP,
kFakeCoordX,
kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_UNCONFIRMED, GetMostRecentGestureEventType());
}
TEST_F(GestureProviderTest, NoDoubleTapWhenExplicitlyDisabled) {
// Ensure that double-tap gestures can be disabled.
gesture_provider_->SetDoubleTapSupportForPlatformEnabled(false);
base::TimeTicks event_time = base::TimeTicks::Now();
MockMotionEvent event = ObtainMotionEvent(
event_time, MotionEvent::ACTION_DOWN, kFakeCoordX, kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
event = ObtainMotionEvent(event_time + kOneMicrosecond,
MotionEvent::ACTION_UP,
kFakeCoordX,
kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
event_time += GetValidDoubleTapDelay();
event = ObtainMotionEvent(event_time,
MotionEvent::ACTION_DOWN,
kFakeCoordX,
kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
event = ObtainMotionEvent(event_time + kOneMicrosecond,
MotionEvent::ACTION_UP,
kFakeCoordX,
kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
// Ensure that double-tap gestures can be interrupted.
gesture_provider_->SetDoubleTapSupportForPlatformEnabled(true);
event_time = base::TimeTicks::Now();
event = ObtainMotionEvent(
event_time, MotionEvent::ACTION_DOWN, kFakeCoordX, kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(5U, GetReceivedGestureCount());
event = ObtainMotionEvent(event_time + kOneMicrosecond,
MotionEvent::ACTION_UP,
kFakeCoordX,
kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_UNCONFIRMED, GetMostRecentGestureEventType());
gesture_provider_->SetDoubleTapSupportForPlatformEnabled(false);
EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
// Ensure that double-tap gestures can be resumed.
gesture_provider_->SetDoubleTapSupportForPlatformEnabled(true);
event_time += GetValidDoubleTapDelay();
event = ObtainMotionEvent(event_time,
MotionEvent::ACTION_DOWN,
kFakeCoordX,
kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
event = ObtainMotionEvent(event_time + kOneMicrosecond,
MotionEvent::ACTION_UP,
kFakeCoordX,
kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_UNCONFIRMED, GetMostRecentGestureEventType());
event_time += GetValidDoubleTapDelay();
event = ObtainMotionEvent(event_time,
MotionEvent::ACTION_DOWN,
kFakeCoordX,
kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
event = ObtainMotionEvent(event_time + kOneMicrosecond,
MotionEvent::ACTION_UP,
kFakeCoordX,
kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_DOUBLE_TAP, GetMostRecentGestureEventType());
}
TEST_F(GestureProviderTest, NoDelayedTapWhenDoubleTapSupportToggled) {
gesture_provider_->SetDoubleTapSupportForPlatformEnabled(true);
base::TimeTicks event_time = base::TimeTicks::Now();
MockMotionEvent event = ObtainMotionEvent(
event_time, MotionEvent::ACTION_DOWN, kFakeCoordX, kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
EXPECT_EQ(1U, GetReceivedGestureCount());
event = ObtainMotionEvent(event_time + kOneMicrosecond,
MotionEvent::ACTION_UP,
kFakeCoordX,
kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_UNCONFIRMED, GetMostRecentGestureEventType());
EXPECT_EQ(2U, GetReceivedGestureCount());
// Disabling double-tap during the tap timeout should flush the delayed tap.
gesture_provider_->SetDoubleTapSupportForPlatformEnabled(false);
EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
EXPECT_EQ(3U, GetReceivedGestureCount());
// No further timeout gestures should arrive.
const base::TimeDelta long_press_timeout =
GetLongPressTimeout() + GetShowPressTimeout() + kOneMicrosecond;
RunTasksAndWait(long_press_timeout);
EXPECT_EQ(3U, GetReceivedGestureCount());
}
TEST_F(GestureProviderTest, NoDoubleTapDragZoomWhenDisabledOnPlatform) {
const base::TimeTicks down_time_1 = TimeTicks::Now();
const base::TimeTicks down_time_2 = down_time_1 + GetValidDoubleTapDelay();
gesture_provider_->SetDoubleTapSupportForPlatformEnabled(false);
MockMotionEvent event =
ObtainMotionEvent(down_time_1, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(down_time_1 + kOneMicrosecond,
MotionEvent::ACTION_UP,
kFakeCoordX,
kFakeCoordY);
gesture_provider_->OnTouchEvent(event);
event = ObtainMotionEvent(
down_time_2, MotionEvent::ACTION_DOWN, kFakeCoordX, kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(down_time_2 + kOneMicrosecond,
MotionEvent::ACTION_MOVE,
kFakeCoordX,
kFakeCoordY + 100);
// The move should become a scroll, as doubletap drag zoom is disabled.
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN));
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_PINCH_BEGIN));
event = ObtainMotionEvent(down_time_2 + kOneMicrosecond * 2,
MotionEvent::ACTION_MOVE,
kFakeCoordX,
kFakeCoordY + 200);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_SCROLL_UPDATE, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
EXPECT_EQ(down_time_2 + kOneMicrosecond * 2,
GetMostRecentGestureEvent().time);
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_PINCH_UPDATE));
event = ObtainMotionEvent(down_time_2 + kOneMicrosecond * 3,
MotionEvent::ACTION_UP,
kFakeCoordX,
kFakeCoordY + 200);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_PINCH_END));
}
// Verify that double tap drag zoom feature is not invoked when the gesture
// handler is told to disable double tap gesture detection.
// The second tap sequence should be treated just as the first would be.
TEST_F(GestureProviderTest, NoDoubleTapDragZoomWhenDisabledOnPage) {
const base::TimeTicks down_time_1 = TimeTicks::Now();
const base::TimeTicks down_time_2 = down_time_1 + GetValidDoubleTapDelay();
gesture_provider_->SetDoubleTapSupportForPageEnabled(false);
MockMotionEvent event =
ObtainMotionEvent(down_time_1, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(down_time_1 + kOneMicrosecond,
MotionEvent::ACTION_UP,
kFakeCoordX,
kFakeCoordY);
gesture_provider_->OnTouchEvent(event);
event = ObtainMotionEvent(
down_time_2, MotionEvent::ACTION_DOWN, kFakeCoordX, kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(down_time_2 + kOneMicrosecond,
MotionEvent::ACTION_MOVE,
kFakeCoordX,
kFakeCoordY + 100);
// The move should become a scroll, as double tap drag zoom is disabled.
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN));
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_PINCH_BEGIN));
event = ObtainMotionEvent(down_time_2 + kOneMicrosecond * 2,
MotionEvent::ACTION_MOVE,
kFakeCoordX,
kFakeCoordY + 200);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_SCROLL_UPDATE, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_PINCH_UPDATE));
event = ObtainMotionEvent(down_time_2 + kOneMicrosecond * 3,
MotionEvent::ACTION_UP,
kFakeCoordX,
kFakeCoordY + 200);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_PINCH_END));
}
// Verify that updating double tap support during a double tap drag zoom
// disables double tap detection after the gesture has ended.
TEST_F(GestureProviderTest, FixedPageScaleDuringDoubleTapDragZoom) {
base::TimeTicks down_time_1 = TimeTicks::Now();
base::TimeTicks down_time_2 = down_time_1 + GetValidDoubleTapDelay();
gesture_provider_->SetDoubleTapSupportForPageEnabled(true);
gesture_provider_->SetDoubleTapSupportForPlatformEnabled(true);
// Start a double-tap drag gesture.
MockMotionEvent event =
ObtainMotionEvent(down_time_1, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(down_time_1 + kOneMicrosecond,
MotionEvent::ACTION_UP,
kFakeCoordX,
kFakeCoordY);
gesture_provider_->OnTouchEvent(event);
event = ObtainMotionEvent(
down_time_2, MotionEvent::ACTION_DOWN, kFakeCoordX, kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(down_time_2 + kOneMicrosecond,
MotionEvent::ACTION_MOVE,
kFakeCoordX,
kFakeCoordY + 100);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN));
EXPECT_EQ(ET_GESTURE_PINCH_BEGIN, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
// Simulate setting a fixed page scale (or a mobile viewport);
// this should not disrupt the current double-tap gesture.
gesture_provider_->SetDoubleTapSupportForPageEnabled(false);
// Double tap zoom updates should continue.
event = ObtainMotionEvent(down_time_2 + kOneMicrosecond * 2,
MotionEvent::ACTION_MOVE,
kFakeCoordX,
kFakeCoordY + 200);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_PINCH_UPDATE, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
EXPECT_LT(1.f, GetMostRecentGestureEvent().details.scale());
event = ObtainMotionEvent(down_time_2 + kOneMicrosecond * 3,
MotionEvent::ACTION_UP,
kFakeCoordX,
kFakeCoordY + 200);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_PINCH_END));
EXPECT_EQ(ET_GESTURE_SCROLL_END, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
// The double-tap gesture has finished, but the page scale is fixed.
// The same event sequence should not generate any double tap getsures.
gestures_.clear();
down_time_1 += kOneMicrosecond * 40;
down_time_2 += kOneMicrosecond * 40;
// Start a double-tap drag gesture.
event = ObtainMotionEvent(down_time_1, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(down_time_1 + kOneMicrosecond,
MotionEvent::ACTION_UP,
kFakeCoordX,
kFakeCoordY);
gesture_provider_->OnTouchEvent(event);
event = ObtainMotionEvent(
down_time_2, MotionEvent::ACTION_DOWN, kFakeCoordX, kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(down_time_2 + kOneMicrosecond,
MotionEvent::ACTION_MOVE,
kFakeCoordX,
kFakeCoordY + 100);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN));
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_PINCH_BEGIN));
// Double tap zoom updates should not be sent.
// Instead, the second tap drag becomes a scroll gesture sequence.
event = ObtainMotionEvent(down_time_2 + kOneMicrosecond * 2,
MotionEvent::ACTION_MOVE,
kFakeCoordX,
kFakeCoordY + 200);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SCROLL_UPDATE));
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_PINCH_UPDATE));
event = ObtainMotionEvent(down_time_2 + kOneMicrosecond * 3,
MotionEvent::ACTION_UP,
kFakeCoordX,
kFakeCoordY + 200);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_PINCH_END));
}
// Verify that pinch zoom sends the proper event sequence.
TEST_F(GestureProviderTest, PinchZoom) {
base::TimeTicks event_time = base::TimeTicks::Now();
const float touch_slop = GetTouchSlop();
const float raw_offset_x = 3.2f;
const float raw_offset_y = 4.3f;
int motion_event_id = 6;
gesture_provider_->SetDoubleTapSupportForPageEnabled(false);
gesture_provider_->SetDoubleTapSupportForPlatformEnabled(true);
gesture_provider_->SetMultiTouchZoomSupportEnabled(true);
int secondary_coord_x = kFakeCoordX + 20 * touch_slop;
int secondary_coord_y = kFakeCoordY + 20 * touch_slop;
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
event.SetPrimaryPointerId(motion_event_id);
event.SetRawOffset(raw_offset_x, raw_offset_y);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
EXPECT_EQ(kFakeCoordX, GetMostRecentGestureEvent().x);
EXPECT_EQ(kFakeCoordY, GetMostRecentGestureEvent().y);
EXPECT_EQ(kFakeCoordX + raw_offset_x, GetMostRecentGestureEvent().raw_x);
EXPECT_EQ(kFakeCoordY + raw_offset_y, GetMostRecentGestureEvent().raw_y);
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
EXPECT_EQ(BoundsForSingleMockTouchAtLocation(kFakeCoordX, kFakeCoordY),
GetMostRecentGestureEvent().details.bounding_box_f());
// Toggling double-tap support should not take effect until the next sequence.
gesture_provider_->SetDoubleTapSupportForPageEnabled(true);
event = ObtainMotionEvent(event_time,
MotionEvent::ACTION_POINTER_DOWN,
kFakeCoordX,
kFakeCoordY,
secondary_coord_x,
secondary_coord_y);
event.SetPrimaryPointerId(motion_event_id);
event.SetRawOffset(raw_offset_x, raw_offset_y);
gesture_provider_->OnTouchEvent(event);
EXPECT_EQ(1U, GetReceivedGestureCount());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
EXPECT_EQ(BoundsForSingleMockTouchAtLocation(kFakeCoordX, kFakeCoordY),
GetMostRecentGestureEvent().details.bounding_box_f());
secondary_coord_x += 5 * touch_slop;
secondary_coord_y += 5 * touch_slop;
event = ObtainMotionEvent(event_time,
MotionEvent::ACTION_MOVE,
kFakeCoordX,
kFakeCoordY,
secondary_coord_x,
secondary_coord_y);
event.SetPrimaryPointerId(motion_event_id);
event.SetRawOffset(raw_offset_x, raw_offset_y);
// Toggling double-tap support should not take effect until the next sequence.
gesture_provider_->SetDoubleTapSupportForPageEnabled(false);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(motion_event_id, GetMostRecentGestureEvent().motion_event_id);
EXPECT_EQ(2, GetMostRecentGestureEvent().details.touch_points());
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_PINCH_BEGIN));
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN));
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SCROLL_UPDATE));
EXPECT_EQ((kFakeCoordX + secondary_coord_x) / 2, GetReceivedGesture(3).x);
EXPECT_EQ((kFakeCoordY + secondary_coord_y) / 2, GetReceivedGesture(3).y);
EXPECT_EQ((kFakeCoordX + secondary_coord_x) / 2 + raw_offset_x,
GetReceivedGesture(3).raw_x);
EXPECT_EQ((kFakeCoordY + secondary_coord_y) / 2 + raw_offset_y,
GetReceivedGesture(3).raw_y);
EXPECT_EQ(
gfx::RectF(kFakeCoordX - kMockTouchRadius, kFakeCoordY - kMockTouchRadius,
secondary_coord_x - kFakeCoordX + kMockTouchRadius * 2,
secondary_coord_y - kFakeCoordY + kMockTouchRadius * 2),
GetMostRecentGestureEvent().details.bounding_box_f());
secondary_coord_x += 2 * touch_slop;
secondary_coord_y += 2 * touch_slop;
event = ObtainMotionEvent(event_time,
MotionEvent::ACTION_MOVE,
kFakeCoordX,
kFakeCoordY,
secondary_coord_x,
secondary_coord_y);
event.SetPrimaryPointerId(motion_event_id);
// Toggling double-tap support should not take effect until the next sequence.
gesture_provider_->SetDoubleTapSupportForPageEnabled(true);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(motion_event_id, GetMostRecentGestureEvent().motion_event_id);
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SCROLL_UPDATE));
EXPECT_EQ(ET_GESTURE_PINCH_UPDATE, GetMostRecentGestureEventType());
EXPECT_EQ(2, GetMostRecentGestureEvent().details.touch_points());
EXPECT_LT(1.f, GetMostRecentGestureEvent().details.scale());
EXPECT_EQ(
gfx::RectF(kFakeCoordX - kMockTouchRadius, kFakeCoordY - kMockTouchRadius,
secondary_coord_x - kFakeCoordX + kMockTouchRadius * 2,
secondary_coord_y - kFakeCoordY + kMockTouchRadius * 2),
GetMostRecentGestureEvent().details.bounding_box_f());
event = ObtainMotionEvent(event_time,
MotionEvent::ACTION_POINTER_UP,
kFakeCoordX,
kFakeCoordY,
secondary_coord_x,
secondary_coord_y);
event.SetPrimaryPointerId(motion_event_id);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(motion_event_id, GetMostRecentGestureEvent().motion_event_id);
EXPECT_EQ(ET_GESTURE_PINCH_END, GetMostRecentGestureEventType());
EXPECT_EQ(2, GetMostRecentGestureEvent().details.touch_points());
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_SCROLL_END));
EXPECT_EQ(
gfx::RectF(kFakeCoordX - kMockTouchRadius, kFakeCoordY - kMockTouchRadius,
secondary_coord_x - kFakeCoordX + kMockTouchRadius * 2,
secondary_coord_y - kFakeCoordY + kMockTouchRadius * 2),
GetMostRecentGestureEvent().details.bounding_box_f());
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_UP);
gesture_provider_->OnTouchEvent(event);
EXPECT_EQ(ET_GESTURE_SCROLL_END, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
EXPECT_EQ(
gfx::RectF(kFakeCoordX - kMockTouchRadius, kFakeCoordY - kMockTouchRadius,
kMockTouchRadius * 2, kMockTouchRadius * 2),
GetMostRecentGestureEvent().details.bounding_box_f());
}
// Verify that no accidental pinching occurs if the touch size is large relative
// to the min scaling span when the touch major value is used in scaling.
TEST_F(GestureProviderTest, NoPinchZoomWithFatFinger) {
base::TimeTicks event_time = base::TimeTicks::Now();
const float kFatFingerSize = GetMinScalingSpan() * 3.f;
gesture_provider_->SetDoubleTapSupportForPlatformEnabled(false);
gesture_provider_->SetMultiTouchZoomSupportEnabled(true);
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
EXPECT_EQ(1U, GetReceivedGestureCount());
event = ObtainMotionEvent(event_time + kOneSecond,
MotionEvent::ACTION_MOVE);
event.SetTouchMajor(0.1f);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(1U, GetReceivedGestureCount());
event = ObtainMotionEvent(event_time + kOneSecond * 2,
MotionEvent::ACTION_MOVE,
kFakeCoordX + 1.f,
kFakeCoordY);
event.SetTouchMajor(1.f);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(1U, GetReceivedGestureCount());
event = ObtainMotionEvent(event_time + kOneSecond * 3,
MotionEvent::ACTION_MOVE);
event.SetTouchMajor(kFatFingerSize * 3.5f);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(1U, GetReceivedGestureCount());
event = ObtainMotionEvent(event_time + kOneSecond * 4,
MotionEvent::ACTION_MOVE);
event.SetTouchMajor(kFatFingerSize * 5.f);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(1U, GetReceivedGestureCount());
event = ObtainMotionEvent(event_time + kOneSecond * 4,
MotionEvent::ACTION_MOVE,
kFakeCoordX + 50.f,
kFakeCoordY - 25.f);
event.SetTouchMajor(kFatFingerSize * 10.f);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_PINCH_BEGIN));
event = ObtainMotionEvent(event_time + kOneSecond * 4,
MotionEvent::ACTION_MOVE,
kFakeCoordX + 100.f,
kFakeCoordY - 50.f);
event.SetTouchMajor(kFatFingerSize * 5.f);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_PINCH_BEGIN));
}
// Verify that multi-finger swipe sends the proper event sequence.
TEST_F(GestureProviderTest, MultiFingerSwipe) {
EnableSwipe();
gesture_provider_->SetMultiTouchZoomSupportEnabled(false);
const float min_swipe_velocity = GetMinSwipeVelocity();
// One finger - swipe right
OneFingerSwipe(2 * min_swipe_velocity, 0);
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SWIPE));
EXPECT_TRUE(GetMostRecentGestureEvent().details.swipe_right());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
ResetGestureDetection();
// One finger - swipe left
OneFingerSwipe(-2 * min_swipe_velocity, 0);
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SWIPE));
EXPECT_TRUE(GetMostRecentGestureEvent().details.swipe_left());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
ResetGestureDetection();
// One finger - swipe down
OneFingerSwipe(0, 2 * min_swipe_velocity);
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SWIPE));
EXPECT_TRUE(GetMostRecentGestureEvent().details.swipe_down());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
ResetGestureDetection();
// One finger - swipe up
OneFingerSwipe(0, -2 * min_swipe_velocity);
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SWIPE));
EXPECT_TRUE(GetMostRecentGestureEvent().details.swipe_up());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
ResetGestureDetection();
// Two fingers
// Swipe right.
TwoFingerSwipe(min_swipe_velocity * 2, 0, min_swipe_velocity * 2, 0);
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SWIPE));
EXPECT_TRUE(GetMostRecentGestureEvent().details.swipe_right());
EXPECT_EQ(2, GetMostRecentGestureEvent().details.touch_points());
ResetGestureDetection();
// Swipe left.
TwoFingerSwipe(-min_swipe_velocity * 2, 0, -min_swipe_velocity * 2, 0);
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SWIPE));
EXPECT_TRUE(GetMostRecentGestureEvent().details.swipe_left());
EXPECT_EQ(2, GetMostRecentGestureEvent().details.touch_points());
ResetGestureDetection();
// No swipe with different touch directions.
TwoFingerSwipe(min_swipe_velocity * 2, 0, -min_swipe_velocity * 2, 0);
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_SWIPE));
ResetGestureDetection();
// No swipe without a dominant direction.
TwoFingerSwipe(min_swipe_velocity * 2,
min_swipe_velocity * 2,
min_swipe_velocity * 2,
min_swipe_velocity * 2);
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_SWIPE));
ResetGestureDetection();
// Swipe down with non-zero velocities on both axes and dominant direction.
TwoFingerSwipe(-min_swipe_velocity,
min_swipe_velocity * 4,
-min_swipe_velocity,
min_swipe_velocity * 4);
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SWIPE));
EXPECT_TRUE(GetMostRecentGestureEvent().details.swipe_down());
EXPECT_FALSE(GetMostRecentGestureEvent().details.swipe_left());
EXPECT_EQ(2, GetMostRecentGestureEvent().details.touch_points());
ResetGestureDetection();
// Swipe up with non-zero velocities on both axes.
TwoFingerSwipe(min_swipe_velocity,
-min_swipe_velocity * 4,
min_swipe_velocity,
-min_swipe_velocity * 4);
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SWIPE));
EXPECT_TRUE(GetMostRecentGestureEvent().details.swipe_up());
EXPECT_FALSE(GetMostRecentGestureEvent().details.swipe_right());
EXPECT_EQ(2, GetMostRecentGestureEvent().details.touch_points());
ResetGestureDetection();
// No swipe without sufficient velocity.
TwoFingerSwipe(min_swipe_velocity / 2, 0, 0, 0);
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_SWIPE));
ResetGestureDetection();
// Swipe up with one small and one medium velocity in slightly different but
// not opposing directions.
TwoFingerSwipe(min_swipe_velocity / 2,
min_swipe_velocity / 2,
0,
min_swipe_velocity * 2);
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SWIPE));
EXPECT_TRUE(GetMostRecentGestureEvent().details.swipe_down());
EXPECT_FALSE(GetMostRecentGestureEvent().details.swipe_right());
EXPECT_EQ(2, GetMostRecentGestureEvent().details.touch_points());
ResetGestureDetection();
// No swipe in orthogonal directions.
TwoFingerSwipe(min_swipe_velocity * 2, 0, 0, min_swipe_velocity * 7);
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_SWIPE));
ResetGestureDetection();
// Three finger swipe in same directions.
ThreeFingerSwipe(min_swipe_velocity * 2,
0,
min_swipe_velocity * 3,
0,
min_swipe_velocity * 4,
0);
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SWIPE));
EXPECT_TRUE(GetMostRecentGestureEvent().details.swipe_right());
EXPECT_EQ(3, GetMostRecentGestureEvent().details.touch_points());
ResetGestureDetection();
// No three finger swipe in different directions.
ThreeFingerSwipe(min_swipe_velocity * 2,
0,
0,
min_swipe_velocity * 3,
min_swipe_velocity * 4,
0);
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_SWIPE));
}
// Verify that the timer of LONG_PRESS will be cancelled when scrolling begins
// so LONG_PRESS and LONG_TAP won't be triggered.
TEST_F(GestureProviderTest, GesturesCancelledAfterLongPressCausesLostFocus) {
base::TimeTicks event_time = base::TimeTicks::Now();
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
const base::TimeDelta long_press_timeout =
GetLongPressTimeout() + GetShowPressTimeout() + kOneMicrosecond;
RunTasksAndWait(long_press_timeout);
EXPECT_EQ(ET_GESTURE_LONG_PRESS, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
EXPECT_TRUE(CancelActiveTouchSequence());
EXPECT_FALSE(HasDownEvent());
event = ObtainMotionEvent(event_time + long_press_timeout,
MotionEvent::ACTION_UP);
gesture_provider_->OnTouchEvent(event);
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_LONG_TAP));
}
// Verify that inserting a touch cancel event will trigger proper touch and
// gesture sequence cancellation.
TEST_F(GestureProviderTest, CancelActiveTouchSequence) {
base::TimeTicks event_time = base::TimeTicks::Now();
int motion_event_id = 6;
EXPECT_FALSE(CancelActiveTouchSequence());
EXPECT_EQ(0U, GetReceivedGestureCount());
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
event.SetPrimaryPointerId(motion_event_id);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
EXPECT_EQ(motion_event_id, GetMostRecentGestureEvent().motion_event_id);
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
ASSERT_TRUE(CancelActiveTouchSequence());
EXPECT_FALSE(HasDownEvent());
// Subsequent MotionEvent's are dropped until ACTION_DOWN.
event = ObtainMotionEvent(event_time + kOneMicrosecond,
MotionEvent::ACTION_MOVE);
EXPECT_FALSE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time + kOneMicrosecond * 2,
MotionEvent::ACTION_UP);
EXPECT_FALSE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time + kOneMicrosecond * 3,
MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
}
TEST_F(GestureProviderTest, DoubleTapDragZoomCancelledOnSecondaryPointerDown) {
const base::TimeTicks down_time_1 = TimeTicks::Now();
const base::TimeTicks down_time_2 = down_time_1 + GetValidDoubleTapDelay();
gesture_provider_->SetDoubleTapSupportForPlatformEnabled(true);
MockMotionEvent event =
ObtainMotionEvent(down_time_1, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
event =
ObtainMotionEvent(down_time_1 + kOneMicrosecond, MotionEvent::ACTION_UP);
gesture_provider_->OnTouchEvent(event);
EXPECT_EQ(ET_GESTURE_TAP_UNCONFIRMED, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
event = ObtainMotionEvent(down_time_2, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
event = ObtainMotionEvent(down_time_2 + kOneMicrosecond,
MotionEvent::ACTION_MOVE,
kFakeCoordX,
kFakeCoordY - 30);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN));
EXPECT_EQ(ET_GESTURE_PINCH_BEGIN, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
event = ObtainMotionEvent(down_time_2 + kOneMicrosecond * 2,
MotionEvent::ACTION_POINTER_DOWN,
kFakeCoordX,
kFakeCoordY - 30,
kFakeCoordX + 50,
kFakeCoordY + 50);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_PINCH_END, GetMostRecentGestureEventType());
EXPECT_EQ(2, GetMostRecentGestureEvent().details.touch_points());
const size_t gesture_count = GetReceivedGestureCount();
event = ObtainMotionEvent(down_time_2 + kOneMicrosecond * 3,
MotionEvent::ACTION_POINTER_UP,
kFakeCoordX,
kFakeCoordY - 30,
kFakeCoordX + 50,
kFakeCoordY + 50);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(gesture_count, GetReceivedGestureCount());
event = ObtainMotionEvent(down_time_2 + kOneSecond,
MotionEvent::ACTION_UP);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(gesture_count + 1, GetReceivedGestureCount());
EXPECT_EQ(ET_GESTURE_SCROLL_END, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
}
// Verify that gesture begin and gesture end events are dispatched correctly.
TEST_F(GestureProviderTest, GestureBeginAndEnd) {
EnableBeginEndTypes();
base::TimeTicks event_time = base::TimeTicks::Now();
const float raw_offset_x = 7.5f;
const float raw_offset_y = 5.7f;
EXPECT_EQ(0U, GetReceivedGestureCount());
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN, 1, 1);
event.SetRawOffset(raw_offset_x, raw_offset_y);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_BEGIN, GetReceivedGesture(0).type());
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
EXPECT_EQ(2U, GetReceivedGestureCount());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
EXPECT_EQ(1, GetMostRecentGestureEvent().x);
EXPECT_EQ(1, GetMostRecentGestureEvent().y);
EXPECT_EQ(1 + raw_offset_x, GetMostRecentGestureEvent().raw_x);
EXPECT_EQ(1 + raw_offset_y, GetMostRecentGestureEvent().raw_y);
EXPECT_EQ(gfx::RectF(1 - kMockTouchRadius, 1 - kMockTouchRadius,
kMockTouchRadius * 2, kMockTouchRadius * 2),
GetMostRecentGestureEvent().details.bounding_box_f());
event = ObtainMotionEvent(
event_time, MotionEvent::ACTION_POINTER_DOWN, 1, 1, 2, 2);
event.SetRawOffset(raw_offset_x, raw_offset_y);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_BEGIN, GetMostRecentGestureEventType());
EXPECT_EQ(3U, GetReceivedGestureCount());
EXPECT_EQ(2, GetMostRecentGestureEvent().details.touch_points());
EXPECT_EQ(2, GetMostRecentGestureEvent().x);
EXPECT_EQ(2, GetMostRecentGestureEvent().y);
EXPECT_EQ(2 + raw_offset_x, GetMostRecentGestureEvent().raw_x);
EXPECT_EQ(2 + raw_offset_y, GetMostRecentGestureEvent().raw_y);
event = ObtainMotionEvent(
event_time, MotionEvent::ACTION_POINTER_DOWN, 1, 1, 2, 2, 3, 3);
event.SetRawOffset(raw_offset_x, raw_offset_y);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_BEGIN, GetMostRecentGestureEventType());
EXPECT_EQ(4U, GetReceivedGestureCount());
EXPECT_EQ(3, GetMostRecentGestureEvent().details.touch_points());
EXPECT_EQ(3, GetMostRecentGestureEvent().x);
EXPECT_EQ(3, GetMostRecentGestureEvent().y);
EXPECT_EQ(3 + raw_offset_x, GetMostRecentGestureEvent().raw_x);
EXPECT_EQ(3 + raw_offset_y, GetMostRecentGestureEvent().raw_y);
event = ObtainMotionEvent(
event_time, MotionEvent::ACTION_POINTER_UP, 1, 1, 2, 2, 3, 3);
event.SetRawOffset(raw_offset_x, raw_offset_y);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_END, GetMostRecentGestureEventType());
EXPECT_EQ(5U, GetReceivedGestureCount());
EXPECT_EQ(3, GetMostRecentGestureEvent().details.touch_points());
EXPECT_EQ(1, GetMostRecentGestureEvent().x);
EXPECT_EQ(1, GetMostRecentGestureEvent().y);
EXPECT_EQ(1 + raw_offset_x, GetMostRecentGestureEvent().raw_x);
EXPECT_EQ(1 + raw_offset_y, GetMostRecentGestureEvent().raw_y);
event = ObtainMotionEvent(
event_time, MotionEvent::ACTION_POINTER_DOWN, 2, 2, 3, 3, 4, 4);
event.SetRawOffset(raw_offset_x, raw_offset_y);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_BEGIN, GetMostRecentGestureEventType());
EXPECT_EQ(6U, GetReceivedGestureCount());
EXPECT_EQ(3, GetMostRecentGestureEvent().details.touch_points());
EXPECT_EQ(4, GetMostRecentGestureEvent().x);
EXPECT_EQ(4, GetMostRecentGestureEvent().y);
EXPECT_EQ(4 + raw_offset_x, GetMostRecentGestureEvent().raw_x);
EXPECT_EQ(4 + raw_offset_y, GetMostRecentGestureEvent().raw_y);
event = ObtainMotionEvent(
event_time, MotionEvent::ACTION_POINTER_UP, 2, 2, 3, 3, 4, 4);
event.SetRawOffset(raw_offset_x, raw_offset_y);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_END, GetMostRecentGestureEventType());
EXPECT_EQ(7U, GetReceivedGestureCount());
EXPECT_EQ(3, GetMostRecentGestureEvent().details.touch_points());
EXPECT_EQ(2, GetMostRecentGestureEvent().x);
EXPECT_EQ(2, GetMostRecentGestureEvent().y);
EXPECT_EQ(2 + raw_offset_x, GetMostRecentGestureEvent().raw_x);
EXPECT_EQ(2 + raw_offset_y, GetMostRecentGestureEvent().raw_y);
event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_POINTER_UP, 3, 3, 4, 4);
event.SetRawOffset(raw_offset_x, raw_offset_y);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_END, GetMostRecentGestureEventType());
EXPECT_EQ(8U, GetReceivedGestureCount());
EXPECT_EQ(2, GetMostRecentGestureEvent().details.touch_points());
EXPECT_EQ(3, GetMostRecentGestureEvent().x);
EXPECT_EQ(3, GetMostRecentGestureEvent().y);
EXPECT_EQ(3 + raw_offset_x, GetMostRecentGestureEvent().raw_x);
EXPECT_EQ(3 + raw_offset_y, GetMostRecentGestureEvent().raw_y);
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_UP, 4, 4);
event.SetRawOffset(raw_offset_x, raw_offset_y);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_END, GetMostRecentGestureEventType());
EXPECT_EQ(9U, GetReceivedGestureCount());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
EXPECT_EQ(4, GetMostRecentGestureEvent().x);
EXPECT_EQ(4, GetMostRecentGestureEvent().y);
EXPECT_EQ(4 + raw_offset_x, GetMostRecentGestureEvent().raw_x);
EXPECT_EQ(4 + raw_offset_y, GetMostRecentGestureEvent().raw_y);
}
// Verify that gesture begin and gesture end events are dispatched correctly
// when an ACTION_CANCEL is received.
TEST_F(GestureProviderTest, GestureBeginAndEndOnCancel) {
EnableBeginEndTypes();
base::TimeTicks event_time = base::TimeTicks::Now();
EXPECT_EQ(0U, GetReceivedGestureCount());
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN, 1, 1);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_BEGIN, GetReceivedGesture(0).type());
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
EXPECT_EQ(2U, GetReceivedGestureCount());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
EXPECT_EQ(gfx::RectF(1 - kMockTouchRadius, 1 - kMockTouchRadius,
kMockTouchRadius * 2, kMockTouchRadius * 2),
GetMostRecentGestureEvent().details.bounding_box_f());
EXPECT_EQ(1, GetMostRecentGestureEvent().x);
EXPECT_EQ(1, GetMostRecentGestureEvent().y);
event = ObtainMotionEvent(
event_time, MotionEvent::ACTION_POINTER_DOWN, 1, 1, 2, 2);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_BEGIN, GetMostRecentGestureEventType());
EXPECT_EQ(3U, GetReceivedGestureCount());
EXPECT_EQ(2, GetMostRecentGestureEvent().details.touch_points());
EXPECT_EQ(2, GetMostRecentGestureEvent().x);
EXPECT_EQ(2, GetMostRecentGestureEvent().y);
event = ObtainMotionEvent(
event_time, MotionEvent::ACTION_POINTER_DOWN, 1, 1, 2, 2, 3, 3);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_BEGIN, GetMostRecentGestureEventType());
EXPECT_EQ(4U, GetReceivedGestureCount());
EXPECT_EQ(3, GetMostRecentGestureEvent().details.touch_points());
EXPECT_EQ(3, GetMostRecentGestureEvent().x);
EXPECT_EQ(3, GetMostRecentGestureEvent().y);
event = ObtainMotionEvent(
event_time, MotionEvent::ACTION_CANCEL, 1, 1, 2, 2, 3, 3);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(5U, GetReceivedGestureCount());
EXPECT_EQ(3, GetReceivedGesture(4).details.touch_points());
EXPECT_EQ(ET_GESTURE_END, GetReceivedGesture(4).type());
EXPECT_EQ(1, GetMostRecentGestureEvent().x);
EXPECT_EQ(1, GetMostRecentGestureEvent().y);
event = ObtainMotionEvent(
event_time, MotionEvent::ACTION_CANCEL, 1, 1, 3, 3);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(6U, GetReceivedGestureCount());
EXPECT_EQ(2, GetMostRecentGestureEvent().details.touch_points());
EXPECT_EQ(ET_GESTURE_END, GetMostRecentGestureEvent().type());
EXPECT_EQ(1, GetMostRecentGestureEvent().x);
EXPECT_EQ(1, GetMostRecentGestureEvent().y);
event = ObtainMotionEvent(
event_time, MotionEvent::ACTION_CANCEL, 3, 3);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
EXPECT_EQ(ET_GESTURE_END, GetMostRecentGestureEvent().type());
EXPECT_EQ(3, GetMostRecentGestureEvent().x);
EXPECT_EQ(3, GetMostRecentGestureEvent().y);
}
// Test a simple two finger tap
TEST_F(GestureProviderTest, TwoFingerTap) {
// The time between ACTION_POINTER_DOWN and ACTION_POINTER_UP must be <= the
// two finger tap delay.
EnableTwoFingerTap(kMaxTwoFingerTapSeparation, base::TimeDelta());
const float scaled_touch_slop = GetTouchSlop();
base::TimeTicks event_time = base::TimeTicks::Now();
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN, 0, 0);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time,
MotionEvent::ACTION_MOVE,
0,
scaled_touch_slop / 2);
event = ObtainMotionEvent(event_time,
MotionEvent::ACTION_POINTER_DOWN,
0,
0,
kMaxTwoFingerTapSeparation / 2,
0);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event =
ObtainMotionEvent(event_time,
MotionEvent::ACTION_MOVE,
0,
-scaled_touch_slop / 2,
kMaxTwoFingerTapSeparation / 2 + scaled_touch_slop / 2,
0);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time,
MotionEvent::ACTION_POINTER_UP,
0,
0,
kMaxTwoFingerTapSeparation,
0);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetReceivedGesture(0).type());
EXPECT_EQ(ET_GESTURE_TWO_FINGER_TAP, GetReceivedGesture(1).type());
EXPECT_EQ(2U, GetReceivedGestureCount());
EXPECT_EQ(kMockTouchRadius * 2,
GetReceivedGesture(1).details.first_finger_width());
EXPECT_EQ(kMockTouchRadius * 2,
GetReceivedGesture(1).details.first_finger_height());
}
// Test preventing a two finger tap via finger movement.
TEST_F(GestureProviderTest, TwoFingerTapCancelledByFingerMovement) {
EnableTwoFingerTap(kMaxTwoFingerTapSeparation, base::TimeDelta());
const float scaled_touch_slop = GetTouchSlop();
base::TimeTicks event_time = base::TimeTicks::Now();
MockMotionEvent event = ObtainMotionEvent(
event_time, MotionEvent::ACTION_DOWN, kFakeCoordX, kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time,
MotionEvent::ACTION_POINTER_DOWN,
kFakeCoordX,
kFakeCoordY,
kFakeCoordX,
kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(
event_time, MotionEvent::ACTION_MOVE, kFakeCoordX, kFakeCoordY,
kFakeCoordX + 2 * scaled_touch_slop + 2, kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time,
MotionEvent::ACTION_POINTER_UP,
kFakeCoordX,
kFakeCoordY,
kFakeCoordX,
kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetReceivedGesture(0).type());
EXPECT_EQ(ET_GESTURE_SCROLL_BEGIN, GetReceivedGesture(1).type());
EXPECT_EQ(ET_GESTURE_SCROLL_UPDATE, GetReceivedGesture(2).type());
// d_x = 2 * scaled_touch_slop + 2,
// d_focus_x = scaled_touch_slop + 1,
// touch_slop / event.GetPointerCount() is deducted from first scroll,
// scroll_x = scaled_touch_slop + 1 - scaled_touch_slop / 2
EXPECT_FLOAT_EQ(scaled_touch_slop / 2 + 1,
GetReceivedGesture(2).details.scroll_x());
EXPECT_EQ(0, GetReceivedGesture(2).details.scroll_y());
EXPECT_EQ(3U, GetReceivedGestureCount());
}
// Test preventing a two finger tap by waiting too long before releasing the
// secondary pointer.
TEST_F(GestureProviderTest, TwoFingerTapCancelledByDelay) {
base::TimeDelta two_finger_tap_timeout = kOneSecond;
EnableTwoFingerTap(kMaxTwoFingerTapSeparation, two_finger_tap_timeout);
base::TimeTicks event_time = base::TimeTicks::Now();
MockMotionEvent event = ObtainMotionEvent(
event_time, MotionEvent::ACTION_DOWN, kFakeCoordX, kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time,
MotionEvent::ACTION_MOVE,
kFakeCoordX,
kFakeCoordY);
event = ObtainMotionEvent(event_time,
MotionEvent::ACTION_POINTER_DOWN,
kFakeCoordX,
kFakeCoordY,
kFakeCoordX + kMaxTwoFingerTapSeparation / 2,
kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time + kOneSecond + kOneMicrosecond,
MotionEvent::ACTION_POINTER_UP,
kFakeCoordX,
kFakeCoordY,
kFakeCoordX + kMaxTwoFingerTapSeparation / 2,
kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetReceivedGesture(0).type());
EXPECT_EQ(1U, GetReceivedGestureCount());
}
// Test preventing a two finger tap by pressing the secondary pointer too far
// from the first
TEST_F(GestureProviderTest, TwoFingerTapCancelledByDistanceBetweenPointers) {
EnableTwoFingerTap(kMaxTwoFingerTapSeparation, base::TimeDelta());
base::TimeTicks event_time = base::TimeTicks::Now();
MockMotionEvent event = ObtainMotionEvent(
event_time, MotionEvent::ACTION_DOWN, kFakeCoordX, kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time,
MotionEvent::ACTION_POINTER_DOWN,
kFakeCoordX,
kFakeCoordY,
kFakeCoordX + kMaxTwoFingerTapSeparation,
kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time,
MotionEvent::ACTION_POINTER_UP,
kFakeCoordX,
kFakeCoordY,
kFakeCoordX + kMaxTwoFingerTapSeparation,
kFakeCoordY);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetReceivedGesture(0).type());
EXPECT_EQ(1U, GetReceivedGestureCount());
}
// Verify that pinch zoom only sends updates which exceed the
// min_pinch_update_span_delta.
TEST_F(GestureProviderTest, PinchZoomWithThreshold) {
const float kMinPinchUpdateDistance = 5;
base::TimeTicks event_time = base::TimeTicks::Now();
const float touch_slop = GetTouchSlop();
SetMinPinchUpdateSpanDelta(kMinPinchUpdateDistance);
gesture_provider_->SetDoubleTapSupportForPageEnabled(false);
gesture_provider_->SetDoubleTapSupportForPlatformEnabled(true);
gesture_provider_->SetMultiTouchZoomSupportEnabled(true);
int secondary_coord_x = kFakeCoordX + 20 * touch_slop;
int secondary_coord_y = kFakeCoordY + 20 * touch_slop;
// First finger down.
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
// Second finger down.
event = ObtainMotionEvent(event_time,
MotionEvent::ACTION_POINTER_DOWN,
kFakeCoordX,
kFakeCoordY,
secondary_coord_x,
secondary_coord_y);
gesture_provider_->OnTouchEvent(event);
EXPECT_EQ(1U, GetReceivedGestureCount());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
// Move second finger.
secondary_coord_x += 5 * touch_slop;
secondary_coord_y += 5 * touch_slop;
event = ObtainMotionEvent(event_time,
MotionEvent::ACTION_MOVE,
kFakeCoordX,
kFakeCoordY,
secondary_coord_x,
secondary_coord_y);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(2, GetMostRecentGestureEvent().details.touch_points());
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_PINCH_BEGIN));
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_PINCH_UPDATE));
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN));
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SCROLL_UPDATE));
// Small move, shouldn't trigger pinch.
event = ObtainMotionEvent(event_time,
MotionEvent::ACTION_MOVE,
kFakeCoordX,
kFakeCoordY,
secondary_coord_x + kMinPinchUpdateDistance,
secondary_coord_y);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_PINCH_UPDATE));
EXPECT_EQ(2, GetMostRecentGestureEvent().details.touch_points());
// Small move, but combined with the previous move, should trigger pinch. We
// need to overshoot kMinPinchUpdateDistance by a fair bit, as the span
// calculation factors in touch radius.
const float kOvershootMinPinchUpdateDistance = 3;
event = ObtainMotionEvent(event_time,
MotionEvent::ACTION_MOVE,
kFakeCoordX,
kFakeCoordY,
secondary_coord_x + kMinPinchUpdateDistance +
kOvershootMinPinchUpdateDistance,
secondary_coord_y);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_PINCH_UPDATE));
EXPECT_EQ(2, GetMostRecentGestureEvent().details.touch_points());
}
// Verify that the min gesture bound setting is honored.
TEST_F(GestureProviderTest, MinGestureBoundsLength) {
const float kMinGestureBoundsLength = 10.f * kMockTouchRadius;
SetMinMaxGestureBoundsLength(kMinGestureBoundsLength, 0.f);
gesture_provider_->SetDoubleTapSupportForPlatformEnabled(false);
base::TimeTicks event_time = base::TimeTicks::Now();
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
EXPECT_EQ(kMinGestureBoundsLength,
GetMostRecentGestureEvent().details.bounding_box_f().width());
EXPECT_EQ(kMinGestureBoundsLength,
GetMostRecentGestureEvent().details.bounding_box_f().height());
event =
ObtainMotionEvent(event_time + kOneMicrosecond, MotionEvent::ACTION_UP);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
EXPECT_EQ(kMinGestureBoundsLength,
GetMostRecentGestureEvent().details.bounding_box_f().width());
EXPECT_EQ(kMinGestureBoundsLength,
GetMostRecentGestureEvent().details.bounding_box_f().height());
}
TEST_F(GestureProviderTest, MaxGestureBoundsLength) {
const float kMaxGestureBoundsLength = kMockTouchRadius / 10.f;
SetMinMaxGestureBoundsLength(0.f, kMaxGestureBoundsLength);
gesture_provider_->SetDoubleTapSupportForPlatformEnabled(false);
base::TimeTicks event_time = base::TimeTicks::Now();
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
EXPECT_EQ(kMaxGestureBoundsLength,
GetMostRecentGestureEvent().details.bounding_box_f().width());
EXPECT_EQ(kMaxGestureBoundsLength,
GetMostRecentGestureEvent().details.bounding_box_f().height());
event =
ObtainMotionEvent(event_time + kOneMicrosecond, MotionEvent::ACTION_UP);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
EXPECT_EQ(kMaxGestureBoundsLength,
GetMostRecentGestureEvent().details.bounding_box_f().width());
EXPECT_EQ(kMaxGestureBoundsLength,
GetMostRecentGestureEvent().details.bounding_box_f().height());
}
TEST_F(GestureProviderTest, ZeroRadiusBoundingBox) {
base::TimeTicks event_time = base::TimeTicks::Now();
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN, 10, 20);
event.SetTouchMajor(0);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(gfx::RectF(10, 20, 0, 0),
GetMostRecentGestureEvent().details.bounding_box_f());
event = ObtainMotionEvent(
event_time, MotionEvent::ACTION_POINTER_DOWN, 10, 20, 110, 120);
event.SetTouchMajor(0);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(
event_time, MotionEvent::ACTION_MOVE, 10, 20, 110, 150);
event.SetTouchMajor(0);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(gfx::RectF(10, 20, 100, 130),
GetMostRecentGestureEvent().details.bounding_box_f());
}
// Verify that the min/max gesture bound settings are not applied to stylus
// or mouse-derived MotionEvents.
TEST_F(GestureProviderTest, NoMinOrMaxGestureBoundsLengthWithStylusOrMouse) {
const float kMinGestureBoundsLength = 5.f * kMockTouchRadius;
const float kMaxGestureBoundsLength = 10.f * kMockTouchRadius;
SetMinMaxGestureBoundsLength(kMinGestureBoundsLength,
kMaxGestureBoundsLength);
gesture_provider_->SetDoubleTapSupportForPlatformEnabled(false);
base::TimeTicks event_time = base::TimeTicks::Now();
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
event.SetTouchMajor(0);
event.SetToolType(0, MotionEvent::TOOL_TYPE_MOUSE);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
EXPECT_EQ(MotionEvent::TOOL_TYPE_MOUSE,
GetMostRecentGestureEvent().primary_tool_type);
EXPECT_EQ(0.f, GetMostRecentGestureEvent().details.bounding_box_f().width());
EXPECT_EQ(0.f, GetMostRecentGestureEvent().details.bounding_box_f().height());
event =
ObtainMotionEvent(event_time + kOneMicrosecond, MotionEvent::ACTION_UP);
event.SetTouchMajor(1);
event.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
EXPECT_EQ(MotionEvent::TOOL_TYPE_STYLUS,
GetMostRecentGestureEvent().primary_tool_type);
EXPECT_EQ(0, GetMostRecentGestureEvent().details.bounding_box_f().width());
EXPECT_EQ(0, GetMostRecentGestureEvent().details.bounding_box_f().height());
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
event.SetTouchMajor(2.f * kMaxGestureBoundsLength);
event.SetToolType(0, MotionEvent::TOOL_TYPE_MOUSE);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(MotionEvent::TOOL_TYPE_MOUSE,
GetMostRecentGestureEvent().primary_tool_type);
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
EXPECT_EQ(2.f * kMaxGestureBoundsLength,
GetMostRecentGestureEvent().details.bounding_box_f().width());
EXPECT_EQ(2.f * kMaxGestureBoundsLength,
GetMostRecentGestureEvent().details.bounding_box_f().height());
event =
ObtainMotionEvent(event_time + kOneMicrosecond, MotionEvent::ACTION_UP);
event.SetTouchMajor(2.f * kMaxGestureBoundsLength);
event.SetToolType(0, MotionEvent::TOOL_TYPE_ERASER);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
EXPECT_EQ(MotionEvent::TOOL_TYPE_ERASER,
GetMostRecentGestureEvent().primary_tool_type);
EXPECT_EQ(2.f * kMaxGestureBoundsLength,
GetMostRecentGestureEvent().details.bounding_box_f().width());
EXPECT_EQ(2.f * kMaxGestureBoundsLength,
GetMostRecentGestureEvent().details.bounding_box_f().height());
}
// Test the bounding box for show press and tap gestures.
TEST_F(GestureProviderTest, BoundingBoxForShowPressAndTapGesture) {
base::TimeTicks event_time = base::TimeTicks::Now();
gesture_provider_->SetDoubleTapSupportForPlatformEnabled(false);
base::TimeDelta showpress_timeout = kOneMicrosecond;
base::TimeDelta longpress_timeout = kOneSecond;
SetShowPressAndLongPressTimeout(showpress_timeout, longpress_timeout);
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN, 10, 10);
event.SetTouchMajor(10);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP_DOWN, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
EXPECT_EQ(gfx::RectF(5, 5, 10, 10),
GetMostRecentGestureEvent().details.bounding_box_f());
event = ObtainMotionEvent(
event_time + kOneMicrosecond, MotionEvent::ACTION_MOVE, 11, 9);
event.SetTouchMajor(20);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(
event_time + kOneMicrosecond, MotionEvent::ACTION_MOVE, 8, 11);
event.SetTouchMajor(10);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
RunTasksAndWait(showpress_timeout + kOneMicrosecond);
EXPECT_EQ(ET_GESTURE_SHOW_PRESS, GetMostRecentGestureEventType());
EXPECT_EQ(gfx::RectF(0, 0, 20, 20),
GetMostRecentGestureEvent().details.bounding_box_f());
event =
ObtainMotionEvent(event_time + kOneMicrosecond, MotionEvent::ACTION_UP);
event.SetTouchMajor(30);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.tap_count());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points());
EXPECT_EQ(gfx::RectF(0, 0, 20, 20),
GetMostRecentGestureEvent().details.bounding_box_f());
}
TEST_F(GestureProviderTest, SingleTapRepeat) {
SetSingleTapRepeatInterval(3);
gesture_provider_->SetDoubleTapSupportForPlatformEnabled(false);
base::TimeTicks event_time = base::TimeTicks::Now();
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_UP);
gesture_provider_->OnTouchEvent(event);
EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.tap_count());
// A second tap after the double-tap timeout window will not increment
// the tap count.
event_time += GetDoubleTapTimeout() + kOneMicrosecond;
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_UP);
gesture_provider_->OnTouchEvent(event);
EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.tap_count());
// A secondary tap within the tap repeat period should increment
// the tap count.
event_time += GetValidDoubleTapDelay();
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_UP);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
EXPECT_EQ(2, GetMostRecentGestureEvent().details.tap_count());
// A secondary tap within the tap repeat location threshold should increment
// the tap count.
event_time += GetValidDoubleTapDelay();
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN, kFakeCoordX,
kFakeCoordY + GetTouchSlop() / 2);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_UP);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
EXPECT_EQ(3, GetMostRecentGestureEvent().details.tap_count());
// The tap count should reset after hitting the repeat length.
event_time += GetValidDoubleTapDelay();
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_UP);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.tap_count());
// If double-tap is enabled, the tap repeat count should always be 1.
gesture_provider_->SetDoubleTapSupportForPlatformEnabled(true);
event_time += GetValidDoubleTapDelay();
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_UP);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
RunTasksAndWait(GetDoubleTapTimeout());
EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.tap_count());
event_time += GetValidDoubleTapDelay();
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_UP);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
RunTasksAndWait(GetDoubleTapTimeout());
EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.tap_count());
}
TEST_F(GestureProviderTest, SingleTapRepeatLengthOfOne) {
SetSingleTapRepeatInterval(1);
gesture_provider_->SetDoubleTapSupportForPlatformEnabled(false);
base::TimeTicks event_time = base::TimeTicks::Now();
MockMotionEvent event =
ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_UP);
gesture_provider_->OnTouchEvent(event);
EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.tap_count());
// Repeated taps should still produce a tap count of 1 if the
// tap repeat length is 1.
event_time += GetValidDoubleTapDelay();
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_UP);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.tap_count());
event_time += GetValidDoubleTapDelay();
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN, kFakeCoordX,
kFakeCoordY + GetTouchSlop() / 2);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
event = ObtainMotionEvent(event_time, MotionEvent::ACTION_UP);
EXPECT_TRUE(gesture_provider_->OnTouchEvent(event));
EXPECT_EQ(ET_GESTURE_TAP, GetMostRecentGestureEventType());
EXPECT_EQ(1, GetMostRecentGestureEvent().details.tap_count());
}
} // namespace ui