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_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 double_tap_support_for_platform_enabled(true),
69 gesture_begin_end_types_enabled(false),
70 min_gesture_bounds_length(0),
71 max_gesture_bounds_length(0) {
74 GestureProvider::Config::~Config() {
77 // GestureProvider::GestureListener
79 class GestureProvider::GestureListenerImpl
: public ScaleGestureListener
,
80 public GestureListener
,
81 public DoubleTapListener
{
83 GestureListenerImpl(const GestureProvider::Config
& config
,
84 GestureProviderClient
* client
)
87 gesture_detector_(config
.gesture_detector_config
, this, this),
88 scale_gesture_detector_(config
.scale_gesture_detector_config
, this),
89 snap_scroll_controller_(config
.gesture_detector_config
.touch_slop
,
90 config
.display
.size()),
91 ignore_multitouch_zoom_events_(false),
92 ignore_single_tap_(false),
93 pinch_event_sent_(false),
94 scroll_event_sent_(false),
95 max_diameter_before_show_press_(0),
96 show_press_event_sent_(false) {}
98 void OnTouchEvent(const MotionEvent
& event
) {
99 const bool in_scale_gesture
= IsScaleGestureDetectionInProgress();
100 snap_scroll_controller_
.SetSnapScrollMode(event
, in_scale_gesture
);
101 if (in_scale_gesture
)
102 SetIgnoreSingleTap(true);
104 const MotionEvent::Action action
= event
.GetAction();
105 if (action
== MotionEvent::ACTION_DOWN
) {
106 current_down_time_
= event
.GetEventTime();
107 current_longpress_time_
= base::TimeTicks();
108 ignore_single_tap_
= false;
109 scroll_event_sent_
= false;
110 pinch_event_sent_
= false;
111 show_press_event_sent_
= false;
112 gesture_detector_
.set_longpress_enabled(true);
113 tap_down_point_
= gfx::PointF(event
.GetX(), event
.GetY());
114 max_diameter_before_show_press_
= event
.GetTouchMajor();
117 gesture_detector_
.OnTouchEvent(event
);
118 scale_gesture_detector_
.OnTouchEvent(event
);
120 if (action
== MotionEvent::ACTION_UP
||
121 action
== MotionEvent::ACTION_CANCEL
) {
122 // Note: This call will have no effect if a fling was just generated, as
123 // |Fling()| will have already signalled an end to touch-scrolling.
124 if (scroll_event_sent_
)
125 Send(CreateGesture(ET_GESTURE_SCROLL_END
, event
));
126 current_down_time_
= base::TimeTicks();
127 } else if (action
== MotionEvent::ACTION_MOVE
) {
128 if (!show_press_event_sent_
&& !scroll_event_sent_
) {
129 max_diameter_before_show_press_
=
130 std::max(max_diameter_before_show_press_
, event
.GetTouchMajor());
135 void Send(GestureEventData gesture
) {
136 DCHECK(!gesture
.time
.is_null());
137 // The only valid events that should be sent without an active touch
138 // sequence are SHOW_PRESS and TAP, potentially triggered by the double-tap
140 DCHECK(!current_down_time_
.is_null() || gesture
.type() == ET_GESTURE_TAP
||
141 gesture
.type() == ET_GESTURE_SHOW_PRESS
||
142 gesture
.type() == ET_GESTURE_BEGIN
||
143 gesture
.type() == ET_GESTURE_END
);
145 if (gesture
.primary_tool_type
== MotionEvent::TOOL_TYPE_UNKNOWN
||
146 gesture
.primary_tool_type
== MotionEvent::TOOL_TYPE_FINGER
) {
147 gesture
.details
.set_bounding_box(
148 ClampBoundingBox(gesture
.details
.bounding_box_f(),
149 config_
.min_gesture_bounds_length
,
150 config_
.max_gesture_bounds_length
));
153 switch (gesture
.type()) {
154 case ET_GESTURE_LONG_PRESS
:
155 DCHECK(!IsScaleGestureDetectionInProgress());
156 current_longpress_time_
= gesture
.time
;
158 case ET_GESTURE_LONG_TAP
:
159 current_longpress_time_
= base::TimeTicks();
161 case ET_GESTURE_SCROLL_BEGIN
:
162 DCHECK(!scroll_event_sent_
);
163 scroll_event_sent_
= true;
165 case ET_GESTURE_SCROLL_END
:
166 DCHECK(scroll_event_sent_
);
167 if (pinch_event_sent_
)
168 Send(GestureEventData(ET_GESTURE_PINCH_END
, gesture
));
169 scroll_event_sent_
= false;
171 case ET_SCROLL_FLING_START
:
172 DCHECK(scroll_event_sent_
);
173 scroll_event_sent_
= false;
175 case ET_GESTURE_PINCH_BEGIN
:
176 DCHECK(!pinch_event_sent_
);
177 if (!scroll_event_sent_
)
178 Send(GestureEventData(ET_GESTURE_SCROLL_BEGIN
, gesture
));
179 pinch_event_sent_
= true;
181 case ET_GESTURE_PINCH_END
:
182 DCHECK(pinch_event_sent_
);
183 pinch_event_sent_
= false;
185 case ET_GESTURE_SHOW_PRESS
:
186 // It's possible that a double-tap drag zoom (from ScaleGestureDetector)
187 // will start before the press gesture fires (from GestureDetector), in
188 // which case the press should simply be dropped.
189 if (pinch_event_sent_
|| scroll_event_sent_
)
195 client_
->OnGestureEvent(gesture
);
196 GestureTouchUMAHistogram::RecordGestureEvent(gesture
);
199 // ScaleGestureListener implementation.
200 bool OnScaleBegin(const ScaleGestureDetector
& detector
,
201 const MotionEvent
& e
) override
{
202 if (ignore_multitouch_zoom_events_
&& !detector
.InDoubleTapMode())
207 void OnScaleEnd(const ScaleGestureDetector
& detector
,
208 const MotionEvent
& e
) override
{
209 if (!pinch_event_sent_
)
211 Send(CreateGesture(ET_GESTURE_PINCH_END
, e
));
214 bool OnScale(const ScaleGestureDetector
& detector
,
215 const MotionEvent
& e
) override
{
216 if (ignore_multitouch_zoom_events_
&& !detector
.InDoubleTapMode())
218 if (!pinch_event_sent_
) {
219 Send(CreateGesture(ET_GESTURE_PINCH_BEGIN
,
222 detector
.GetEventTime(),
223 detector
.GetFocusX(),
224 detector
.GetFocusY(),
225 detector
.GetFocusX() + e
.GetRawOffsetX(),
226 detector
.GetFocusY() + e
.GetRawOffsetY(),
228 GetBoundingBox(e
, ET_GESTURE_PINCH_BEGIN
),
232 if (std::abs(detector
.GetCurrentSpan() - detector
.GetPreviousSpan()) <
233 config_
.scale_gesture_detector_config
.min_pinch_update_span_delta
) {
237 float scale
= detector
.GetScaleFactor();
241 if (detector
.InDoubleTapMode()) {
242 // Relative changes in the double-tap scale factor computed by |detector|
243 // diminish as the touch moves away from the original double-tap focus.
244 // For historical reasons, Chrome has instead adopted a scale factor
245 // computation that is invariant to the focal distance, where
246 // the scale delta remains constant if the touch velocity is constant.
248 (detector
.GetCurrentSpanY() - detector
.GetPreviousSpanY()) * 0.5f
;
249 scale
= std::pow(scale
> 1 ? 1.0f
+ kDoubleTapDragZoomSpeed
250 : 1.0f
- kDoubleTapDragZoomSpeed
,
253 GestureEventDetails
pinch_details(ET_GESTURE_PINCH_UPDATE
);
254 pinch_details
.set_scale(scale
);
255 Send(CreateGesture(pinch_details
,
258 detector
.GetEventTime(),
259 detector
.GetFocusX(),
260 detector
.GetFocusY(),
261 detector
.GetFocusX() + e
.GetRawOffsetX(),
262 detector
.GetFocusY() + e
.GetRawOffsetY(),
264 GetBoundingBox(e
, pinch_details
.type()),
269 // GestureListener implementation.
270 bool OnDown(const MotionEvent
& e
) override
{
271 GestureEventDetails
tap_details(ET_GESTURE_TAP_DOWN
);
272 Send(CreateGesture(tap_details
, e
));
274 // Return true to indicate that we want to handle touch.
278 bool OnScroll(const MotionEvent
& e1
,
279 const MotionEvent
& e2
,
280 float raw_distance_x
,
281 float raw_distance_y
) override
{
282 float distance_x
= raw_distance_x
;
283 float distance_y
= raw_distance_y
;
284 if (!scroll_event_sent_
&& e2
.GetPointerCount() == 1) {
285 // Remove the touch slop region from the first scroll event to
286 // avoid a jump. Touch slop isn't used for multi-finger
287 // gestures, so in those cases we don't subtract the slop.
289 std::sqrt(distance_x
* distance_x
+ distance_y
* distance_y
);
290 float epsilon
= 1e-3f
;
291 if (distance
> epsilon
) {
294 distance
- config_
.gesture_detector_config
.touch_slop
) /
301 snap_scroll_controller_
.UpdateSnapScrollMode(distance_x
, distance_y
);
302 if (snap_scroll_controller_
.IsSnappingScrolls()) {
303 if (snap_scroll_controller_
.IsSnapHorizontal())
309 if (!distance_x
&& !distance_y
)
312 if (!scroll_event_sent_
) {
313 // Note that scroll start hints are in distance traveled, where
314 // scroll deltas are in the opposite direction.
315 GestureEventDetails
scroll_details(
316 ET_GESTURE_SCROLL_BEGIN
, -raw_distance_x
, -raw_distance_y
);
318 // Use the co-ordinates from the touch down, as these co-ordinates are
319 // used to determine which layer the scroll should affect.
320 Send(CreateGesture(scroll_details
, e2
.GetPointerId(), e2
.GetToolType(),
321 e2
.GetEventTime(), e1
.GetX(), e1
.GetY(), e1
.GetRawX(),
322 e1
.GetRawY(), e2
.GetPointerCount(),
323 GetBoundingBox(e2
, scroll_details
.type()),
325 DCHECK(scroll_event_sent_
);
328 GestureEventDetails
scroll_details(ET_GESTURE_SCROLL_UPDATE
, -distance_x
,
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
, e2
.GetPointerId(), e2
.GetToolType(),
335 e2
.GetEventTime(), center
.x(), center
.y(),
336 raw_center
.x(), raw_center
.y(), e2
.GetPointerCount(),
337 bounding_box
, e2
.GetFlags()));
342 bool OnFling(const MotionEvent
& e1
,
343 const MotionEvent
& e2
,
345 float velocity_y
) override
{
346 if (snap_scroll_controller_
.IsSnappingScrolls()) {
347 if (snap_scroll_controller_
.IsSnapHorizontal()) {
354 if (!velocity_x
&& !velocity_y
)
357 if (!scroll_event_sent_
) {
358 // The native side needs a ET_GESTURE_SCROLL_BEGIN before
359 // ET_SCROLL_FLING_START to send the fling to the correct target.
360 // The distance traveled in one second is a reasonable scroll start hint.
361 GestureEventDetails
scroll_details(
362 ET_GESTURE_SCROLL_BEGIN
, velocity_x
, velocity_y
);
363 Send(CreateGesture(scroll_details
, e2
));
366 GestureEventDetails
fling_details(
367 ET_SCROLL_FLING_START
, velocity_x
, velocity_y
);
368 Send(CreateGesture(fling_details
, e2
));
372 bool OnSwipe(const MotionEvent
& e1
,
373 const MotionEvent
& e2
,
375 float velocity_y
) override
{
376 GestureEventDetails
swipe_details(ET_GESTURE_SWIPE
, velocity_x
, velocity_y
);
377 Send(CreateGesture(swipe_details
, e2
));
381 bool OnTwoFingerTap(const MotionEvent
& e1
, const MotionEvent
& e2
) override
{
382 // The location of the two finger tap event should be the location of the
384 GestureEventDetails
two_finger_tap_details(
385 ET_GESTURE_TWO_FINGER_TAP
, e1
.GetTouchMajor(), e1
.GetTouchMajor());
386 Send(CreateGesture(two_finger_tap_details
,
394 e2
.GetPointerCount(),
395 GetBoundingBox(e2
, two_finger_tap_details
.type()),
400 void OnShowPress(const MotionEvent
& e
) override
{
401 GestureEventDetails
show_press_details(ET_GESTURE_SHOW_PRESS
);
402 show_press_event_sent_
= true;
403 Send(CreateGesture(show_press_details
, e
));
406 bool OnSingleTapUp(const MotionEvent
& e
) override
{
407 // This is a hack to address the issue where user hovers
408 // over a link for longer than double_tap_timeout_, then
409 // OnSingleTapConfirmed() is not triggered. But we still
410 // want to trigger the tap event at UP. So we override
411 // OnSingleTapUp() in this case. This assumes singleTapUp
412 // gets always called before singleTapConfirmed.
413 if (!ignore_single_tap_
) {
414 if (e
.GetEventTime() - current_down_time_
>
415 config_
.gesture_detector_config
.double_tap_timeout
) {
416 return OnSingleTapConfirmed(e
);
417 } else if (!IsDoubleTapEnabled() || config_
.disable_click_delay
) {
418 // If double-tap has been disabled, there is no need to wait
419 // for the double-tap timeout.
420 return OnSingleTapConfirmed(e
);
422 // Notify Blink about this tapUp event anyway, when none of the above
423 // conditions applied.
424 Send(CreateTapGesture(ET_GESTURE_TAP_UNCONFIRMED
, e
));
428 if (e
.GetAction() == MotionEvent::ACTION_UP
&&
429 !current_longpress_time_
.is_null() &&
430 !IsScaleGestureDetectionInProgress()) {
431 GestureEventDetails
long_tap_details(ET_GESTURE_LONG_TAP
);
432 Send(CreateGesture(long_tap_details
, e
));
439 // DoubleTapListener implementation.
440 bool OnSingleTapConfirmed(const MotionEvent
& e
) override
{
441 // Long taps in the edges of the screen have their events delayed by
442 // ContentViewHolder for tab swipe operations. As a consequence of the delay
443 // this method might be called after receiving the up event.
444 // These corner cases should be ignored.
445 if (ignore_single_tap_
)
448 ignore_single_tap_
= true;
450 Send(CreateTapGesture(ET_GESTURE_TAP
, e
));
454 bool OnDoubleTap(const MotionEvent
& e
) override
{
455 return scale_gesture_detector_
.OnDoubleTap(e
);
458 bool OnDoubleTapEvent(const MotionEvent
& e
) override
{
459 switch (e
.GetAction()) {
460 case MotionEvent::ACTION_DOWN
:
461 gesture_detector_
.set_longpress_enabled(false);
464 case MotionEvent::ACTION_UP
:
465 if (!IsPinchInProgress() && !IsScrollInProgress()) {
466 Send(CreateTapGesture(ET_GESTURE_DOUBLE_TAP
, e
));
477 void OnLongPress(const MotionEvent
& e
) override
{
478 DCHECK(!IsDoubleTapInProgress());
479 SetIgnoreSingleTap(true);
480 GestureEventDetails
long_press_details(ET_GESTURE_LONG_PRESS
);
481 Send(CreateGesture(long_press_details
, e
));
484 GestureEventData
CreateGesture(const GestureEventDetails
& details
,
486 MotionEvent::ToolType primary_tool_type
,
487 base::TimeTicks time
,
492 size_t touch_point_count
,
493 const gfx::RectF
& bounding_box
,
495 return GestureEventData(details
,
508 GestureEventData
CreateGesture(EventType type
,
510 MotionEvent::ToolType primary_tool_type
,
511 base::TimeTicks time
,
516 size_t touch_point_count
,
517 const gfx::RectF
& bounding_box
,
519 return GestureEventData(GestureEventDetails(type
),
532 GestureEventData
CreateGesture(const GestureEventDetails
& details
,
533 const MotionEvent
& event
) {
534 return GestureEventData(details
,
535 event
.GetPointerId(),
537 event
.GetEventTime(),
542 event
.GetPointerCount(),
543 GetBoundingBox(event
, details
.type()),
547 GestureEventData
CreateGesture(EventType type
, const MotionEvent
& event
) {
548 return CreateGesture(GestureEventDetails(type
), event
);
551 GestureEventData
CreateTapGesture(EventType type
, const MotionEvent
& event
) {
552 // Set the tap count to 1 even for ET_GESTURE_DOUBLE_TAP, in order to be
553 // consistent with double tap behavior on a mobile viewport. See
554 // crbug.com/234986 for context.
555 GestureEventDetails
details(type
);
556 details
.set_tap_count(1);
557 return CreateGesture(details
, event
);
560 gfx::RectF
GetBoundingBox(const MotionEvent
& event
, EventType type
) {
561 // Can't use gfx::RectF::Union, as it ignores touches with a radius of 0.
562 float left
= std::numeric_limits
<float>::max();
563 float top
= std::numeric_limits
<float>::max();
564 float right
= -std::numeric_limits
<float>::max();
565 float bottom
= -std::numeric_limits
<float>::max();
566 for (size_t i
= 0; i
< event
.GetPointerCount(); ++i
) {
567 float x
, y
, diameter
;
568 // Only for the show press and tap events, the bounding box is calculated
569 // based on the touch start point and the maximum diameter before the
570 // show press event is sent.
571 if (type
== ET_GESTURE_SHOW_PRESS
|| type
== ET_GESTURE_TAP
||
572 type
== ET_GESTURE_TAP_UNCONFIRMED
) {
574 diameter
= max_diameter_before_show_press_
;
575 x
= tap_down_point_
.x();
576 y
= tap_down_point_
.y();
578 diameter
= event
.GetTouchMajor(i
);
582 x
= x
- diameter
/ 2;
583 y
= y
- diameter
/ 2;
584 left
= std::min(left
, x
);
585 right
= std::max(right
, x
+ diameter
);
586 top
= std::min(top
, y
);
587 bottom
= std::max(bottom
, y
+ diameter
);
589 return gfx::RectF(left
, top
, right
- left
, bottom
- top
);
592 void SetDoubleTapEnabled(bool enabled
) {
593 DCHECK(!IsDoubleTapInProgress());
594 gesture_detector_
.SetDoubleTapListener(enabled
? this : NULL
);
597 void SetMultiTouchZoomEnabled(bool enabled
) {
598 // Note that returning false from |OnScaleBegin()| or |OnScale()| prevents
599 // the detector from emitting further scale updates for the current touch
600 // sequence. Thus, if multitouch events are enabled in the middle of a
601 // gesture, it will only take effect with the next gesture.
602 ignore_multitouch_zoom_events_
= !enabled
;
605 bool IsDoubleTapInProgress() const {
606 return gesture_detector_
.is_double_tapping() ||
607 (IsScaleGestureDetectionInProgress() && InDoubleTapMode());
610 bool IsScrollInProgress() const { return scroll_event_sent_
; }
612 bool IsPinchInProgress() const { return pinch_event_sent_
; }
615 bool IsScaleGestureDetectionInProgress() const {
616 return scale_gesture_detector_
.IsInProgress();
619 bool InDoubleTapMode() const {
620 return scale_gesture_detector_
.InDoubleTapMode();
623 bool IsDoubleTapEnabled() const {
624 return gesture_detector_
.has_doubletap_listener();
627 void SetIgnoreSingleTap(bool value
) { ignore_single_tap_
= value
; }
629 const GestureProvider::Config config_
;
630 GestureProviderClient
* const client_
;
632 GestureDetector gesture_detector_
;
633 ScaleGestureDetector scale_gesture_detector_
;
634 SnapScrollController snap_scroll_controller_
;
636 base::TimeTicks current_down_time_
;
638 // Keeps track of the current GESTURE_LONG_PRESS event. If a context menu is
639 // opened after a GESTURE_LONG_PRESS, this is used to insert a
640 // GESTURE_TAP_CANCEL for removing any ::active styling.
641 base::TimeTicks current_longpress_time_
;
643 // Completely silence multi-touch (pinch) scaling events. Used in WebView when
644 // zoom support is turned off.
645 bool ignore_multitouch_zoom_events_
;
647 // TODO(klobag): This is to avoid a bug in GestureDetector. With multi-touch,
648 // always_in_tap_region_ is not reset. So when the last finger is up,
649 // |OnSingleTapUp()| will be mistakenly fired.
650 bool ignore_single_tap_
;
652 // Tracks whether {PINCH|SCROLL}_BEGIN events have been forwarded for the
653 // current touch sequence.
654 bool pinch_event_sent_
;
655 bool scroll_event_sent_
;
657 // Only track the maximum diameter before the show press event has been
658 // sent and a tap must still be possible for this touch sequence.
659 float max_diameter_before_show_press_
;
661 gfx::PointF tap_down_point_
;
663 // Tracks whether an ET_GESTURE_SHOW_PRESS event has been sent for this touch
665 bool show_press_event_sent_
;
667 DISALLOW_COPY_AND_ASSIGN(GestureListenerImpl
);
672 GestureProvider::GestureProvider(const Config
& config
,
673 GestureProviderClient
* client
)
674 : double_tap_support_for_page_(true),
675 double_tap_support_for_platform_(
676 config
.double_tap_support_for_platform_enabled
),
677 gesture_begin_end_types_enabled_(config
.gesture_begin_end_types_enabled
) {
679 DCHECK(!config
.min_gesture_bounds_length
||
680 !config
.max_gesture_bounds_length
||
681 config
.min_gesture_bounds_length
<= config
.max_gesture_bounds_length
);
682 TRACE_EVENT0("input", "GestureProvider::InitGestureDetectors");
683 gesture_listener_
.reset(new GestureListenerImpl(config
, client
));
684 UpdateDoubleTapDetectionSupport();
687 GestureProvider::~GestureProvider() {
690 bool GestureProvider::OnTouchEvent(const MotionEvent
& event
) {
691 TRACE_EVENT1("input",
692 "GestureProvider::OnTouchEvent",
694 GetMotionEventActionName(event
.GetAction()));
696 DCHECK_NE(0u, event
.GetPointerCount());
698 if (!CanHandle(event
))
701 OnTouchEventHandlingBegin(event
);
702 gesture_listener_
->OnTouchEvent(event
);
703 OnTouchEventHandlingEnd(event
);
704 uma_histogram_
.RecordTouchEvent(event
);
708 void GestureProvider::ResetDetection() {
709 MotionEventGeneric
generic_cancel_event(MotionEvent::ACTION_CANCEL
,
710 base::TimeTicks::Now(),
711 PointerProperties());
712 OnTouchEvent(generic_cancel_event
);
715 void GestureProvider::SetMultiTouchZoomSupportEnabled(bool enabled
) {
716 gesture_listener_
->SetMultiTouchZoomEnabled(enabled
);
719 void GestureProvider::SetDoubleTapSupportForPlatformEnabled(bool enabled
) {
720 if (double_tap_support_for_platform_
== enabled
)
722 double_tap_support_for_platform_
= enabled
;
723 UpdateDoubleTapDetectionSupport();
726 void GestureProvider::SetDoubleTapSupportForPageEnabled(bool enabled
) {
727 if (double_tap_support_for_page_
== enabled
)
729 double_tap_support_for_page_
= enabled
;
730 UpdateDoubleTapDetectionSupport();
733 bool GestureProvider::IsScrollInProgress() const {
734 return gesture_listener_
->IsScrollInProgress();
737 bool GestureProvider::IsPinchInProgress() const {
738 return gesture_listener_
->IsPinchInProgress();
741 bool GestureProvider::IsDoubleTapInProgress() const {
742 return gesture_listener_
->IsDoubleTapInProgress();
745 bool GestureProvider::CanHandle(const MotionEvent
& event
) const {
746 // Aura requires one cancel event per touch point, whereas Android requires
747 // one cancel event per touch sequence. Thus we need to allow extra cancel
749 return current_down_event_
|| event
.GetAction() == MotionEvent::ACTION_DOWN
||
750 event
.GetAction() == MotionEvent::ACTION_CANCEL
;
753 void GestureProvider::OnTouchEventHandlingBegin(const MotionEvent
& event
) {
754 switch (event
.GetAction()) {
755 case MotionEvent::ACTION_DOWN
:
756 current_down_event_
= event
.Clone();
757 if (gesture_begin_end_types_enabled_
)
758 gesture_listener_
->Send(
759 gesture_listener_
->CreateGesture(ET_GESTURE_BEGIN
, event
));
761 case MotionEvent::ACTION_POINTER_DOWN
:
762 if (gesture_begin_end_types_enabled_
) {
763 const int action_index
= event
.GetActionIndex();
764 gesture_listener_
->Send(gesture_listener_
->CreateGesture(
766 event
.GetPointerId(),
768 event
.GetEventTime(),
769 event
.GetX(action_index
),
770 event
.GetY(action_index
),
771 event
.GetRawX(action_index
),
772 event
.GetRawY(action_index
),
773 event
.GetPointerCount(),
774 gesture_listener_
->GetBoundingBox(event
, ET_GESTURE_BEGIN
),
778 case MotionEvent::ACTION_POINTER_UP
:
779 case MotionEvent::ACTION_UP
:
780 case MotionEvent::ACTION_CANCEL
:
781 case MotionEvent::ACTION_MOVE
:
786 void GestureProvider::OnTouchEventHandlingEnd(const MotionEvent
& event
) {
787 switch (event
.GetAction()) {
788 case MotionEvent::ACTION_UP
:
789 case MotionEvent::ACTION_CANCEL
: {
790 if (gesture_begin_end_types_enabled_
)
791 gesture_listener_
->Send(
792 gesture_listener_
->CreateGesture(ET_GESTURE_END
, event
));
794 current_down_event_
.reset();
796 UpdateDoubleTapDetectionSupport();
799 case MotionEvent::ACTION_POINTER_UP
:
800 if (gesture_begin_end_types_enabled_
)
801 gesture_listener_
->Send(
802 gesture_listener_
->CreateGesture(ET_GESTURE_END
, event
));
804 case MotionEvent::ACTION_DOWN
:
805 case MotionEvent::ACTION_POINTER_DOWN
:
806 case MotionEvent::ACTION_MOVE
:
811 void GestureProvider::UpdateDoubleTapDetectionSupport() {
812 // The GestureDetector requires that any provided DoubleTapListener remain
813 // attached to it for the duration of a touch sequence. Defer any potential
814 // null'ing of the listener until the sequence has ended.
815 if (current_down_event_
)
818 const bool double_tap_enabled
=
819 double_tap_support_for_page_
&& double_tap_support_for_platform_
;
820 gesture_listener_
->SetDoubleTapEnabled(double_tap_enabled
);