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/motion_event.h"
18 // Double-tap drag zoom sensitivity (speed).
19 const float kDoubleTapDragZoomSpeed
= 0.005f
;
21 const char* GetMotionEventActionName(MotionEvent::Action action
) {
23 case MotionEvent::ACTION_POINTER_DOWN
: return "ACTION_POINTER_DOWN";
24 case MotionEvent::ACTION_POINTER_UP
: return "ACTION_POINTER_UP";
25 case MotionEvent::ACTION_DOWN
: return "ACTION_DOWN";
26 case MotionEvent::ACTION_UP
: return "ACTION_UP";
27 case MotionEvent::ACTION_CANCEL
: return "ACTION_CANCEL";
28 case MotionEvent::ACTION_MOVE
: return "ACTION_MOVE";
33 gfx::RectF
GetBoundingBox(const MotionEvent
& event
) {
35 for (size_t i
= 0; i
< event
.GetPointerCount(); ++i
) {
36 float diameter
= event
.GetTouchMajor(i
);
37 bounds
.Union(gfx::RectF(event
.GetX(i
) - diameter
/ 2,
38 event
.GetY(i
) - diameter
/ 2,
45 GestureEventData
CreateGesture(EventType type
,
50 size_t touch_point_count
,
51 const gfx::RectF
& bounding_box
,
52 const GestureEventDetails
& details
) {
53 return GestureEventData(type
,
58 static_cast<int>(touch_point_count
),
63 GestureEventData
CreateGesture(EventType type
,
68 size_t touch_point_count
,
69 const gfx::RectF
& bounding_box
) {
70 return GestureEventData(type
,
75 static_cast<int>(touch_point_count
),
79 GestureEventData
CreateGesture(EventType type
,
80 const MotionEvent
& event
,
81 const GestureEventDetails
& details
) {
82 return CreateGesture(type
,
87 event
.GetPointerCount(),
88 GetBoundingBox(event
),
92 GestureEventData
CreateGesture(EventType type
,
93 const MotionEvent
& event
) {
94 return CreateGesture(type
,
99 event
.GetPointerCount(),
100 GetBoundingBox(event
));
103 GestureEventDetails
CreateTapGestureDetails(EventType type
,
104 const MotionEvent
& event
) {
105 // Set the tap count to 1 even for ET_GESTURE_DOUBLE_TAP, in order to be
106 // consistent with double tap behavior on a mobile viewport. See
107 // crbug.com/234986 for context.
108 GestureEventDetails
tap_details(type
, 1, 0);
114 // GestureProvider:::Config
116 GestureProvider::Config::Config()
117 : display(gfx::Display::kInvalidDisplayID
, gfx::Rect(1, 1)),
118 disable_click_delay(false),
119 gesture_begin_end_types_enabled(false) {}
121 GestureProvider::Config::~Config() {}
123 // GestureProvider::ScaleGestureListener
125 class GestureProvider::ScaleGestureListenerImpl
126 : public ScaleGestureDetector::ScaleGestureListener
{
128 ScaleGestureListenerImpl(const ScaleGestureDetector::Config
& config
,
129 GestureProvider
* provider
)
130 : scale_gesture_detector_(config
, this),
132 ignore_multitouch_events_(false),
133 pinch_event_sent_(false),
134 min_pinch_update_span_delta_(config
.min_pinch_update_span_delta
) {}
136 bool OnTouchEvent(const MotionEvent
& event
) {
137 // TODO: Need to deal with multi-touch transition.
138 const bool in_scale_gesture
= IsScaleGestureDetectionInProgress();
139 bool handled
= scale_gesture_detector_
.OnTouchEvent(event
);
140 if (!in_scale_gesture
&&
141 (event
.GetAction() == MotionEvent::ACTION_UP
||
142 event
.GetAction() == MotionEvent::ACTION_CANCEL
)) {
148 // ScaleGestureDetector::ScaleGestureListener implementation.
149 virtual bool OnScaleBegin(const ScaleGestureDetector
& detector
,
150 const MotionEvent
& e
) OVERRIDE
{
151 if (ignore_multitouch_events_
&& !detector
.InDoubleTapMode())
153 pinch_event_sent_
= false;
157 virtual void OnScaleEnd(const ScaleGestureDetector
& detector
,
158 const MotionEvent
& e
) OVERRIDE
{
159 if (!pinch_event_sent_
)
161 provider_
->Send(CreateGesture(ET_GESTURE_PINCH_END
,
163 detector
.GetEventTime(),
168 pinch_event_sent_
= false;
171 virtual bool OnScale(const ScaleGestureDetector
& detector
,
172 const MotionEvent
& e
) OVERRIDE
{
173 if (ignore_multitouch_events_
&& !detector
.InDoubleTapMode())
175 if (!pinch_event_sent_
) {
176 pinch_event_sent_
= true;
177 provider_
->Send(CreateGesture(ET_GESTURE_PINCH_BEGIN
,
179 detector
.GetEventTime(),
180 detector
.GetFocusX(),
181 detector
.GetFocusY(),
186 if (std::abs(detector
.GetCurrentSpan() - detector
.GetPreviousSpan()) <
187 min_pinch_update_span_delta_
) {
191 float scale
= detector
.GetScaleFactor();
195 if (detector
.InDoubleTapMode()) {
196 // Relative changes in the double-tap scale factor computed by |detector|
197 // diminish as the touch moves away from the original double-tap focus.
198 // For historical reasons, Chrome has instead adopted a scale factor
199 // computation that is invariant to the focal distance, where
200 // the scale delta remains constant if the touch velocity is constant.
202 (detector
.GetCurrentSpanY() - detector
.GetPreviousSpanY()) * 0.5f
;
203 scale
= std::pow(scale
> 1 ? 1.0f
+ kDoubleTapDragZoomSpeed
204 : 1.0f
- kDoubleTapDragZoomSpeed
,
207 GestureEventDetails
pinch_details(ET_GESTURE_PINCH_UPDATE
, scale
, 0);
208 provider_
->Send(CreateGesture(ET_GESTURE_PINCH_UPDATE
,
210 detector
.GetEventTime(),
211 detector
.GetFocusX(),
212 detector
.GetFocusY(),
219 void SetDoubleTapEnabled(bool enabled
) {
220 DCHECK(!IsDoubleTapInProgress());
221 scale_gesture_detector_
.SetQuickScaleEnabled(enabled
);
224 void SetMultiTouchEnabled(bool enabled
) {
225 // Note that returning false from OnScaleBegin / OnScale makes the
226 // gesture detector not to emit further scaling notifications
227 // related to this gesture. Thus, if detector events are enabled in
228 // the middle of the gesture, we don't need to do anything.
229 ignore_multitouch_events_
= !enabled
;
232 bool IsDoubleTapInProgress() const {
233 return IsScaleGestureDetectionInProgress() && InDoubleTapMode();
236 bool IsScaleGestureDetectionInProgress() const {
237 return scale_gesture_detector_
.IsInProgress();
241 bool InDoubleTapMode() const {
242 return scale_gesture_detector_
.InDoubleTapMode();
245 ScaleGestureDetector scale_gesture_detector_
;
247 GestureProvider
* const provider_
;
249 // Completely silence multi-touch (pinch) scaling events. Used in WebView when
250 // zoom support is turned off.
251 bool ignore_multitouch_events_
;
253 // Whether any pinch zoom event has been sent to native.
254 bool pinch_event_sent_
;
256 // The minimum change in span required before this is considered a pinch. See
258 float min_pinch_update_span_delta_
;
260 DISALLOW_COPY_AND_ASSIGN(ScaleGestureListenerImpl
);
263 // GestureProvider::GestureListener
265 class GestureProvider::GestureListenerImpl
266 : public GestureDetector::GestureListener
,
267 public GestureDetector::DoubleTapListener
{
270 const gfx::Display
& display
,
271 const GestureDetector::Config
& gesture_detector_config
,
272 bool disable_click_delay
,
273 GestureProvider
* provider
)
274 : gesture_detector_(gesture_detector_config
, this, this),
275 snap_scroll_controller_(display
),
277 disable_click_delay_(disable_click_delay
),
278 touch_slop_(gesture_detector_config
.touch_slop
),
279 double_tap_timeout_(gesture_detector_config
.double_tap_timeout
),
280 ignore_single_tap_(false),
281 seen_first_scroll_event_(false) {}
283 virtual ~GestureListenerImpl() {}
285 bool OnTouchEvent(const MotionEvent
& e
,
286 bool is_scale_gesture_detection_in_progress
) {
287 snap_scroll_controller_
.SetSnapScrollingMode(
288 e
, is_scale_gesture_detection_in_progress
);
290 if (is_scale_gesture_detection_in_progress
)
291 SetIgnoreSingleTap(true);
293 if (e
.GetAction() == MotionEvent::ACTION_DOWN
)
294 gesture_detector_
.set_longpress_enabled(true);
296 return gesture_detector_
.OnTouchEvent(e
);
299 // GestureDetector::GestureListener implementation.
300 virtual bool OnDown(const MotionEvent
& e
) OVERRIDE
{
301 current_down_time_
= e
.GetEventTime();
302 ignore_single_tap_
= false;
303 seen_first_scroll_event_
= false;
305 GestureEventDetails
tap_details(ET_GESTURE_TAP_DOWN
, 0, 0);
306 provider_
->Send(CreateGesture(ET_GESTURE_TAP_DOWN
, e
, tap_details
));
308 // Return true to indicate that we want to handle touch.
312 virtual bool OnScroll(const MotionEvent
& e1
,
313 const MotionEvent
& e2
,
314 float raw_distance_x
,
315 float raw_distance_y
) OVERRIDE
{
316 float distance_x
= raw_distance_x
;
317 float distance_y
= raw_distance_y
;
318 if (!seen_first_scroll_event_
) {
319 // Remove the touch slop region from the first scroll event to avoid a
321 seen_first_scroll_event_
= true;
323 std::sqrt(distance_x
* distance_x
+ distance_y
* distance_y
);
324 double epsilon
= 1e-3;
325 if (distance
> epsilon
) {
326 double ratio
= std::max(0., distance
- touch_slop_
) / distance
;
331 snap_scroll_controller_
.UpdateSnapScrollMode(distance_x
, distance_y
);
332 if (snap_scroll_controller_
.IsSnappingScrolls()) {
333 if (snap_scroll_controller_
.IsSnapHorizontal()) {
340 if (!provider_
->IsScrollInProgress()) {
341 // Note that scroll start hints are in distance traveled, where
342 // scroll deltas are in the opposite direction.
343 GestureEventDetails
scroll_details(
344 ET_GESTURE_SCROLL_BEGIN
, -raw_distance_x
, -raw_distance_y
);
346 // Use the co-ordinates from the touch down, as these co-ordinates are
347 // used to determine which layer the scroll should affect.
348 provider_
->Send(CreateGesture(ET_GESTURE_SCROLL_BEGIN
,
353 e2
.GetPointerCount(),
358 if (distance_x
|| distance_y
) {
359 GestureEventDetails
scroll_details(
360 ET_GESTURE_SCROLL_UPDATE
, -distance_x
, -distance_y
);
362 CreateGesture(ET_GESTURE_SCROLL_UPDATE
, e2
, scroll_details
));
368 virtual bool OnFling(const MotionEvent
& e1
,
369 const MotionEvent
& e2
,
371 float velocity_y
) OVERRIDE
{
372 if (snap_scroll_controller_
.IsSnappingScrolls()) {
373 if (snap_scroll_controller_
.IsSnapHorizontal()) {
380 provider_
->Fling(e2
, velocity_x
, velocity_y
);
384 virtual bool OnSwipe(const MotionEvent
& e1
,
385 const MotionEvent
& e2
,
387 float velocity_y
) OVERRIDE
{
388 GestureEventDetails
swipe_details(ET_GESTURE_SWIPE
, velocity_x
, velocity_y
);
389 provider_
->Send(CreateGesture(ET_GESTURE_SWIPE
, e2
, swipe_details
));
393 virtual bool OnTwoFingerTap(const MotionEvent
& e1
,
394 const MotionEvent
& e2
) OVERRIDE
{
395 // The location of the two finger tap event should be the location of the
397 GestureEventDetails
two_finger_tap_details(ET_GESTURE_TWO_FINGER_TAP
,
400 provider_
->Send(CreateGesture(ET_GESTURE_TWO_FINGER_TAP
,
405 e2
.GetPointerCount(),
407 two_finger_tap_details
));
411 virtual void OnShowPress(const MotionEvent
& e
) OVERRIDE
{
412 GestureEventDetails
show_press_details(ET_GESTURE_SHOW_PRESS
, 0, 0);
414 CreateGesture(ET_GESTURE_SHOW_PRESS
, e
, show_press_details
));
417 virtual bool OnSingleTapUp(const MotionEvent
& e
) OVERRIDE
{
418 // This is a hack to address the issue where user hovers
419 // over a link for longer than double_tap_timeout_, then
420 // OnSingleTapConfirmed() is not triggered. But we still
421 // want to trigger the tap event at UP. So we override
422 // OnSingleTapUp() in this case. This assumes singleTapUp
423 // gets always called before singleTapConfirmed.
424 if (!ignore_single_tap_
) {
425 if (e
.GetEventTime() - current_down_time_
> double_tap_timeout_
) {
426 return OnSingleTapConfirmed(e
);
427 } else if (!IsDoubleTapEnabled() || disable_click_delay_
) {
428 // If double-tap has been disabled, there is no need to wait
429 // for the double-tap timeout.
430 return OnSingleTapConfirmed(e
);
432 // Notify Blink about this tapUp event anyway, when none of the above
433 // conditions applied.
434 provider_
->Send(CreateGesture(
435 ET_GESTURE_TAP_UNCONFIRMED
,
437 CreateTapGestureDetails(ET_GESTURE_TAP_UNCONFIRMED
, e
)));
441 return provider_
->SendLongTapIfNecessary(e
);
444 // GestureDetector::DoubleTapListener implementation.
445 virtual bool OnSingleTapConfirmed(const MotionEvent
& e
) OVERRIDE
{
446 // Long taps in the edges of the screen have their events delayed by
447 // ContentViewHolder for tab swipe operations. As a consequence of the delay
448 // this method might be called after receiving the up event.
449 // These corner cases should be ignored.
450 if (ignore_single_tap_
)
453 ignore_single_tap_
= true;
455 provider_
->Send(CreateGesture(
456 ET_GESTURE_TAP
, e
, CreateTapGestureDetails(ET_GESTURE_TAP
, e
)));
460 virtual bool OnDoubleTap(const MotionEvent
& e
) OVERRIDE
{ return false; }
462 virtual bool OnDoubleTapEvent(const MotionEvent
& e
) OVERRIDE
{
463 switch (e
.GetAction()) {
464 case MotionEvent::ACTION_DOWN
:
465 gesture_detector_
.set_longpress_enabled(false);
468 case MotionEvent::ACTION_UP
:
469 if (!provider_
->IsPinchInProgress() &&
470 !provider_
->IsScrollInProgress()) {
472 CreateGesture(ET_GESTURE_DOUBLE_TAP
,
474 CreateTapGestureDetails(ET_GESTURE_DOUBLE_TAP
, e
)));
484 virtual bool OnLongPress(const MotionEvent
& e
) OVERRIDE
{
485 DCHECK(!IsDoubleTapInProgress());
486 SetIgnoreSingleTap(true);
488 GestureEventDetails
long_press_details(ET_GESTURE_LONG_PRESS
, 0, 0);
490 CreateGesture(ET_GESTURE_LONG_PRESS
, e
, long_press_details
));
492 // Returning true puts the GestureDetector in "longpress" mode, disabling
493 // further scrolling. This is undesirable, as it is quite common for a
494 // longpress gesture to fire on content that won't trigger a context menu.
498 void SetDoubleTapEnabled(bool enabled
) {
499 DCHECK(!IsDoubleTapInProgress());
500 gesture_detector_
.SetDoubleTapListener(enabled
? this : NULL
);
503 bool IsDoubleTapInProgress() const {
504 return gesture_detector_
.is_double_tapping();
508 void SetIgnoreSingleTap(bool value
) { ignore_single_tap_
= value
; }
510 bool IsDoubleTapEnabled() const {
511 return gesture_detector_
.has_doubletap_listener();
514 GestureDetector gesture_detector_
;
515 SnapScrollController snap_scroll_controller_
;
517 GestureProvider
* const provider_
;
519 // Whether the click delay should always be disabled by sending clicks for
520 // double-tap gestures.
521 const bool disable_click_delay_
;
523 const float touch_slop_
;
525 const base::TimeDelta double_tap_timeout_
;
527 base::TimeTicks current_down_time_
;
529 // TODO(klobag): This is to avoid a bug in GestureDetector. With multi-touch,
530 // always_in_tap_region_ is not reset. So when the last finger is up,
531 // OnSingleTapUp() will be mistakenly fired.
532 bool ignore_single_tap_
;
534 // Used to remove the touch slop from the initial scroll event in a scroll
536 bool seen_first_scroll_event_
;
538 DISALLOW_COPY_AND_ASSIGN(GestureListenerImpl
);
543 GestureProvider::GestureProvider(const Config
& config
,
544 GestureProviderClient
* client
)
546 touch_scroll_in_progress_(false),
547 pinch_in_progress_(false),
548 double_tap_support_for_page_(true),
549 double_tap_support_for_platform_(true),
550 gesture_begin_end_types_enabled_(config
.gesture_begin_end_types_enabled
) {
552 InitGestureDetectors(config
);
555 GestureProvider::~GestureProvider() {}
557 bool GestureProvider::OnTouchEvent(const MotionEvent
& event
) {
558 TRACE_EVENT1("input", "GestureProvider::OnTouchEvent",
559 "action", GetMotionEventActionName(event
.GetAction()));
561 DCHECK_NE(0u, event
.GetPointerCount());
563 if (!CanHandle(event
))
566 const bool in_scale_gesture
=
567 scale_gesture_listener_
->IsScaleGestureDetectionInProgress();
569 OnTouchEventHandlingBegin(event
);
570 gesture_listener_
->OnTouchEvent(event
, in_scale_gesture
);
571 scale_gesture_listener_
->OnTouchEvent(event
);
572 OnTouchEventHandlingEnd(event
);
576 void GestureProvider::SetMultiTouchZoomSupportEnabled(bool enabled
) {
577 scale_gesture_listener_
->SetMultiTouchEnabled(enabled
);
580 void GestureProvider::SetDoubleTapSupportForPlatformEnabled(bool enabled
) {
581 if (double_tap_support_for_platform_
== enabled
)
583 double_tap_support_for_platform_
= enabled
;
584 UpdateDoubleTapDetectionSupport();
587 void GestureProvider::SetDoubleTapSupportForPageEnabled(bool enabled
) {
588 if (double_tap_support_for_page_
== enabled
)
590 double_tap_support_for_page_
= enabled
;
591 UpdateDoubleTapDetectionSupport();
594 bool GestureProvider::IsScrollInProgress() const {
595 // TODO(wangxianzhu): Also return true when fling is active once the UI knows
596 // exactly when the fling ends.
597 return touch_scroll_in_progress_
;
600 bool GestureProvider::IsPinchInProgress() const { return pinch_in_progress_
; }
602 bool GestureProvider::IsDoubleTapInProgress() const {
603 return gesture_listener_
->IsDoubleTapInProgress() ||
604 scale_gesture_listener_
->IsDoubleTapInProgress();
607 void GestureProvider::InitGestureDetectors(const Config
& config
) {
608 TRACE_EVENT0("input", "GestureProvider::InitGestureDetectors");
609 gesture_listener_
.reset(
610 new GestureListenerImpl(config
.display
,
611 config
.gesture_detector_config
,
612 config
.disable_click_delay
,
615 scale_gesture_listener_
.reset(
616 new ScaleGestureListenerImpl(config
.scale_gesture_detector_config
, this));
618 UpdateDoubleTapDetectionSupport();
621 bool GestureProvider::CanHandle(const MotionEvent
& event
) const {
622 return event
.GetAction() == MotionEvent::ACTION_DOWN
|| current_down_event_
;
625 void GestureProvider::Fling(const MotionEvent
& event
,
628 if (!velocity_x
&& !velocity_y
) {
629 EndTouchScrollIfNecessary(event
, true);
633 if (!touch_scroll_in_progress_
) {
634 // The native side needs a ET_GESTURE_SCROLL_BEGIN before
635 // ET_SCROLL_FLING_START to send the fling to the correct target. Send if it
636 // has not sent. The distance traveled in one second is a reasonable scroll
638 GestureEventDetails
scroll_details(
639 ET_GESTURE_SCROLL_BEGIN
, velocity_x
, velocity_y
);
640 Send(CreateGesture(ET_GESTURE_SCROLL_BEGIN
, event
, scroll_details
));
642 EndTouchScrollIfNecessary(event
, false);
644 GestureEventDetails
fling_details(
645 ET_SCROLL_FLING_START
, velocity_x
, velocity_y
);
647 ET_SCROLL_FLING_START
, event
, fling_details
));
650 void GestureProvider::Send(const GestureEventData
& gesture
) {
651 DCHECK(!gesture
.time
.is_null());
652 // The only valid events that should be sent without an active touch sequence
653 // are SHOW_PRESS and TAP, potentially triggered by the double-tap
655 DCHECK(current_down_event_
|| gesture
.type
== ET_GESTURE_TAP
||
656 gesture
.type
== ET_GESTURE_SHOW_PRESS
);
658 switch (gesture
.type
) {
659 case ET_GESTURE_LONG_PRESS
:
660 DCHECK(!scale_gesture_listener_
->IsScaleGestureDetectionInProgress());
661 current_longpress_time_
= gesture
.time
;
663 case ET_GESTURE_LONG_TAP
:
664 current_longpress_time_
= base::TimeTicks();
666 case ET_GESTURE_SCROLL_BEGIN
:
667 DCHECK(!touch_scroll_in_progress_
);
668 touch_scroll_in_progress_
= true;
670 case ET_GESTURE_SCROLL_END
:
671 DCHECK(touch_scroll_in_progress_
);
672 if (pinch_in_progress_
)
673 Send(CreateGesture(ET_GESTURE_PINCH_END
,
674 gesture
.motion_event_id
,
678 gesture
.details
.touch_points(),
679 gesture
.details
.bounding_box()));
680 touch_scroll_in_progress_
= false;
682 case ET_GESTURE_PINCH_BEGIN
:
683 DCHECK(!pinch_in_progress_
);
684 if (!touch_scroll_in_progress_
)
685 Send(CreateGesture(ET_GESTURE_SCROLL_BEGIN
,
686 gesture
.motion_event_id
,
690 gesture
.details
.touch_points(),
691 gesture
.details
.bounding_box()));
692 pinch_in_progress_
= true;
694 case ET_GESTURE_PINCH_END
:
695 DCHECK(pinch_in_progress_
);
696 pinch_in_progress_
= false;
698 case ET_GESTURE_SHOW_PRESS
:
699 // It's possible that a double-tap drag zoom (from ScaleGestureDetector)
700 // will start before the press gesture fires (from GestureDetector), in
701 // which case the press should simply be dropped.
702 if (pinch_in_progress_
|| touch_scroll_in_progress_
)
708 client_
->OnGestureEvent(gesture
);
711 bool GestureProvider::SendLongTapIfNecessary(const MotionEvent
& event
) {
712 if (event
.GetAction() == MotionEvent::ACTION_UP
&&
713 !current_longpress_time_
.is_null() &&
714 !scale_gesture_listener_
->IsScaleGestureDetectionInProgress()) {
715 GestureEventDetails
long_tap_details(ET_GESTURE_LONG_TAP
, 0, 0);
716 Send(CreateGesture(ET_GESTURE_LONG_TAP
, event
, long_tap_details
));
722 void GestureProvider::EndTouchScrollIfNecessary(const MotionEvent
& event
,
723 bool send_scroll_end_event
) {
724 if (!touch_scroll_in_progress_
)
726 if (send_scroll_end_event
)
727 Send(CreateGesture(ET_GESTURE_SCROLL_END
, event
));
728 touch_scroll_in_progress_
= false;
731 void GestureProvider::OnTouchEventHandlingBegin(const MotionEvent
& event
) {
732 switch (event
.GetAction()) {
733 case MotionEvent::ACTION_DOWN
:
734 current_down_event_
= event
.Clone();
735 touch_scroll_in_progress_
= false;
736 pinch_in_progress_
= false;
737 current_longpress_time_
= base::TimeTicks();
738 if (gesture_begin_end_types_enabled_
)
739 Send(CreateGesture(ET_GESTURE_BEGIN
, event
));
741 case MotionEvent::ACTION_POINTER_DOWN
:
742 if (gesture_begin_end_types_enabled_
)
743 Send(CreateGesture(ET_GESTURE_BEGIN
, event
));
745 case MotionEvent::ACTION_POINTER_UP
:
746 case MotionEvent::ACTION_UP
:
747 case MotionEvent::ACTION_CANCEL
:
748 case MotionEvent::ACTION_MOVE
:
753 void GestureProvider::OnTouchEventHandlingEnd(const MotionEvent
& event
) {
754 switch (event
.GetAction()) {
755 case MotionEvent::ACTION_UP
:
756 case MotionEvent::ACTION_CANCEL
:
757 // Note: This call will have no effect if a fling was just generated, as
758 // |Fling()| will have already signalled an end to touch-scrolling.
759 EndTouchScrollIfNecessary(event
, true);
761 if (gesture_begin_end_types_enabled_
)
762 Send(CreateGesture(ET_GESTURE_END
, event
));
764 current_down_event_
.reset();
766 UpdateDoubleTapDetectionSupport();
768 case MotionEvent::ACTION_POINTER_UP
:
769 if (gesture_begin_end_types_enabled_
)
770 Send(CreateGesture(ET_GESTURE_END
, event
));
772 case MotionEvent::ACTION_DOWN
:
773 case MotionEvent::ACTION_POINTER_DOWN
:
774 case MotionEvent::ACTION_MOVE
:
779 void GestureProvider::UpdateDoubleTapDetectionSupport() {
780 // The GestureDetector requires that any provided DoubleTapListener remain
781 // attached to it for the duration of a touch sequence. Defer any potential
782 // null'ing of the listener until the sequence has ended.
783 if (current_down_event_
)
786 const bool double_tap_enabled
= double_tap_support_for_page_
&&
787 double_tap_support_for_platform_
;
788 gesture_listener_
->SetDoubleTapEnabled(double_tap_enabled
);
789 scale_gesture_listener_
->SetDoubleTapEnabled(double_tap_enabled
);