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
:
24 return "ACTION_POINTER_DOWN";
25 case MotionEvent::ACTION_POINTER_UP
:
26 return "ACTION_POINTER_UP";
27 case MotionEvent::ACTION_DOWN
:
29 case MotionEvent::ACTION_UP
:
31 case MotionEvent::ACTION_CANCEL
:
32 return "ACTION_CANCEL";
33 case MotionEvent::ACTION_MOVE
:
39 gfx::RectF
GetBoundingBox(const MotionEvent
& event
) {
40 // Can't use gfx::RectF::Union, as it ignores touches with a radius of 0.
41 float left
= std::numeric_limits
<float>::max();
42 float top
= std::numeric_limits
<float>::max();
43 float right
= -std::numeric_limits
<float>::max();
44 float bottom
= -std::numeric_limits
<float>::max();
45 for (size_t i
= 0; i
< event
.GetPointerCount(); ++i
) {
46 float diameter
= event
.GetTouchMajor(i
);
47 float x
= event
.GetX(i
) - diameter
/ 2;
48 float y
= event
.GetY(i
) - diameter
/ 2;
49 left
= std::min(left
, x
);
50 right
= std::max(right
, x
+ diameter
);
51 top
= std::min(top
, y
);
52 bottom
= std::max(bottom
, y
+ diameter
);
54 return gfx::RectF(left
, top
, right
- left
, bottom
- top
);
57 GestureEventData
CreateGesture(const GestureEventDetails
& details
,
59 MotionEvent::ToolType primary_tool_type
,
65 size_t touch_point_count
,
66 const gfx::RectF
& bounding_box
) {
67 return GestureEventData(details
,
79 GestureEventData
CreateGesture(EventType type
,
81 MotionEvent::ToolType primary_tool_type
,
87 size_t touch_point_count
,
88 const gfx::RectF
& bounding_box
) {
89 return GestureEventData(GestureEventDetails(type
, 0, 0),
101 GestureEventData
CreateGesture(const GestureEventDetails
& details
,
102 const MotionEvent
& event
) {
103 return GestureEventData(details
,
106 event
.GetEventTime(),
111 event
.GetPointerCount(),
112 GetBoundingBox(event
));
115 GestureEventData
CreateGesture(EventType type
, const MotionEvent
& event
) {
116 return CreateGesture(GestureEventDetails(type
, 0, 0), event
);
119 GestureEventData
CreateTapGesture(EventType type
, const MotionEvent
& event
) {
120 // Set the tap count to 1 even for ET_GESTURE_DOUBLE_TAP, in order to be
121 // consistent with double tap behavior on a mobile viewport. See
122 // crbug.com/234986 for context.
123 return CreateGesture(GestureEventDetails(type
, 1, 0), event
);
126 gfx::RectF
ClampBoundingBox(const gfx::RectF
& bounds
,
129 float width
= bounds
.width();
130 float height
= bounds
.height();
132 width
= std::max(min_length
, width
);
133 height
= std::max(min_length
, height
);
136 width
= std::min(max_length
, width
);
137 height
= std::min(max_length
, height
);
139 const gfx::PointF center
= bounds
.CenterPoint();
141 center
.x() - width
/ 2.f
, center
.y() - height
/ 2.f
, width
, height
);
146 // GestureProvider:::Config
148 GestureProvider::Config::Config()
149 : display(gfx::Display::kInvalidDisplayID
, gfx::Rect(1, 1)),
150 disable_click_delay(false),
151 gesture_begin_end_types_enabled(false),
152 min_gesture_bounds_length(0),
153 max_gesture_bounds_length(0) {
156 GestureProvider::Config::~Config() {
159 // GestureProvider::GestureListener
161 class GestureProvider::GestureListenerImpl
162 : public ScaleGestureDetector::ScaleGestureListener
,
163 public GestureDetector::GestureListener
,
164 public GestureDetector::DoubleTapListener
{
166 GestureListenerImpl(const GestureProvider::Config
& config
,
167 GestureProviderClient
* client
)
170 gesture_detector_(config
.gesture_detector_config
, this, this),
171 scale_gesture_detector_(config
.scale_gesture_detector_config
, this),
172 snap_scroll_controller_(config
.display
),
173 ignore_multitouch_zoom_events_(false),
174 ignore_single_tap_(false),
175 pinch_event_sent_(false),
176 scroll_event_sent_(false) {}
178 void OnTouchEvent(const MotionEvent
& event
) {
179 const bool in_scale_gesture
= IsScaleGestureDetectionInProgress();
180 snap_scroll_controller_
.SetSnapScrollingMode(event
, in_scale_gesture
);
181 if (in_scale_gesture
)
182 SetIgnoreSingleTap(true);
184 const MotionEvent::Action action
= event
.GetAction();
185 if (action
== MotionEvent::ACTION_DOWN
) {
186 current_down_time_
= event
.GetEventTime();
187 current_longpress_time_
= base::TimeTicks();
188 ignore_single_tap_
= false;
189 scroll_event_sent_
= false;
190 pinch_event_sent_
= false;
191 gesture_detector_
.set_longpress_enabled(true);
194 gesture_detector_
.OnTouchEvent(event
);
195 scale_gesture_detector_
.OnTouchEvent(event
);
197 if (action
== MotionEvent::ACTION_UP
||
198 action
== MotionEvent::ACTION_CANCEL
) {
199 // Note: This call will have no effect if a fling was just generated, as
200 // |Fling()| will have already signalled an end to touch-scrolling.
201 if (scroll_event_sent_
)
202 Send(CreateGesture(ET_GESTURE_SCROLL_END
, event
));
203 current_down_time_
= base::TimeTicks();
207 void Send(GestureEventData gesture
) {
208 DCHECK(!gesture
.time
.is_null());
209 // The only valid events that should be sent without an active touch
210 // sequence are SHOW_PRESS and TAP, potentially triggered by the double-tap
212 DCHECK(!current_down_time_
.is_null() || gesture
.type() == ET_GESTURE_TAP
||
213 gesture
.type() == ET_GESTURE_SHOW_PRESS
||
214 gesture
.type() == ET_GESTURE_BEGIN
||
215 gesture
.type() == ET_GESTURE_END
);
217 if (gesture
.primary_tool_type
== MotionEvent::TOOL_TYPE_UNKNOWN
||
218 gesture
.primary_tool_type
== MotionEvent::TOOL_TYPE_FINGER
) {
219 gesture
.details
.set_bounding_box(
220 ClampBoundingBox(gesture
.details
.bounding_box_f(),
221 config_
.min_gesture_bounds_length
,
222 config_
.max_gesture_bounds_length
));
225 switch (gesture
.type()) {
226 case ET_GESTURE_LONG_PRESS
:
227 DCHECK(!IsScaleGestureDetectionInProgress());
228 current_longpress_time_
= gesture
.time
;
230 case ET_GESTURE_LONG_TAP
:
231 current_longpress_time_
= base::TimeTicks();
233 case ET_GESTURE_SCROLL_BEGIN
:
234 DCHECK(!scroll_event_sent_
);
235 scroll_event_sent_
= true;
237 case ET_GESTURE_SCROLL_END
:
238 DCHECK(scroll_event_sent_
);
239 if (pinch_event_sent_
)
240 Send(GestureEventData(ET_GESTURE_PINCH_END
, gesture
));
241 scroll_event_sent_
= false;
243 case ET_SCROLL_FLING_START
:
244 DCHECK(scroll_event_sent_
);
245 scroll_event_sent_
= false;
247 case ET_GESTURE_PINCH_BEGIN
:
248 DCHECK(!pinch_event_sent_
);
249 if (!scroll_event_sent_
)
250 Send(GestureEventData(ET_GESTURE_SCROLL_BEGIN
, gesture
));
251 pinch_event_sent_
= true;
253 case ET_GESTURE_PINCH_END
:
254 DCHECK(pinch_event_sent_
);
255 pinch_event_sent_
= false;
257 case ET_GESTURE_SHOW_PRESS
:
258 // It's possible that a double-tap drag zoom (from ScaleGestureDetector)
259 // will start before the press gesture fires (from GestureDetector), in
260 // which case the press should simply be dropped.
261 if (pinch_event_sent_
|| scroll_event_sent_
)
267 client_
->OnGestureEvent(gesture
);
270 // ScaleGestureDetector::ScaleGestureListener implementation.
271 virtual bool OnScaleBegin(const ScaleGestureDetector
& detector
,
272 const MotionEvent
& e
) OVERRIDE
{
273 if (ignore_multitouch_zoom_events_
&& !detector
.InDoubleTapMode())
278 virtual void OnScaleEnd(const ScaleGestureDetector
& detector
,
279 const MotionEvent
& e
) OVERRIDE
{
280 if (!pinch_event_sent_
)
282 Send(CreateGesture(ET_GESTURE_PINCH_END
, e
));
285 virtual bool OnScale(const ScaleGestureDetector
& detector
,
286 const MotionEvent
& e
) OVERRIDE
{
287 if (ignore_multitouch_zoom_events_
&& !detector
.InDoubleTapMode())
289 if (!pinch_event_sent_
) {
290 Send(CreateGesture(ET_GESTURE_PINCH_BEGIN
,
293 detector
.GetEventTime(),
294 detector
.GetFocusX(),
295 detector
.GetFocusY(),
296 detector
.GetFocusX() + e
.GetRawOffsetX(),
297 detector
.GetFocusY() + e
.GetRawOffsetY(),
302 if (std::abs(detector
.GetCurrentSpan() - detector
.GetPreviousSpan()) <
303 config_
.scale_gesture_detector_config
.min_pinch_update_span_delta
) {
307 float scale
= detector
.GetScaleFactor();
311 if (detector
.InDoubleTapMode()) {
312 // Relative changes in the double-tap scale factor computed by |detector|
313 // diminish as the touch moves away from the original double-tap focus.
314 // For historical reasons, Chrome has instead adopted a scale factor
315 // computation that is invariant to the focal distance, where
316 // the scale delta remains constant if the touch velocity is constant.
318 (detector
.GetCurrentSpanY() - detector
.GetPreviousSpanY()) * 0.5f
;
319 scale
= std::pow(scale
> 1 ? 1.0f
+ kDoubleTapDragZoomSpeed
320 : 1.0f
- kDoubleTapDragZoomSpeed
,
323 GestureEventDetails
pinch_details(ET_GESTURE_PINCH_UPDATE
, scale
, 0);
324 Send(CreateGesture(pinch_details
,
327 detector
.GetEventTime(),
328 detector
.GetFocusX(),
329 detector
.GetFocusY(),
330 detector
.GetFocusX() + e
.GetRawOffsetX(),
331 detector
.GetFocusY() + e
.GetRawOffsetY(),
337 // GestureDetector::GestureListener implementation.
338 virtual bool OnDown(const MotionEvent
& e
) OVERRIDE
{
339 GestureEventDetails
tap_details(ET_GESTURE_TAP_DOWN
, 0, 0);
340 Send(CreateGesture(tap_details
, e
));
342 // Return true to indicate that we want to handle touch.
346 virtual bool OnScroll(const MotionEvent
& e1
,
347 const MotionEvent
& e2
,
348 float raw_distance_x
,
349 float raw_distance_y
) OVERRIDE
{
350 float distance_x
= raw_distance_x
;
351 float distance_y
= raw_distance_y
;
352 if (!scroll_event_sent_
) {
353 // Remove the touch slop region from the first scroll event to avoid a
356 std::sqrt(distance_x
* distance_x
+ distance_y
* distance_y
);
357 double epsilon
= 1e-3;
358 if (distance
> epsilon
) {
361 distance
- config_
.gesture_detector_config
.touch_slop
) /
367 // Note that scroll start hints are in distance traveled, where
368 // scroll deltas are in the opposite direction.
369 GestureEventDetails
scroll_details(
370 ET_GESTURE_SCROLL_BEGIN
, -raw_distance_x
, -raw_distance_y
);
372 // Use the co-ordinates from the touch down, as these co-ordinates are
373 // used to determine which layer the scroll should affect.
374 Send(CreateGesture(scroll_details
,
382 e2
.GetPointerCount(),
383 GetBoundingBox(e2
)));
384 DCHECK(scroll_event_sent_
);
387 snap_scroll_controller_
.UpdateSnapScrollMode(distance_x
, distance_y
);
388 if (snap_scroll_controller_
.IsSnappingScrolls()) {
389 if (snap_scroll_controller_
.IsSnapHorizontal())
395 if (distance_x
|| distance_y
) {
396 const gfx::RectF bounding_box
= GetBoundingBox(e2
);
397 const gfx::PointF center
= bounding_box
.CenterPoint();
398 const gfx::PointF raw_center
=
399 center
+ gfx::Vector2dF(e2
.GetRawOffsetX(), e2
.GetRawOffsetY());
400 GestureEventDetails
scroll_details(
401 ET_GESTURE_SCROLL_UPDATE
, -distance_x
, -distance_y
);
402 Send(CreateGesture(scroll_details
,
410 e2
.GetPointerCount(),
417 virtual bool OnFling(const MotionEvent
& e1
,
418 const MotionEvent
& e2
,
420 float velocity_y
) OVERRIDE
{
421 if (snap_scroll_controller_
.IsSnappingScrolls()) {
422 if (snap_scroll_controller_
.IsSnapHorizontal()) {
429 if (!velocity_x
&& !velocity_y
)
432 if (!scroll_event_sent_
) {
433 // The native side needs a ET_GESTURE_SCROLL_BEGIN before
434 // ET_SCROLL_FLING_START to send the fling to the correct target.
435 // The distance traveled in one second is a reasonable scroll start hint.
436 GestureEventDetails
scroll_details(
437 ET_GESTURE_SCROLL_BEGIN
, velocity_x
, velocity_y
);
438 Send(CreateGesture(scroll_details
, e2
));
441 GestureEventDetails
fling_details(
442 ET_SCROLL_FLING_START
, velocity_x
, velocity_y
);
443 Send(CreateGesture(fling_details
, e2
));
447 virtual bool OnSwipe(const MotionEvent
& e1
,
448 const MotionEvent
& e2
,
450 float velocity_y
) OVERRIDE
{
451 GestureEventDetails
swipe_details(ET_GESTURE_SWIPE
, velocity_x
, velocity_y
);
452 Send(CreateGesture(swipe_details
, e2
));
456 virtual bool OnTwoFingerTap(const MotionEvent
& e1
,
457 const MotionEvent
& e2
) OVERRIDE
{
458 // The location of the two finger tap event should be the location of the
460 GestureEventDetails
two_finger_tap_details(
461 ET_GESTURE_TWO_FINGER_TAP
, e1
.GetTouchMajor(), e1
.GetTouchMajor());
462 Send(CreateGesture(two_finger_tap_details
,
470 e2
.GetPointerCount(),
471 GetBoundingBox(e2
)));
475 virtual void OnShowPress(const MotionEvent
& e
) OVERRIDE
{
476 GestureEventDetails
show_press_details(ET_GESTURE_SHOW_PRESS
, 0, 0);
477 Send(CreateGesture(show_press_details
, e
));
480 virtual bool OnSingleTapUp(const MotionEvent
& e
) OVERRIDE
{
481 // This is a hack to address the issue where user hovers
482 // over a link for longer than double_tap_timeout_, then
483 // OnSingleTapConfirmed() is not triggered. But we still
484 // want to trigger the tap event at UP. So we override
485 // OnSingleTapUp() in this case. This assumes singleTapUp
486 // gets always called before singleTapConfirmed.
487 if (!ignore_single_tap_
) {
488 if (e
.GetEventTime() - current_down_time_
>
489 config_
.gesture_detector_config
.double_tap_timeout
) {
490 return OnSingleTapConfirmed(e
);
491 } else if (!IsDoubleTapEnabled() || config_
.disable_click_delay
) {
492 // If double-tap has been disabled, there is no need to wait
493 // for the double-tap timeout.
494 return OnSingleTapConfirmed(e
);
496 // Notify Blink about this tapUp event anyway, when none of the above
497 // conditions applied.
498 Send(CreateTapGesture(ET_GESTURE_TAP_UNCONFIRMED
, e
));
502 if (e
.GetAction() == MotionEvent::ACTION_UP
&&
503 !current_longpress_time_
.is_null() &&
504 !IsScaleGestureDetectionInProgress()) {
505 GestureEventDetails
long_tap_details(ET_GESTURE_LONG_TAP
, 0, 0);
506 Send(CreateGesture(long_tap_details
, e
));
513 // GestureDetector::DoubleTapListener implementation.
514 virtual bool OnSingleTapConfirmed(const MotionEvent
& e
) OVERRIDE
{
515 // Long taps in the edges of the screen have their events delayed by
516 // ContentViewHolder for tab swipe operations. As a consequence of the delay
517 // this method might be called after receiving the up event.
518 // These corner cases should be ignored.
519 if (ignore_single_tap_
)
522 ignore_single_tap_
= true;
524 Send(CreateTapGesture(ET_GESTURE_TAP
, e
));
528 virtual bool OnDoubleTap(const MotionEvent
& e
) OVERRIDE
{
529 return scale_gesture_detector_
.OnDoubleTap(e
);
532 virtual bool OnDoubleTapEvent(const MotionEvent
& e
) OVERRIDE
{
533 switch (e
.GetAction()) {
534 case MotionEvent::ACTION_DOWN
:
535 gesture_detector_
.set_longpress_enabled(false);
538 case MotionEvent::ACTION_UP
:
539 if (!IsPinchInProgress() && !IsScrollInProgress()) {
540 Send(CreateTapGesture(ET_GESTURE_DOUBLE_TAP
, e
));
551 virtual void OnLongPress(const MotionEvent
& e
) OVERRIDE
{
552 DCHECK(!IsDoubleTapInProgress());
553 SetIgnoreSingleTap(true);
554 GestureEventDetails
long_press_details(ET_GESTURE_LONG_PRESS
, 0, 0);
555 Send(CreateGesture(long_press_details
, e
));
558 void SetDoubleTapEnabled(bool enabled
) {
559 DCHECK(!IsDoubleTapInProgress());
560 gesture_detector_
.SetDoubleTapListener(enabled
? this : NULL
);
563 void SetMultiTouchZoomEnabled(bool enabled
) {
564 // Note that returning false from |OnScaleBegin()| or |OnScale()| prevents
565 // the detector from emitting further scale updates for the current touch
566 // sequence. Thus, if multitouch events are enabled in the middle of a
567 // gesture, it will only take effect with the next gesture.
568 ignore_multitouch_zoom_events_
= !enabled
;
571 bool IsDoubleTapInProgress() const {
572 return gesture_detector_
.is_double_tapping() ||
573 (IsScaleGestureDetectionInProgress() && InDoubleTapMode());
576 bool IsScrollInProgress() const { return scroll_event_sent_
; }
578 bool IsPinchInProgress() const { return pinch_event_sent_
; }
581 bool IsScaleGestureDetectionInProgress() const {
582 return scale_gesture_detector_
.IsInProgress();
585 bool InDoubleTapMode() const {
586 return scale_gesture_detector_
.InDoubleTapMode();
589 bool IsDoubleTapEnabled() const {
590 return gesture_detector_
.has_doubletap_listener();
593 void SetIgnoreSingleTap(bool value
) { ignore_single_tap_
= value
; }
595 const GestureProvider::Config config_
;
596 GestureProviderClient
* const client_
;
598 GestureDetector gesture_detector_
;
599 ScaleGestureDetector scale_gesture_detector_
;
600 SnapScrollController snap_scroll_controller_
;
602 base::TimeTicks current_down_time_
;
604 // Keeps track of the current GESTURE_LONG_PRESS event. If a context menu is
605 // opened after a GESTURE_LONG_PRESS, this is used to insert a
606 // GESTURE_TAP_CANCEL for removing any ::active styling.
607 base::TimeTicks current_longpress_time_
;
609 // Completely silence multi-touch (pinch) scaling events. Used in WebView when
610 // zoom support is turned off.
611 bool ignore_multitouch_zoom_events_
;
613 // TODO(klobag): This is to avoid a bug in GestureDetector. With multi-touch,
614 // always_in_tap_region_ is not reset. So when the last finger is up,
615 // |OnSingleTapUp()| will be mistakenly fired.
616 bool ignore_single_tap_
;
618 // Tracks whether {PINCH|SCROLL}_BEGIN events have been forwarded for the
619 // current touch sequence.
620 bool pinch_event_sent_
;
621 bool scroll_event_sent_
;
623 DISALLOW_COPY_AND_ASSIGN(GestureListenerImpl
);
628 GestureProvider::GestureProvider(const Config
& config
,
629 GestureProviderClient
* client
)
630 : double_tap_support_for_page_(true),
631 double_tap_support_for_platform_(true),
632 gesture_begin_end_types_enabled_(config
.gesture_begin_end_types_enabled
) {
634 DCHECK(!config
.min_gesture_bounds_length
||
635 !config
.max_gesture_bounds_length
||
636 config
.min_gesture_bounds_length
<= config
.max_gesture_bounds_length
);
637 TRACE_EVENT0("input", "GestureProvider::InitGestureDetectors");
638 gesture_listener_
.reset(new GestureListenerImpl(config
, client
));
639 UpdateDoubleTapDetectionSupport();
642 GestureProvider::~GestureProvider() {
645 bool GestureProvider::OnTouchEvent(const MotionEvent
& event
) {
646 TRACE_EVENT1("input",
647 "GestureProvider::OnTouchEvent",
649 GetMotionEventActionName(event
.GetAction()));
651 DCHECK_NE(0u, event
.GetPointerCount());
653 if (!CanHandle(event
))
656 OnTouchEventHandlingBegin(event
);
657 gesture_listener_
->OnTouchEvent(event
);
658 OnTouchEventHandlingEnd(event
);
662 void GestureProvider::SetMultiTouchZoomSupportEnabled(bool enabled
) {
663 gesture_listener_
->SetMultiTouchZoomEnabled(enabled
);
666 void GestureProvider::SetDoubleTapSupportForPlatformEnabled(bool enabled
) {
667 if (double_tap_support_for_platform_
== enabled
)
669 double_tap_support_for_platform_
= enabled
;
670 UpdateDoubleTapDetectionSupport();
673 void GestureProvider::SetDoubleTapSupportForPageEnabled(bool enabled
) {
674 if (double_tap_support_for_page_
== enabled
)
676 double_tap_support_for_page_
= enabled
;
677 UpdateDoubleTapDetectionSupport();
680 bool GestureProvider::IsScrollInProgress() const {
681 return gesture_listener_
->IsScrollInProgress();
684 bool GestureProvider::IsPinchInProgress() const {
685 return gesture_listener_
->IsPinchInProgress();
688 bool GestureProvider::IsDoubleTapInProgress() const {
689 return gesture_listener_
->IsDoubleTapInProgress();
692 bool GestureProvider::CanHandle(const MotionEvent
& event
) const {
693 // Aura requires one cancel event per touch point, whereas Android requires
694 // one cancel event per touch sequence. Thus we need to allow extra cancel
696 return current_down_event_
|| event
.GetAction() == MotionEvent::ACTION_DOWN
||
697 event
.GetAction() == MotionEvent::ACTION_CANCEL
;
700 void GestureProvider::OnTouchEventHandlingBegin(const MotionEvent
& event
) {
701 switch (event
.GetAction()) {
702 case MotionEvent::ACTION_DOWN
:
703 current_down_event_
= event
.Clone();
704 if (gesture_begin_end_types_enabled_
)
705 gesture_listener_
->Send(CreateGesture(ET_GESTURE_BEGIN
, event
));
707 case MotionEvent::ACTION_POINTER_DOWN
:
708 if (gesture_begin_end_types_enabled_
) {
709 const int action_index
= event
.GetActionIndex();
710 gesture_listener_
->Send(CreateGesture(ET_GESTURE_BEGIN
,
713 event
.GetEventTime(),
714 event
.GetX(action_index
),
715 event
.GetY(action_index
),
716 event
.GetRawX(action_index
),
717 event
.GetRawY(action_index
),
718 event
.GetPointerCount(),
719 GetBoundingBox(event
)));
722 case MotionEvent::ACTION_POINTER_UP
:
723 case MotionEvent::ACTION_UP
:
724 case MotionEvent::ACTION_CANCEL
:
725 case MotionEvent::ACTION_MOVE
:
730 void GestureProvider::OnTouchEventHandlingEnd(const MotionEvent
& event
) {
731 switch (event
.GetAction()) {
732 case MotionEvent::ACTION_UP
:
733 case MotionEvent::ACTION_CANCEL
: {
734 if (gesture_begin_end_types_enabled_
)
735 gesture_listener_
->Send(CreateGesture(ET_GESTURE_END
, event
));
737 current_down_event_
.reset();
739 UpdateDoubleTapDetectionSupport();
742 case MotionEvent::ACTION_POINTER_UP
:
743 if (gesture_begin_end_types_enabled_
)
744 gesture_listener_
->Send(CreateGesture(ET_GESTURE_END
, event
));
746 case MotionEvent::ACTION_DOWN
:
747 case MotionEvent::ACTION_POINTER_DOWN
:
748 case MotionEvent::ACTION_MOVE
:
753 void GestureProvider::UpdateDoubleTapDetectionSupport() {
754 // The GestureDetector requires that any provided DoubleTapListener remain
755 // attached to it for the duration of a touch sequence. Defer any potential
756 // null'ing of the listener until the sequence has ended.
757 if (current_down_event_
)
760 const bool double_tap_enabled
=
761 double_tap_support_for_page_
&& double_tap_support_for_platform_
;
762 gesture_listener_
->SetDoubleTapEnabled(double_tap_enabled
);