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/debug/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_POINTER_DOWN
:
28 return "ACTION_POINTER_DOWN";
29 case MotionEvent::ACTION_POINTER_UP
:
30 return "ACTION_POINTER_UP";
31 case MotionEvent::ACTION_DOWN
:
33 case MotionEvent::ACTION_UP
:
35 case MotionEvent::ACTION_CANCEL
:
36 return "ACTION_CANCEL";
37 case MotionEvent::ACTION_MOVE
:
43 gfx::RectF
ClampBoundingBox(const gfx::RectF
& bounds
,
46 float width
= bounds
.width();
47 float height
= bounds
.height();
49 width
= std::max(min_length
, width
);
50 height
= std::max(min_length
, height
);
53 width
= std::min(max_length
, width
);
54 height
= std::min(max_length
, height
);
56 const gfx::PointF center
= bounds
.CenterPoint();
58 center
.x() - width
/ 2.f
, center
.y() - height
/ 2.f
, width
, height
);
63 // GestureProvider:::Config
65 GestureProvider::Config::Config()
66 : display(gfx::Display::kInvalidDisplayID
, gfx::Rect(1, 1)),
67 disable_click_delay(false),
68 gesture_begin_end_types_enabled(false),
69 min_gesture_bounds_length(0),
70 max_gesture_bounds_length(0) {
73 GestureProvider::Config::~Config() {
76 // GestureProvider::GestureListener
78 class GestureProvider::GestureListenerImpl
: public ScaleGestureListener
,
79 public GestureListener
,
80 public DoubleTapListener
{
82 GestureListenerImpl(const GestureProvider::Config
& config
,
83 GestureProviderClient
* client
)
86 gesture_detector_(config
.gesture_detector_config
, this, this),
87 scale_gesture_detector_(config
.scale_gesture_detector_config
, this),
88 snap_scroll_controller_(config
.gesture_detector_config
.touch_slop
,
89 config
.display
.size()),
90 ignore_multitouch_zoom_events_(false),
91 ignore_single_tap_(false),
92 pinch_event_sent_(false),
93 scroll_event_sent_(false),
94 max_diameter_before_show_press_(0),
95 show_press_event_sent_(false) {}
97 void OnTouchEvent(const MotionEvent
& event
) {
98 const bool in_scale_gesture
= IsScaleGestureDetectionInProgress();
99 snap_scroll_controller_
.SetSnapScrollMode(event
, in_scale_gesture
);
100 if (in_scale_gesture
)
101 SetIgnoreSingleTap(true);
103 const MotionEvent::Action action
= event
.GetAction();
104 if (action
== MotionEvent::ACTION_DOWN
) {
105 current_down_time_
= event
.GetEventTime();
106 current_longpress_time_
= base::TimeTicks();
107 ignore_single_tap_
= false;
108 scroll_event_sent_
= false;
109 pinch_event_sent_
= false;
110 show_press_event_sent_
= false;
111 gesture_detector_
.set_longpress_enabled(true);
112 tap_down_point_
= gfx::PointF(event
.GetX(), event
.GetY());
113 max_diameter_before_show_press_
= event
.GetTouchMajor();
116 gesture_detector_
.OnTouchEvent(event
);
117 scale_gesture_detector_
.OnTouchEvent(event
);
119 if (action
== MotionEvent::ACTION_UP
||
120 action
== MotionEvent::ACTION_CANCEL
) {
121 // Note: This call will have no effect if a fling was just generated, as
122 // |Fling()| will have already signalled an end to touch-scrolling.
123 if (scroll_event_sent_
)
124 Send(CreateGesture(ET_GESTURE_SCROLL_END
, event
));
125 current_down_time_
= base::TimeTicks();
126 } else if (action
== MotionEvent::ACTION_MOVE
) {
127 if (!show_press_event_sent_
&& !scroll_event_sent_
) {
128 max_diameter_before_show_press_
=
129 std::max(max_diameter_before_show_press_
, event
.GetTouchMajor());
134 void Send(GestureEventData gesture
) {
135 DCHECK(!gesture
.time
.is_null());
136 // The only valid events that should be sent without an active touch
137 // sequence are SHOW_PRESS and TAP, potentially triggered by the double-tap
139 DCHECK(!current_down_time_
.is_null() || gesture
.type() == ET_GESTURE_TAP
||
140 gesture
.type() == ET_GESTURE_SHOW_PRESS
||
141 gesture
.type() == ET_GESTURE_BEGIN
||
142 gesture
.type() == ET_GESTURE_END
);
144 if (gesture
.primary_tool_type
== MotionEvent::TOOL_TYPE_UNKNOWN
||
145 gesture
.primary_tool_type
== MotionEvent::TOOL_TYPE_FINGER
) {
146 gesture
.details
.set_bounding_box(
147 ClampBoundingBox(gesture
.details
.bounding_box_f(),
148 config_
.min_gesture_bounds_length
,
149 config_
.max_gesture_bounds_length
));
152 switch (gesture
.type()) {
153 case ET_GESTURE_LONG_PRESS
:
154 DCHECK(!IsScaleGestureDetectionInProgress());
155 current_longpress_time_
= gesture
.time
;
157 case ET_GESTURE_LONG_TAP
:
158 current_longpress_time_
= base::TimeTicks();
160 case ET_GESTURE_SCROLL_BEGIN
:
161 DCHECK(!scroll_event_sent_
);
162 scroll_event_sent_
= true;
164 case ET_GESTURE_SCROLL_END
:
165 DCHECK(scroll_event_sent_
);
166 if (pinch_event_sent_
)
167 Send(GestureEventData(ET_GESTURE_PINCH_END
, gesture
));
168 scroll_event_sent_
= false;
170 case ET_SCROLL_FLING_START
:
171 DCHECK(scroll_event_sent_
);
172 scroll_event_sent_
= false;
174 case ET_GESTURE_PINCH_BEGIN
:
175 DCHECK(!pinch_event_sent_
);
176 if (!scroll_event_sent_
)
177 Send(GestureEventData(ET_GESTURE_SCROLL_BEGIN
, gesture
));
178 pinch_event_sent_
= true;
180 case ET_GESTURE_PINCH_END
:
181 DCHECK(pinch_event_sent_
);
182 pinch_event_sent_
= false;
184 case ET_GESTURE_SHOW_PRESS
:
185 // It's possible that a double-tap drag zoom (from ScaleGestureDetector)
186 // will start before the press gesture fires (from GestureDetector), in
187 // which case the press should simply be dropped.
188 if (pinch_event_sent_
|| scroll_event_sent_
)
194 client_
->OnGestureEvent(gesture
);
195 GestureTouchUMAHistogram::RecordGestureEvent(gesture
);
198 // ScaleGestureListener implementation.
199 bool OnScaleBegin(const ScaleGestureDetector
& detector
,
200 const MotionEvent
& e
) override
{
201 if (ignore_multitouch_zoom_events_
&& !detector
.InDoubleTapMode())
206 void OnScaleEnd(const ScaleGestureDetector
& detector
,
207 const MotionEvent
& e
) override
{
208 if (!pinch_event_sent_
)
210 Send(CreateGesture(ET_GESTURE_PINCH_END
, e
));
213 bool OnScale(const ScaleGestureDetector
& detector
,
214 const MotionEvent
& e
) override
{
215 if (ignore_multitouch_zoom_events_
&& !detector
.InDoubleTapMode())
217 if (!pinch_event_sent_
) {
218 Send(CreateGesture(ET_GESTURE_PINCH_BEGIN
,
221 detector
.GetEventTime(),
222 detector
.GetFocusX(),
223 detector
.GetFocusY(),
224 detector
.GetFocusX() + e
.GetRawOffsetX(),
225 detector
.GetFocusY() + e
.GetRawOffsetY(),
227 GetBoundingBox(e
, ET_GESTURE_PINCH_BEGIN
),
231 if (std::abs(detector
.GetCurrentSpan() - detector
.GetPreviousSpan()) <
232 config_
.scale_gesture_detector_config
.min_pinch_update_span_delta
) {
236 float scale
= detector
.GetScaleFactor();
240 if (detector
.InDoubleTapMode()) {
241 // Relative changes in the double-tap scale factor computed by |detector|
242 // diminish as the touch moves away from the original double-tap focus.
243 // For historical reasons, Chrome has instead adopted a scale factor
244 // computation that is invariant to the focal distance, where
245 // the scale delta remains constant if the touch velocity is constant.
247 (detector
.GetCurrentSpanY() - detector
.GetPreviousSpanY()) * 0.5f
;
248 scale
= std::pow(scale
> 1 ? 1.0f
+ kDoubleTapDragZoomSpeed
249 : 1.0f
- kDoubleTapDragZoomSpeed
,
252 GestureEventDetails
pinch_details(ET_GESTURE_PINCH_UPDATE
);
253 pinch_details
.set_scale(scale
);
254 Send(CreateGesture(pinch_details
,
257 detector
.GetEventTime(),
258 detector
.GetFocusX(),
259 detector
.GetFocusY(),
260 detector
.GetFocusX() + e
.GetRawOffsetX(),
261 detector
.GetFocusY() + e
.GetRawOffsetY(),
263 GetBoundingBox(e
, pinch_details
.type()),
268 // GestureListener implementation.
269 bool OnDown(const MotionEvent
& e
) override
{
270 GestureEventDetails
tap_details(ET_GESTURE_TAP_DOWN
);
271 Send(CreateGesture(tap_details
, e
));
273 // Return true to indicate that we want to handle touch.
277 bool OnScroll(const MotionEvent
& e1
,
278 const MotionEvent
& e2
,
279 float raw_distance_x
,
280 float raw_distance_y
) override
{
281 float distance_x
= raw_distance_x
;
282 float distance_y
= raw_distance_y
;
283 if (!scroll_event_sent_
) {
284 // Remove the touch slop region from the first scroll event to avoid a
287 std::sqrt(distance_x
* distance_x
+ distance_y
* distance_y
);
288 float epsilon
= 1e-3f
;
289 if (distance
> epsilon
) {
292 distance
- config_
.gesture_detector_config
.touch_slop
) /
298 // Note that scroll start hints are in distance traveled, where
299 // scroll deltas are in the opposite direction.
300 GestureEventDetails
scroll_details(
301 ET_GESTURE_SCROLL_BEGIN
, -raw_distance_x
, -raw_distance_y
);
303 // Use the co-ordinates from the touch down, as these co-ordinates are
304 // used to determine which layer the scroll should affect.
305 Send(CreateGesture(scroll_details
,
313 e2
.GetPointerCount(),
314 GetBoundingBox(e2
, scroll_details
.type()),
316 DCHECK(scroll_event_sent_
);
319 snap_scroll_controller_
.UpdateSnapScrollMode(distance_x
, distance_y
);
320 if (snap_scroll_controller_
.IsSnappingScrolls()) {
321 if (snap_scroll_controller_
.IsSnapHorizontal())
327 if (distance_x
|| distance_y
) {
328 GestureEventDetails
scroll_details(
329 ET_GESTURE_SCROLL_UPDATE
, -distance_x
, -distance_y
);
330 const gfx::RectF bounding_box
= GetBoundingBox(e2
, scroll_details
.type());
331 const gfx::PointF center
= bounding_box
.CenterPoint();
332 const gfx::PointF raw_center
=
333 center
+ gfx::Vector2dF(e2
.GetRawOffsetX(), e2
.GetRawOffsetY());
334 Send(CreateGesture(scroll_details
,
342 e2
.GetPointerCount(),
350 bool OnFling(const MotionEvent
& e1
,
351 const MotionEvent
& e2
,
353 float velocity_y
) override
{
354 if (snap_scroll_controller_
.IsSnappingScrolls()) {
355 if (snap_scroll_controller_
.IsSnapHorizontal()) {
362 if (!velocity_x
&& !velocity_y
)
365 if (!scroll_event_sent_
) {
366 // The native side needs a ET_GESTURE_SCROLL_BEGIN before
367 // ET_SCROLL_FLING_START to send the fling to the correct target.
368 // The distance traveled in one second is a reasonable scroll start hint.
369 GestureEventDetails
scroll_details(
370 ET_GESTURE_SCROLL_BEGIN
, velocity_x
, velocity_y
);
371 Send(CreateGesture(scroll_details
, e2
));
374 GestureEventDetails
fling_details(
375 ET_SCROLL_FLING_START
, velocity_x
, velocity_y
);
376 Send(CreateGesture(fling_details
, e2
));
380 bool OnSwipe(const MotionEvent
& e1
,
381 const MotionEvent
& e2
,
383 float velocity_y
) override
{
384 GestureEventDetails
swipe_details(ET_GESTURE_SWIPE
, velocity_x
, velocity_y
);
385 Send(CreateGesture(swipe_details
, e2
));
389 bool OnTwoFingerTap(const MotionEvent
& e1
, const MotionEvent
& e2
) override
{
390 // The location of the two finger tap event should be the location of the
392 GestureEventDetails
two_finger_tap_details(
393 ET_GESTURE_TWO_FINGER_TAP
, e1
.GetTouchMajor(), e1
.GetTouchMajor());
394 Send(CreateGesture(two_finger_tap_details
,
402 e2
.GetPointerCount(),
403 GetBoundingBox(e2
, two_finger_tap_details
.type()),
408 void OnShowPress(const MotionEvent
& e
) override
{
409 GestureEventDetails
show_press_details(ET_GESTURE_SHOW_PRESS
);
410 show_press_event_sent_
= true;
411 Send(CreateGesture(show_press_details
, e
));
414 bool OnSingleTapUp(const MotionEvent
& e
) override
{
415 // This is a hack to address the issue where user hovers
416 // over a link for longer than double_tap_timeout_, then
417 // OnSingleTapConfirmed() is not triggered. But we still
418 // want to trigger the tap event at UP. So we override
419 // OnSingleTapUp() in this case. This assumes singleTapUp
420 // gets always called before singleTapConfirmed.
421 if (!ignore_single_tap_
) {
422 if (e
.GetEventTime() - current_down_time_
>
423 config_
.gesture_detector_config
.double_tap_timeout
) {
424 return OnSingleTapConfirmed(e
);
425 } else if (!IsDoubleTapEnabled() || config_
.disable_click_delay
) {
426 // If double-tap has been disabled, there is no need to wait
427 // for the double-tap timeout.
428 return OnSingleTapConfirmed(e
);
430 // Notify Blink about this tapUp event anyway, when none of the above
431 // conditions applied.
432 Send(CreateTapGesture(ET_GESTURE_TAP_UNCONFIRMED
, e
));
436 if (e
.GetAction() == MotionEvent::ACTION_UP
&&
437 !current_longpress_time_
.is_null() &&
438 !IsScaleGestureDetectionInProgress()) {
439 GestureEventDetails
long_tap_details(ET_GESTURE_LONG_TAP
);
440 Send(CreateGesture(long_tap_details
, e
));
447 // DoubleTapListener implementation.
448 bool OnSingleTapConfirmed(const MotionEvent
& e
) override
{
449 // Long taps in the edges of the screen have their events delayed by
450 // ContentViewHolder for tab swipe operations. As a consequence of the delay
451 // this method might be called after receiving the up event.
452 // These corner cases should be ignored.
453 if (ignore_single_tap_
)
456 ignore_single_tap_
= true;
458 Send(CreateTapGesture(ET_GESTURE_TAP
, e
));
462 bool OnDoubleTap(const MotionEvent
& e
) override
{
463 return scale_gesture_detector_
.OnDoubleTap(e
);
466 bool OnDoubleTapEvent(const MotionEvent
& e
) override
{
467 switch (e
.GetAction()) {
468 case MotionEvent::ACTION_DOWN
:
469 gesture_detector_
.set_longpress_enabled(false);
472 case MotionEvent::ACTION_UP
:
473 if (!IsPinchInProgress() && !IsScrollInProgress()) {
474 Send(CreateTapGesture(ET_GESTURE_DOUBLE_TAP
, e
));
485 void OnLongPress(const MotionEvent
& e
) override
{
486 DCHECK(!IsDoubleTapInProgress());
487 SetIgnoreSingleTap(true);
488 GestureEventDetails
long_press_details(ET_GESTURE_LONG_PRESS
);
489 Send(CreateGesture(long_press_details
, e
));
492 GestureEventData
CreateGesture(const GestureEventDetails
& details
,
494 MotionEvent::ToolType primary_tool_type
,
495 base::TimeTicks time
,
500 size_t touch_point_count
,
501 const gfx::RectF
& bounding_box
,
503 return GestureEventData(details
,
516 GestureEventData
CreateGesture(EventType type
,
518 MotionEvent::ToolType primary_tool_type
,
519 base::TimeTicks time
,
524 size_t touch_point_count
,
525 const gfx::RectF
& bounding_box
,
527 return GestureEventData(GestureEventDetails(type
),
540 GestureEventData
CreateGesture(const GestureEventDetails
& details
,
541 const MotionEvent
& event
) {
542 return GestureEventData(details
,
545 event
.GetEventTime(),
550 event
.GetPointerCount(),
551 GetBoundingBox(event
, details
.type()),
555 GestureEventData
CreateGesture(EventType type
, const MotionEvent
& event
) {
556 return CreateGesture(GestureEventDetails(type
), event
);
559 GestureEventData
CreateTapGesture(EventType type
, const MotionEvent
& event
) {
560 // Set the tap count to 1 even for ET_GESTURE_DOUBLE_TAP, in order to be
561 // consistent with double tap behavior on a mobile viewport. See
562 // crbug.com/234986 for context.
563 GestureEventDetails
details(type
);
564 details
.set_tap_count(1);
565 return CreateGesture(details
, event
);
568 gfx::RectF
GetBoundingBox(const MotionEvent
& event
, EventType type
) {
569 // Can't use gfx::RectF::Union, as it ignores touches with a radius of 0.
570 float left
= std::numeric_limits
<float>::max();
571 float top
= std::numeric_limits
<float>::max();
572 float right
= -std::numeric_limits
<float>::max();
573 float bottom
= -std::numeric_limits
<float>::max();
574 for (size_t i
= 0; i
< event
.GetPointerCount(); ++i
) {
575 float x
, y
, diameter
;
576 // Only for the show press and tap events, the bounding box is calculated
577 // based on the touch start point and the maximum diameter before the
578 // show press event is sent.
579 if (type
== ET_GESTURE_SHOW_PRESS
|| type
== ET_GESTURE_TAP
||
580 type
== ET_GESTURE_TAP_UNCONFIRMED
) {
582 diameter
= max_diameter_before_show_press_
;
583 x
= tap_down_point_
.x();
584 y
= tap_down_point_
.y();
586 diameter
= event
.GetTouchMajor(i
);
590 x
= x
- diameter
/ 2;
591 y
= y
- diameter
/ 2;
592 left
= std::min(left
, x
);
593 right
= std::max(right
, x
+ diameter
);
594 top
= std::min(top
, y
);
595 bottom
= std::max(bottom
, y
+ diameter
);
597 return gfx::RectF(left
, top
, right
- left
, bottom
- top
);
600 void SetDoubleTapEnabled(bool enabled
) {
601 DCHECK(!IsDoubleTapInProgress());
602 gesture_detector_
.SetDoubleTapListener(enabled
? this : NULL
);
605 void SetMultiTouchZoomEnabled(bool enabled
) {
606 // Note that returning false from |OnScaleBegin()| or |OnScale()| prevents
607 // the detector from emitting further scale updates for the current touch
608 // sequence. Thus, if multitouch events are enabled in the middle of a
609 // gesture, it will only take effect with the next gesture.
610 ignore_multitouch_zoom_events_
= !enabled
;
613 bool IsDoubleTapInProgress() const {
614 return gesture_detector_
.is_double_tapping() ||
615 (IsScaleGestureDetectionInProgress() && InDoubleTapMode());
618 bool IsScrollInProgress() const { return scroll_event_sent_
; }
620 bool IsPinchInProgress() const { return pinch_event_sent_
; }
623 bool IsScaleGestureDetectionInProgress() const {
624 return scale_gesture_detector_
.IsInProgress();
627 bool InDoubleTapMode() const {
628 return scale_gesture_detector_
.InDoubleTapMode();
631 bool IsDoubleTapEnabled() const {
632 return gesture_detector_
.has_doubletap_listener();
635 void SetIgnoreSingleTap(bool value
) { ignore_single_tap_
= value
; }
637 const GestureProvider::Config config_
;
638 GestureProviderClient
* const client_
;
640 GestureDetector gesture_detector_
;
641 ScaleGestureDetector scale_gesture_detector_
;
642 SnapScrollController snap_scroll_controller_
;
644 base::TimeTicks current_down_time_
;
646 // Keeps track of the current GESTURE_LONG_PRESS event. If a context menu is
647 // opened after a GESTURE_LONG_PRESS, this is used to insert a
648 // GESTURE_TAP_CANCEL for removing any ::active styling.
649 base::TimeTicks current_longpress_time_
;
651 // Completely silence multi-touch (pinch) scaling events. Used in WebView when
652 // zoom support is turned off.
653 bool ignore_multitouch_zoom_events_
;
655 // TODO(klobag): This is to avoid a bug in GestureDetector. With multi-touch,
656 // always_in_tap_region_ is not reset. So when the last finger is up,
657 // |OnSingleTapUp()| will be mistakenly fired.
658 bool ignore_single_tap_
;
660 // Tracks whether {PINCH|SCROLL}_BEGIN events have been forwarded for the
661 // current touch sequence.
662 bool pinch_event_sent_
;
663 bool scroll_event_sent_
;
665 // Only track the maximum diameter before the show press event has been
666 // sent and a tap must still be possible for this touch sequence.
667 float max_diameter_before_show_press_
;
669 gfx::PointF tap_down_point_
;
671 // Tracks whether an ET_GESTURE_SHOW_PRESS event has been sent for this touch
673 bool show_press_event_sent_
;
675 DISALLOW_COPY_AND_ASSIGN(GestureListenerImpl
);
680 GestureProvider::GestureProvider(const Config
& config
,
681 GestureProviderClient
* client
)
682 : double_tap_support_for_page_(true),
683 double_tap_support_for_platform_(true),
684 gesture_begin_end_types_enabled_(config
.gesture_begin_end_types_enabled
) {
686 DCHECK(!config
.min_gesture_bounds_length
||
687 !config
.max_gesture_bounds_length
||
688 config
.min_gesture_bounds_length
<= config
.max_gesture_bounds_length
);
689 TRACE_EVENT0("input", "GestureProvider::InitGestureDetectors");
690 gesture_listener_
.reset(new GestureListenerImpl(config
, client
));
691 UpdateDoubleTapDetectionSupport();
694 GestureProvider::~GestureProvider() {
697 bool GestureProvider::OnTouchEvent(const MotionEvent
& event
) {
698 TRACE_EVENT1("input",
699 "GestureProvider::OnTouchEvent",
701 GetMotionEventActionName(event
.GetAction()));
703 DCHECK_NE(0u, event
.GetPointerCount());
705 if (!CanHandle(event
))
708 OnTouchEventHandlingBegin(event
);
709 gesture_listener_
->OnTouchEvent(event
);
710 OnTouchEventHandlingEnd(event
);
711 uma_histogram_
.RecordTouchEvent(event
);
715 void GestureProvider::ResetDetection() {
716 MotionEventGeneric
generic_cancel_event(MotionEvent::ACTION_CANCEL
,
717 base::TimeTicks::Now(),
718 PointerProperties());
719 OnTouchEvent(generic_cancel_event
);
722 void GestureProvider::SetMultiTouchZoomSupportEnabled(bool enabled
) {
723 gesture_listener_
->SetMultiTouchZoomEnabled(enabled
);
726 void GestureProvider::SetDoubleTapSupportForPlatformEnabled(bool enabled
) {
727 if (double_tap_support_for_platform_
== enabled
)
729 double_tap_support_for_platform_
= enabled
;
730 UpdateDoubleTapDetectionSupport();
733 void GestureProvider::SetDoubleTapSupportForPageEnabled(bool enabled
) {
734 if (double_tap_support_for_page_
== enabled
)
736 double_tap_support_for_page_
= enabled
;
737 UpdateDoubleTapDetectionSupport();
740 bool GestureProvider::IsScrollInProgress() const {
741 return gesture_listener_
->IsScrollInProgress();
744 bool GestureProvider::IsPinchInProgress() const {
745 return gesture_listener_
->IsPinchInProgress();
748 bool GestureProvider::IsDoubleTapInProgress() const {
749 return gesture_listener_
->IsDoubleTapInProgress();
752 bool GestureProvider::CanHandle(const MotionEvent
& event
) const {
753 // Aura requires one cancel event per touch point, whereas Android requires
754 // one cancel event per touch sequence. Thus we need to allow extra cancel
756 return current_down_event_
|| event
.GetAction() == MotionEvent::ACTION_DOWN
||
757 event
.GetAction() == MotionEvent::ACTION_CANCEL
;
760 void GestureProvider::OnTouchEventHandlingBegin(const MotionEvent
& event
) {
761 switch (event
.GetAction()) {
762 case MotionEvent::ACTION_DOWN
:
763 current_down_event_
= event
.Clone();
764 if (gesture_begin_end_types_enabled_
)
765 gesture_listener_
->Send(
766 gesture_listener_
->CreateGesture(ET_GESTURE_BEGIN
, event
));
768 case MotionEvent::ACTION_POINTER_DOWN
:
769 if (gesture_begin_end_types_enabled_
) {
770 const int action_index
= event
.GetActionIndex();
771 gesture_listener_
->Send(gesture_listener_
->CreateGesture(
775 event
.GetEventTime(),
776 event
.GetX(action_index
),
777 event
.GetY(action_index
),
778 event
.GetRawX(action_index
),
779 event
.GetRawY(action_index
),
780 event
.GetPointerCount(),
781 gesture_listener_
->GetBoundingBox(event
, ET_GESTURE_BEGIN
),
785 case MotionEvent::ACTION_POINTER_UP
:
786 case MotionEvent::ACTION_UP
:
787 case MotionEvent::ACTION_CANCEL
:
788 case MotionEvent::ACTION_MOVE
:
793 void GestureProvider::OnTouchEventHandlingEnd(const MotionEvent
& event
) {
794 switch (event
.GetAction()) {
795 case MotionEvent::ACTION_UP
:
796 case MotionEvent::ACTION_CANCEL
: {
797 if (gesture_begin_end_types_enabled_
)
798 gesture_listener_
->Send(
799 gesture_listener_
->CreateGesture(ET_GESTURE_END
, event
));
801 current_down_event_
.reset();
803 UpdateDoubleTapDetectionSupport();
806 case MotionEvent::ACTION_POINTER_UP
:
807 if (gesture_begin_end_types_enabled_
)
808 gesture_listener_
->Send(
809 gesture_listener_
->CreateGesture(ET_GESTURE_END
, event
));
811 case MotionEvent::ACTION_DOWN
:
812 case MotionEvent::ACTION_POINTER_DOWN
:
813 case MotionEvent::ACTION_MOVE
:
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
);