1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/events/gesture_detection/gesture_provider.h"
9 #include "base/auto_reset.h"
10 #include "base/trace_event/trace_event.h"
11 #include "ui/events/event_constants.h"
12 #include "ui/events/gesture_detection/gesture_event_data.h"
13 #include "ui/events/gesture_detection/gesture_listeners.h"
14 #include "ui/events/gesture_detection/motion_event.h"
15 #include "ui/events/gesture_detection/motion_event_generic.h"
16 #include "ui/events/gesture_detection/scale_gesture_listeners.h"
17 #include "ui/gfx/geometry/point_f.h"
22 // Double-tap drag zoom sensitivity (speed).
23 const float kDoubleTapDragZoomSpeed
= 0.005f
;
25 const char* GetMotionEventActionName(MotionEvent::Action action
) {
27 case MotionEvent::ACTION_NONE
:
29 case MotionEvent::ACTION_POINTER_DOWN
:
30 return "ACTION_POINTER_DOWN";
31 case MotionEvent::ACTION_POINTER_UP
:
32 return "ACTION_POINTER_UP";
33 case MotionEvent::ACTION_DOWN
:
35 case MotionEvent::ACTION_UP
:
37 case MotionEvent::ACTION_CANCEL
:
38 return "ACTION_CANCEL";
39 case MotionEvent::ACTION_MOVE
:
45 gfx::RectF
ClampBoundingBox(const gfx::RectF
& bounds
,
48 float width
= bounds
.width();
49 float height
= bounds
.height();
51 width
= std::max(min_length
, width
);
52 height
= std::max(min_length
, height
);
55 width
= std::min(max_length
, width
);
56 height
= std::min(max_length
, height
);
58 const gfx::PointF center
= bounds
.CenterPoint();
60 center
.x() - width
/ 2.f
, center
.y() - height
/ 2.f
, width
, height
);
65 // GestureProvider:::Config
67 GestureProvider::Config::Config()
68 : display(gfx::Display::kInvalidDisplayID
, gfx::Rect(1, 1)),
69 double_tap_support_for_platform_enabled(true),
70 gesture_begin_end_types_enabled(false),
71 min_gesture_bounds_length(0),
72 max_gesture_bounds_length(0) {
75 GestureProvider::Config::~Config() {
78 // GestureProvider::GestureListener
80 class GestureProvider::GestureListenerImpl
: public ScaleGestureListener
,
81 public GestureListener
,
82 public DoubleTapListener
{
84 GestureListenerImpl(const GestureProvider::Config
& config
,
85 GestureProviderClient
* client
)
88 gesture_detector_(config
.gesture_detector_config
, this, this),
89 scale_gesture_detector_(config
.scale_gesture_detector_config
, this),
90 snap_scroll_controller_(config
.gesture_detector_config
.touch_slop
,
91 config
.display
.size()),
92 ignore_multitouch_zoom_events_(false),
93 ignore_single_tap_(false),
94 pinch_event_sent_(false),
95 scroll_event_sent_(false),
96 max_diameter_before_show_press_(0),
97 show_press_event_sent_(false) {}
99 void OnTouchEvent(const MotionEvent
& event
) {
100 const bool in_scale_gesture
= IsScaleGestureDetectionInProgress();
101 snap_scroll_controller_
.SetSnapScrollMode(event
, in_scale_gesture
);
102 if (in_scale_gesture
)
103 SetIgnoreSingleTap(true);
105 const MotionEvent::Action action
= event
.GetAction();
106 if (action
== MotionEvent::ACTION_DOWN
) {
107 current_down_time_
= event
.GetEventTime();
108 current_longpress_time_
= base::TimeTicks();
109 ignore_single_tap_
= false;
110 scroll_event_sent_
= false;
111 pinch_event_sent_
= false;
112 show_press_event_sent_
= false;
113 gesture_detector_
.set_longpress_enabled(true);
114 tap_down_point_
= gfx::PointF(event
.GetX(), event
.GetY());
115 max_diameter_before_show_press_
= event
.GetTouchMajor();
118 gesture_detector_
.OnTouchEvent(event
);
119 scale_gesture_detector_
.OnTouchEvent(event
);
121 if (action
== MotionEvent::ACTION_UP
||
122 action
== MotionEvent::ACTION_CANCEL
) {
123 // Note: This call will have no effect if a fling was just generated, as
124 // |Fling()| will have already signalled an end to touch-scrolling.
125 if (scroll_event_sent_
)
126 Send(CreateGesture(ET_GESTURE_SCROLL_END
, event
));
127 current_down_time_
= base::TimeTicks();
128 } else if (action
== MotionEvent::ACTION_MOVE
) {
129 if (!show_press_event_sent_
&& !scroll_event_sent_
) {
130 max_diameter_before_show_press_
=
131 std::max(max_diameter_before_show_press_
, event
.GetTouchMajor());
136 void Send(GestureEventData gesture
) {
137 DCHECK(!gesture
.time
.is_null());
138 // The only valid events that should be sent without an active touch
139 // sequence are SHOW_PRESS and TAP, potentially triggered by the double-tap
141 DCHECK(!current_down_time_
.is_null() || gesture
.type() == ET_GESTURE_TAP
||
142 gesture
.type() == ET_GESTURE_SHOW_PRESS
||
143 gesture
.type() == ET_GESTURE_BEGIN
||
144 gesture
.type() == ET_GESTURE_END
);
146 if (gesture
.primary_tool_type
== MotionEvent::TOOL_TYPE_UNKNOWN
||
147 gesture
.primary_tool_type
== MotionEvent::TOOL_TYPE_FINGER
) {
148 gesture
.details
.set_bounding_box(
149 ClampBoundingBox(gesture
.details
.bounding_box_f(),
150 config_
.min_gesture_bounds_length
,
151 config_
.max_gesture_bounds_length
));
154 switch (gesture
.type()) {
155 case ET_GESTURE_LONG_PRESS
:
156 DCHECK(!IsScaleGestureDetectionInProgress());
157 current_longpress_time_
= gesture
.time
;
159 case ET_GESTURE_LONG_TAP
:
160 current_longpress_time_
= base::TimeTicks();
162 case ET_GESTURE_SCROLL_BEGIN
:
163 DCHECK(!scroll_event_sent_
);
164 scroll_event_sent_
= true;
166 case ET_GESTURE_SCROLL_END
:
167 DCHECK(scroll_event_sent_
);
168 if (pinch_event_sent_
)
169 Send(GestureEventData(ET_GESTURE_PINCH_END
, gesture
));
170 scroll_event_sent_
= false;
172 case ET_SCROLL_FLING_START
:
173 DCHECK(scroll_event_sent_
);
174 scroll_event_sent_
= false;
176 case ET_GESTURE_PINCH_BEGIN
:
177 DCHECK(!pinch_event_sent_
);
178 if (!scroll_event_sent_
)
179 Send(GestureEventData(ET_GESTURE_SCROLL_BEGIN
, gesture
));
180 pinch_event_sent_
= true;
182 case ET_GESTURE_PINCH_END
:
183 DCHECK(pinch_event_sent_
);
184 pinch_event_sent_
= false;
186 case ET_GESTURE_SHOW_PRESS
:
187 // It's possible that a double-tap drag zoom (from ScaleGestureDetector)
188 // will start before the press gesture fires (from GestureDetector), in
189 // which case the press should simply be dropped.
190 if (pinch_event_sent_
|| scroll_event_sent_
)
196 client_
->OnGestureEvent(gesture
);
197 GestureTouchUMAHistogram::RecordGestureEvent(gesture
);
200 // ScaleGestureListener implementation.
201 bool OnScaleBegin(const ScaleGestureDetector
& detector
,
202 const MotionEvent
& e
) override
{
203 if (ignore_multitouch_zoom_events_
&& !detector
.InDoubleTapMode())
208 void OnScaleEnd(const ScaleGestureDetector
& detector
,
209 const MotionEvent
& e
) override
{
210 if (!pinch_event_sent_
)
212 Send(CreateGesture(ET_GESTURE_PINCH_END
, e
));
215 bool OnScale(const ScaleGestureDetector
& detector
,
216 const MotionEvent
& e
) override
{
217 if (ignore_multitouch_zoom_events_
&& !detector
.InDoubleTapMode())
219 if (!pinch_event_sent_
) {
220 Send(CreateGesture(ET_GESTURE_PINCH_BEGIN
,
223 detector
.GetEventTime(),
224 detector
.GetFocusX(),
225 detector
.GetFocusY(),
226 detector
.GetFocusX() + e
.GetRawOffsetX(),
227 detector
.GetFocusY() + e
.GetRawOffsetY(),
229 GetBoundingBox(e
, ET_GESTURE_PINCH_BEGIN
),
233 if (std::abs(detector
.GetCurrentSpan() - detector
.GetPreviousSpan()) <
234 config_
.scale_gesture_detector_config
.min_pinch_update_span_delta
) {
238 float scale
= detector
.GetScaleFactor();
242 if (detector
.InDoubleTapMode()) {
243 // Relative changes in the double-tap scale factor computed by |detector|
244 // diminish as the touch moves away from the original double-tap focus.
245 // For historical reasons, Chrome has instead adopted a scale factor
246 // computation that is invariant to the focal distance, where
247 // the scale delta remains constant if the touch velocity is constant.
249 (detector
.GetCurrentSpanY() - detector
.GetPreviousSpanY()) * 0.5f
;
250 scale
= std::pow(scale
> 1 ? 1.0f
+ kDoubleTapDragZoomSpeed
251 : 1.0f
- kDoubleTapDragZoomSpeed
,
254 GestureEventDetails
pinch_details(ET_GESTURE_PINCH_UPDATE
);
255 pinch_details
.set_scale(scale
);
256 Send(CreateGesture(pinch_details
,
259 detector
.GetEventTime(),
260 detector
.GetFocusX(),
261 detector
.GetFocusY(),
262 detector
.GetFocusX() + e
.GetRawOffsetX(),
263 detector
.GetFocusY() + e
.GetRawOffsetY(),
265 GetBoundingBox(e
, pinch_details
.type()),
270 // GestureListener implementation.
271 bool OnDown(const MotionEvent
& e
) override
{
272 GestureEventDetails
tap_details(ET_GESTURE_TAP_DOWN
);
273 Send(CreateGesture(tap_details
, e
));
275 // Return true to indicate that we want to handle touch.
279 bool OnScroll(const MotionEvent
& e1
,
280 const MotionEvent
& e2
,
281 float raw_distance_x
,
282 float raw_distance_y
) override
{
283 float distance_x
= raw_distance_x
;
284 float distance_y
= raw_distance_y
;
285 if (!scroll_event_sent_
&& e2
.GetPointerCount() == 1) {
286 // Remove the touch slop region from the first scroll event to
287 // avoid a jump. Touch slop isn't used for multi-finger
288 // gestures, so in those cases we don't subtract the slop.
290 std::sqrt(distance_x
* distance_x
+ distance_y
* distance_y
);
291 float epsilon
= 1e-3f
;
292 if (distance
> epsilon
) {
295 distance
- config_
.gesture_detector_config
.touch_slop
) /
302 snap_scroll_controller_
.UpdateSnapScrollMode(distance_x
, distance_y
);
303 if (snap_scroll_controller_
.IsSnappingScrolls()) {
304 if (snap_scroll_controller_
.IsSnapHorizontal())
310 if (!distance_x
&& !distance_y
)
313 if (!scroll_event_sent_
) {
314 // Note that scroll start hints are in distance traveled, where
315 // scroll deltas are in the opposite direction.
316 GestureEventDetails
scroll_details(
317 ET_GESTURE_SCROLL_BEGIN
, -raw_distance_x
, -raw_distance_y
);
319 // Use the co-ordinates from the touch down, as these co-ordinates are
320 // used to determine which layer the scroll should affect.
321 Send(CreateGesture(scroll_details
, e2
.GetPointerId(), e2
.GetToolType(),
322 e2
.GetEventTime(), e1
.GetX(), e1
.GetY(), e1
.GetRawX(),
323 e1
.GetRawY(), e2
.GetPointerCount(),
324 GetBoundingBox(e2
, scroll_details
.type()),
326 DCHECK(scroll_event_sent_
);
329 GestureEventDetails
scroll_details(ET_GESTURE_SCROLL_UPDATE
, -distance_x
,
331 const gfx::RectF bounding_box
= GetBoundingBox(e2
, scroll_details
.type());
332 const gfx::PointF center
= bounding_box
.CenterPoint();
333 const gfx::PointF raw_center
=
334 center
+ gfx::Vector2dF(e2
.GetRawOffsetX(), e2
.GetRawOffsetY());
335 Send(CreateGesture(scroll_details
, e2
.GetPointerId(), e2
.GetToolType(),
336 e2
.GetEventTime(), center
.x(), center
.y(),
337 raw_center
.x(), raw_center
.y(), e2
.GetPointerCount(),
338 bounding_box
, e2
.GetFlags()));
343 bool OnFling(const MotionEvent
& e1
,
344 const MotionEvent
& e2
,
346 float velocity_y
) override
{
347 if (snap_scroll_controller_
.IsSnappingScrolls()) {
348 if (snap_scroll_controller_
.IsSnapHorizontal()) {
355 if (!velocity_x
&& !velocity_y
)
358 if (!scroll_event_sent_
) {
359 // The native side needs a ET_GESTURE_SCROLL_BEGIN before
360 // ET_SCROLL_FLING_START to send the fling to the correct target.
361 // The distance traveled in one second is a reasonable scroll start hint.
362 GestureEventDetails
scroll_details(
363 ET_GESTURE_SCROLL_BEGIN
, velocity_x
, velocity_y
);
364 Send(CreateGesture(scroll_details
, e2
));
367 GestureEventDetails
fling_details(
368 ET_SCROLL_FLING_START
, velocity_x
, velocity_y
);
369 Send(CreateGesture(fling_details
, e2
));
373 bool OnSwipe(const MotionEvent
& e1
,
374 const MotionEvent
& e2
,
376 float velocity_y
) override
{
377 GestureEventDetails
swipe_details(ET_GESTURE_SWIPE
, velocity_x
, velocity_y
);
378 Send(CreateGesture(swipe_details
, e2
));
382 bool OnTwoFingerTap(const MotionEvent
& e1
, const MotionEvent
& e2
) override
{
383 // The location of the two finger tap event should be the location of the
385 GestureEventDetails
two_finger_tap_details(
386 ET_GESTURE_TWO_FINGER_TAP
, e1
.GetTouchMajor(), e1
.GetTouchMajor());
387 Send(CreateGesture(two_finger_tap_details
,
395 e2
.GetPointerCount(),
396 GetBoundingBox(e2
, two_finger_tap_details
.type()),
401 void OnShowPress(const MotionEvent
& e
) override
{
402 GestureEventDetails
show_press_details(ET_GESTURE_SHOW_PRESS
);
403 show_press_event_sent_
= true;
404 Send(CreateGesture(show_press_details
, e
));
407 bool OnSingleTapUp(const MotionEvent
& e
) override
{
408 // This is a hack to address the issue where user hovers
409 // over a link for longer than double_tap_timeout_, then
410 // OnSingleTapConfirmed() is not triggered. But we still
411 // want to trigger the tap event at UP. So we override
412 // OnSingleTapUp() in this case. This assumes singleTapUp
413 // gets always called before singleTapConfirmed.
414 if (!ignore_single_tap_
) {
415 if (e
.GetEventTime() - current_down_time_
>
416 config_
.gesture_detector_config
.double_tap_timeout
) {
417 return OnSingleTapConfirmed(e
);
418 } else if (!IsDoubleTapEnabled()) {
419 // If double-tap has been disabled, there is no need to wait
420 // for the double-tap timeout.
421 return OnSingleTapConfirmed(e
);
423 // Notify Blink about this tapUp event anyway, when none of the above
424 // conditions applied.
425 Send(CreateTapGesture(ET_GESTURE_TAP_UNCONFIRMED
, e
));
429 if (e
.GetAction() == MotionEvent::ACTION_UP
&&
430 !current_longpress_time_
.is_null() &&
431 !IsScaleGestureDetectionInProgress()) {
432 GestureEventDetails
long_tap_details(ET_GESTURE_LONG_TAP
);
433 Send(CreateGesture(long_tap_details
, e
));
440 // DoubleTapListener implementation.
441 bool OnSingleTapConfirmed(const MotionEvent
& e
) override
{
442 // Long taps in the edges of the screen have their events delayed by
443 // ContentViewHolder for tab swipe operations. As a consequence of the delay
444 // this method might be called after receiving the up event.
445 // These corner cases should be ignored.
446 if (ignore_single_tap_
)
449 ignore_single_tap_
= true;
451 Send(CreateTapGesture(ET_GESTURE_TAP
, e
));
455 bool OnDoubleTap(const MotionEvent
& e
) override
{
456 return scale_gesture_detector_
.OnDoubleTap(e
);
459 bool OnDoubleTapEvent(const MotionEvent
& e
) override
{
460 switch (e
.GetAction()) {
461 case MotionEvent::ACTION_DOWN
:
462 gesture_detector_
.set_longpress_enabled(false);
465 case MotionEvent::ACTION_UP
:
466 if (!IsPinchInProgress() && !IsScrollInProgress()) {
467 Send(CreateTapGesture(ET_GESTURE_DOUBLE_TAP
, e
));
478 void OnLongPress(const MotionEvent
& e
) override
{
479 DCHECK(!IsDoubleTapInProgress());
480 SetIgnoreSingleTap(true);
481 GestureEventDetails
long_press_details(ET_GESTURE_LONG_PRESS
);
482 Send(CreateGesture(long_press_details
, e
));
485 GestureEventData
CreateGesture(const GestureEventDetails
& details
,
487 MotionEvent::ToolType primary_tool_type
,
488 base::TimeTicks time
,
493 size_t touch_point_count
,
494 const gfx::RectF
& bounding_box
,
496 return GestureEventData(details
,
509 GestureEventData
CreateGesture(EventType type
,
511 MotionEvent::ToolType primary_tool_type
,
512 base::TimeTicks time
,
517 size_t touch_point_count
,
518 const gfx::RectF
& bounding_box
,
520 return GestureEventData(GestureEventDetails(type
),
533 GestureEventData
CreateGesture(const GestureEventDetails
& details
,
534 const MotionEvent
& event
) {
535 return GestureEventData(details
,
536 event
.GetPointerId(),
538 event
.GetEventTime(),
543 event
.GetPointerCount(),
544 GetBoundingBox(event
, details
.type()),
548 GestureEventData
CreateGesture(EventType type
, const MotionEvent
& event
) {
549 return CreateGesture(GestureEventDetails(type
), event
);
552 GestureEventData
CreateTapGesture(EventType type
, const MotionEvent
& event
) {
553 // Set the tap count to 1 even for ET_GESTURE_DOUBLE_TAP, in order to be
554 // consistent with double tap behavior on a mobile viewport. See
555 // crbug.com/234986 for context.
556 GestureEventDetails
details(type
);
557 details
.set_tap_count(1);
558 return CreateGesture(details
, event
);
561 gfx::RectF
GetBoundingBox(const MotionEvent
& event
, EventType type
) {
562 // Can't use gfx::RectF::Union, as it ignores touches with a radius of 0.
563 float left
= std::numeric_limits
<float>::max();
564 float top
= std::numeric_limits
<float>::max();
565 float right
= -std::numeric_limits
<float>::max();
566 float bottom
= -std::numeric_limits
<float>::max();
567 for (size_t i
= 0; i
< event
.GetPointerCount(); ++i
) {
568 float x
, y
, diameter
;
569 // Only for the show press and tap events, the bounding box is calculated
570 // based on the touch start point and the maximum diameter before the
571 // show press event is sent.
572 if (type
== ET_GESTURE_SHOW_PRESS
|| type
== ET_GESTURE_TAP
||
573 type
== ET_GESTURE_TAP_UNCONFIRMED
) {
575 diameter
= max_diameter_before_show_press_
;
576 x
= tap_down_point_
.x();
577 y
= tap_down_point_
.y();
579 diameter
= event
.GetTouchMajor(i
);
583 x
= x
- diameter
/ 2;
584 y
= y
- diameter
/ 2;
585 left
= std::min(left
, x
);
586 right
= std::max(right
, x
+ diameter
);
587 top
= std::min(top
, y
);
588 bottom
= std::max(bottom
, y
+ diameter
);
590 return gfx::RectF(left
, top
, right
- left
, bottom
- top
);
593 void SetDoubleTapEnabled(bool enabled
) {
594 DCHECK(!IsDoubleTapInProgress());
595 gesture_detector_
.SetDoubleTapListener(enabled
? this : NULL
);
598 void SetMultiTouchZoomEnabled(bool enabled
) {
599 // Note that returning false from |OnScaleBegin()| or |OnScale()| prevents
600 // the detector from emitting further scale updates for the current touch
601 // sequence. Thus, if multitouch events are enabled in the middle of a
602 // gesture, it will only take effect with the next gesture.
603 ignore_multitouch_zoom_events_
= !enabled
;
606 bool IsDoubleTapInProgress() const {
607 return gesture_detector_
.is_double_tapping() ||
608 (IsScaleGestureDetectionInProgress() && InDoubleTapMode());
611 bool IsScrollInProgress() const { return scroll_event_sent_
; }
613 bool IsPinchInProgress() const { return pinch_event_sent_
; }
616 bool IsScaleGestureDetectionInProgress() const {
617 return scale_gesture_detector_
.IsInProgress();
620 bool InDoubleTapMode() const {
621 return scale_gesture_detector_
.InDoubleTapMode();
624 bool IsDoubleTapEnabled() const {
625 return gesture_detector_
.has_doubletap_listener();
628 void SetIgnoreSingleTap(bool value
) { ignore_single_tap_
= value
; }
630 const GestureProvider::Config config_
;
631 GestureProviderClient
* const client_
;
633 GestureDetector gesture_detector_
;
634 ScaleGestureDetector scale_gesture_detector_
;
635 SnapScrollController snap_scroll_controller_
;
637 base::TimeTicks current_down_time_
;
639 // Keeps track of the current GESTURE_LONG_PRESS event. If a context menu is
640 // opened after a GESTURE_LONG_PRESS, this is used to insert a
641 // GESTURE_TAP_CANCEL for removing any ::active styling.
642 base::TimeTicks current_longpress_time_
;
644 // Completely silence multi-touch (pinch) scaling events. Used in WebView when
645 // zoom support is turned off.
646 bool ignore_multitouch_zoom_events_
;
648 // TODO(klobag): This is to avoid a bug in GestureDetector. With multi-touch,
649 // always_in_tap_region_ is not reset. So when the last finger is up,
650 // |OnSingleTapUp()| will be mistakenly fired.
651 bool ignore_single_tap_
;
653 // Tracks whether {PINCH|SCROLL}_BEGIN events have been forwarded for the
654 // current touch sequence.
655 bool pinch_event_sent_
;
656 bool scroll_event_sent_
;
658 // Only track the maximum diameter before the show press event has been
659 // sent and a tap must still be possible for this touch sequence.
660 float max_diameter_before_show_press_
;
662 gfx::PointF tap_down_point_
;
664 // Tracks whether an ET_GESTURE_SHOW_PRESS event has been sent for this touch
666 bool show_press_event_sent_
;
668 DISALLOW_COPY_AND_ASSIGN(GestureListenerImpl
);
673 GestureProvider::GestureProvider(const Config
& config
,
674 GestureProviderClient
* client
)
675 : double_tap_support_for_page_(true),
676 double_tap_support_for_platform_(
677 config
.double_tap_support_for_platform_enabled
),
678 gesture_begin_end_types_enabled_(config
.gesture_begin_end_types_enabled
) {
680 DCHECK(!config
.min_gesture_bounds_length
||
681 !config
.max_gesture_bounds_length
||
682 config
.min_gesture_bounds_length
<= config
.max_gesture_bounds_length
);
683 TRACE_EVENT0("input", "GestureProvider::InitGestureDetectors");
684 gesture_listener_
.reset(new GestureListenerImpl(config
, client
));
685 UpdateDoubleTapDetectionSupport();
688 GestureProvider::~GestureProvider() {
691 bool GestureProvider::OnTouchEvent(const MotionEvent
& event
) {
692 TRACE_EVENT1("input",
693 "GestureProvider::OnTouchEvent",
695 GetMotionEventActionName(event
.GetAction()));
697 DCHECK_NE(0u, event
.GetPointerCount());
699 if (!CanHandle(event
))
702 OnTouchEventHandlingBegin(event
);
703 gesture_listener_
->OnTouchEvent(event
);
704 OnTouchEventHandlingEnd(event
);
705 uma_histogram_
.RecordTouchEvent(event
);
709 void GestureProvider::ResetDetection() {
710 MotionEventGeneric
generic_cancel_event(MotionEvent::ACTION_CANCEL
,
711 base::TimeTicks::Now(),
712 PointerProperties());
713 OnTouchEvent(generic_cancel_event
);
716 void GestureProvider::SetMultiTouchZoomSupportEnabled(bool enabled
) {
717 gesture_listener_
->SetMultiTouchZoomEnabled(enabled
);
720 void GestureProvider::SetDoubleTapSupportForPlatformEnabled(bool enabled
) {
721 if (double_tap_support_for_platform_
== enabled
)
723 double_tap_support_for_platform_
= enabled
;
724 UpdateDoubleTapDetectionSupport();
727 void GestureProvider::SetDoubleTapSupportForPageEnabled(bool enabled
) {
728 if (double_tap_support_for_page_
== enabled
)
730 double_tap_support_for_page_
= enabled
;
731 UpdateDoubleTapDetectionSupport();
734 bool GestureProvider::IsScrollInProgress() const {
735 return gesture_listener_
->IsScrollInProgress();
738 bool GestureProvider::IsPinchInProgress() const {
739 return gesture_listener_
->IsPinchInProgress();
742 bool GestureProvider::IsDoubleTapInProgress() const {
743 return gesture_listener_
->IsDoubleTapInProgress();
746 bool GestureProvider::CanHandle(const MotionEvent
& event
) const {
747 // Aura requires one cancel event per touch point, whereas Android requires
748 // one cancel event per touch sequence. Thus we need to allow extra cancel
750 return current_down_event_
|| event
.GetAction() == MotionEvent::ACTION_DOWN
||
751 event
.GetAction() == MotionEvent::ACTION_CANCEL
;
754 void GestureProvider::OnTouchEventHandlingBegin(const MotionEvent
& event
) {
755 switch (event
.GetAction()) {
756 case MotionEvent::ACTION_DOWN
:
757 current_down_event_
= event
.Clone();
758 if (gesture_begin_end_types_enabled_
)
759 gesture_listener_
->Send(
760 gesture_listener_
->CreateGesture(ET_GESTURE_BEGIN
, event
));
762 case MotionEvent::ACTION_POINTER_DOWN
:
763 if (gesture_begin_end_types_enabled_
) {
764 const int action_index
= event
.GetActionIndex();
765 gesture_listener_
->Send(gesture_listener_
->CreateGesture(
767 event
.GetPointerId(),
769 event
.GetEventTime(),
770 event
.GetX(action_index
),
771 event
.GetY(action_index
),
772 event
.GetRawX(action_index
),
773 event
.GetRawY(action_index
),
774 event
.GetPointerCount(),
775 gesture_listener_
->GetBoundingBox(event
, ET_GESTURE_BEGIN
),
779 case MotionEvent::ACTION_POINTER_UP
:
780 case MotionEvent::ACTION_UP
:
781 case MotionEvent::ACTION_CANCEL
:
782 case MotionEvent::ACTION_MOVE
:
784 case MotionEvent::ACTION_NONE
:
790 void GestureProvider::OnTouchEventHandlingEnd(const MotionEvent
& event
) {
791 switch (event
.GetAction()) {
792 case MotionEvent::ACTION_UP
:
793 case MotionEvent::ACTION_CANCEL
: {
794 if (gesture_begin_end_types_enabled_
)
795 gesture_listener_
->Send(
796 gesture_listener_
->CreateGesture(ET_GESTURE_END
, event
));
798 current_down_event_
.reset();
800 UpdateDoubleTapDetectionSupport();
803 case MotionEvent::ACTION_POINTER_UP
:
804 if (gesture_begin_end_types_enabled_
)
805 gesture_listener_
->Send(
806 gesture_listener_
->CreateGesture(ET_GESTURE_END
, event
));
808 case MotionEvent::ACTION_DOWN
:
809 case MotionEvent::ACTION_POINTER_DOWN
:
810 case MotionEvent::ACTION_MOVE
:
812 case MotionEvent::ACTION_NONE
:
818 void GestureProvider::UpdateDoubleTapDetectionSupport() {
819 // The GestureDetector requires that any provided DoubleTapListener remain
820 // attached to it for the duration of a touch sequence. Defer any potential
821 // null'ing of the listener until the sequence has ended.
822 if (current_down_event_
)
825 const bool double_tap_enabled
=
826 double_tap_support_for_page_
&& double_tap_support_for_platform_
;
827 gesture_listener_
->SetDoubleTapEnabled(double_tap_enabled
);