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