Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / ui / events / gesture_detection / gesture_provider.cc
blobc76460e53c63da200bda0dcf3cec80946e79fd0a
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"
7 #include <cmath>
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"
15 namespace ui {
16 namespace {
18 // Double-tap drag zoom sensitivity (speed).
19 const float kDoubleTapDragZoomSpeed = 0.005f;
21 const char* GetMotionEventActionName(MotionEvent::Action action) {
22 switch (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:
28 return "ACTION_DOWN";
29 case MotionEvent::ACTION_UP:
30 return "ACTION_UP";
31 case MotionEvent::ACTION_CANCEL:
32 return "ACTION_CANCEL";
33 case MotionEvent::ACTION_MOVE:
34 return "ACTION_MOVE";
36 return "";
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,
58 int motion_event_id,
59 MotionEvent::ToolType primary_tool_type,
60 base::TimeTicks time,
61 float x,
62 float y,
63 float raw_x,
64 float raw_y,
65 size_t touch_point_count,
66 const gfx::RectF& bounding_box) {
67 return GestureEventData(details,
68 motion_event_id,
69 primary_tool_type,
70 time,
73 raw_x,
74 raw_y,
75 touch_point_count,
76 bounding_box);
79 GestureEventData CreateGesture(EventType type,
80 int motion_event_id,
81 MotionEvent::ToolType primary_tool_type,
82 base::TimeTicks time,
83 float x,
84 float y,
85 float raw_x,
86 float raw_y,
87 size_t touch_point_count,
88 const gfx::RectF& bounding_box) {
89 return GestureEventData(GestureEventDetails(type, 0, 0),
90 motion_event_id,
91 primary_tool_type,
92 time,
95 raw_x,
96 raw_y,
97 touch_point_count,
98 bounding_box);
101 GestureEventData CreateGesture(const GestureEventDetails& details,
102 const MotionEvent& event) {
103 return GestureEventData(details,
104 event.GetId(),
105 event.GetToolType(),
106 event.GetEventTime(),
107 event.GetX(),
108 event.GetY(),
109 event.GetRawX(),
110 event.GetRawY(),
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,
127 float min_length,
128 float max_length) {
129 float width = bounds.width();
130 float height = bounds.height();
131 if (min_length) {
132 width = std::max(min_length, width);
133 height = std::max(min_length, height);
135 if (max_length) {
136 width = std::min(max_length, width);
137 height = std::min(max_length, height);
139 const gfx::PointF center = bounds.CenterPoint();
140 return gfx::RectF(
141 center.x() - width / 2.f, center.y() - height / 2.f, width, height);
144 } // namespace
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 {
165 public:
166 GestureListenerImpl(const GestureProvider::Config& config,
167 GestureProviderClient* client)
168 : config_(config),
169 client_(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
211 // delay timing out.
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;
229 break;
230 case ET_GESTURE_LONG_TAP:
231 current_longpress_time_ = base::TimeTicks();
232 break;
233 case ET_GESTURE_SCROLL_BEGIN:
234 DCHECK(!scroll_event_sent_);
235 scroll_event_sent_ = true;
236 break;
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;
242 break;
243 case ET_SCROLL_FLING_START:
244 DCHECK(scroll_event_sent_);
245 scroll_event_sent_ = false;
246 break;
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;
252 break;
253 case ET_GESTURE_PINCH_END:
254 DCHECK(pinch_event_sent_);
255 pinch_event_sent_ = false;
256 break;
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_)
262 return;
263 default:
264 break;
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())
274 return false;
275 return true;
278 virtual void OnScaleEnd(const ScaleGestureDetector& detector,
279 const MotionEvent& e) OVERRIDE {
280 if (!pinch_event_sent_)
281 return;
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())
288 return false;
289 if (!pinch_event_sent_) {
290 Send(CreateGesture(ET_GESTURE_PINCH_BEGIN,
291 e.GetId(),
292 e.GetToolType(),
293 detector.GetEventTime(),
294 detector.GetFocusX(),
295 detector.GetFocusY(),
296 detector.GetFocusX() + e.GetRawOffsetX(),
297 detector.GetFocusY() + e.GetRawOffsetY(),
298 e.GetPointerCount(),
299 GetBoundingBox(e)));
302 if (std::abs(detector.GetCurrentSpan() - detector.GetPreviousSpan()) <
303 config_.scale_gesture_detector_config.min_pinch_update_span_delta) {
304 return false;
307 float scale = detector.GetScaleFactor();
308 if (scale == 1)
309 return true;
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.
317 float dy =
318 (detector.GetCurrentSpanY() - detector.GetPreviousSpanY()) * 0.5f;
319 scale = std::pow(scale > 1 ? 1.0f + kDoubleTapDragZoomSpeed
320 : 1.0f - kDoubleTapDragZoomSpeed,
321 std::abs(dy));
323 GestureEventDetails pinch_details(ET_GESTURE_PINCH_UPDATE, scale, 0);
324 Send(CreateGesture(pinch_details,
325 e.GetId(),
326 e.GetToolType(),
327 detector.GetEventTime(),
328 detector.GetFocusX(),
329 detector.GetFocusY(),
330 detector.GetFocusX() + e.GetRawOffsetX(),
331 detector.GetFocusY() + e.GetRawOffsetY(),
332 e.GetPointerCount(),
333 GetBoundingBox(e)));
334 return true;
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.
343 return true;
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
354 // jump.
355 double distance =
356 std::sqrt(distance_x * distance_x + distance_y * distance_y);
357 double epsilon = 1e-3;
358 if (distance > epsilon) {
359 double ratio =
360 std::max(0.,
361 distance - config_.gesture_detector_config.touch_slop) /
362 distance;
363 distance_x *= ratio;
364 distance_y *= ratio;
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,
375 e2.GetId(),
376 e2.GetToolType(),
377 e2.GetEventTime(),
378 e1.GetX(),
379 e1.GetY(),
380 e1.GetRawX(),
381 e1.GetRawY(),
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())
390 distance_y = 0;
391 else
392 distance_x = 0;
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,
403 e2.GetId(),
404 e2.GetToolType(),
405 e2.GetEventTime(),
406 center.x(),
407 center.y(),
408 raw_center.x(),
409 raw_center.y(),
410 e2.GetPointerCount(),
411 bounding_box));
414 return true;
417 virtual bool OnFling(const MotionEvent& e1,
418 const MotionEvent& e2,
419 float velocity_x,
420 float velocity_y) OVERRIDE {
421 if (snap_scroll_controller_.IsSnappingScrolls()) {
422 if (snap_scroll_controller_.IsSnapHorizontal()) {
423 velocity_y = 0;
424 } else {
425 velocity_x = 0;
429 if (!velocity_x && !velocity_y)
430 return true;
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));
444 return true;
447 virtual bool OnSwipe(const MotionEvent& e1,
448 const MotionEvent& e2,
449 float velocity_x,
450 float velocity_y) OVERRIDE {
451 GestureEventDetails swipe_details(ET_GESTURE_SWIPE, velocity_x, velocity_y);
452 Send(CreateGesture(swipe_details, e2));
453 return true;
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
459 // primary pointer.
460 GestureEventDetails two_finger_tap_details(
461 ET_GESTURE_TWO_FINGER_TAP, e1.GetTouchMajor(), e1.GetTouchMajor());
462 Send(CreateGesture(two_finger_tap_details,
463 e2.GetId(),
464 e2.GetToolType(),
465 e2.GetEventTime(),
466 e1.GetX(),
467 e1.GetY(),
468 e1.GetRawX(),
469 e1.GetRawY(),
470 e2.GetPointerCount(),
471 GetBoundingBox(e2)));
472 return true;
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);
495 } else {
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));
507 return true;
510 return false;
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_)
520 return true;
522 ignore_single_tap_ = true;
524 Send(CreateTapGesture(ET_GESTURE_TAP, e));
525 return true;
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);
536 break;
538 case MotionEvent::ACTION_UP:
539 if (!IsPinchInProgress() && !IsScrollInProgress()) {
540 Send(CreateTapGesture(ET_GESTURE_DOUBLE_TAP, e));
541 return true;
543 break;
545 default:
546 break;
548 return false;
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_; }
580 private:
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);
626 // GestureProvider
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) {
633 DCHECK(client);
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",
648 "action",
649 GetMotionEventActionName(event.GetAction()));
651 DCHECK_NE(0u, event.GetPointerCount());
653 if (!CanHandle(event))
654 return false;
656 OnTouchEventHandlingBegin(event);
657 gesture_listener_->OnTouchEvent(event);
658 OnTouchEventHandlingEnd(event);
659 return true;
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)
668 return;
669 double_tap_support_for_platform_ = enabled;
670 UpdateDoubleTapDetectionSupport();
673 void GestureProvider::SetDoubleTapSupportForPageEnabled(bool enabled) {
674 if (double_tap_support_for_page_ == enabled)
675 return;
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
695 // events.
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));
706 break;
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,
711 event.GetId(),
712 event.GetToolType(),
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)));
721 break;
722 case MotionEvent::ACTION_POINTER_UP:
723 case MotionEvent::ACTION_UP:
724 case MotionEvent::ACTION_CANCEL:
725 case MotionEvent::ACTION_MOVE:
726 break;
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();
740 break;
742 case MotionEvent::ACTION_POINTER_UP:
743 if (gesture_begin_end_types_enabled_)
744 gesture_listener_->Send(CreateGesture(ET_GESTURE_END, event));
745 break;
746 case MotionEvent::ACTION_DOWN:
747 case MotionEvent::ACTION_POINTER_DOWN:
748 case MotionEvent::ACTION_MOVE:
749 break;
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_)
758 return;
760 const bool double_tap_enabled =
761 double_tap_support_for_page_ && double_tap_support_for_platform_;
762 gesture_listener_->SetDoubleTapEnabled(double_tap_enabled);
765 } // namespace ui