Revert of Roll src/third_party/WebKit e0eac24:489c548 (svn 193311:193320) (patchset...
[chromium-blink-merge.git] / ui / events / gesture_detection / gesture_provider.cc
blob0d3a55f6b5aa229c0161bc0163cbc712f3e40668
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/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"
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 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 {
82 public:
83 GestureListenerImpl(const GestureProvider::Config& config,
84 GestureProviderClient* client)
85 : config_(config),
86 client_(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
139 // delay timing out.
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;
157 break;
158 case ET_GESTURE_LONG_TAP:
159 current_longpress_time_ = base::TimeTicks();
160 break;
161 case ET_GESTURE_SCROLL_BEGIN:
162 DCHECK(!scroll_event_sent_);
163 scroll_event_sent_ = true;
164 break;
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;
170 break;
171 case ET_SCROLL_FLING_START:
172 DCHECK(scroll_event_sent_);
173 scroll_event_sent_ = false;
174 break;
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;
180 break;
181 case ET_GESTURE_PINCH_END:
182 DCHECK(pinch_event_sent_);
183 pinch_event_sent_ = false;
184 break;
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_)
190 return;
191 default:
192 break;
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())
203 return false;
204 return true;
207 void OnScaleEnd(const ScaleGestureDetector& detector,
208 const MotionEvent& e) override {
209 if (!pinch_event_sent_)
210 return;
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())
217 return false;
218 if (!pinch_event_sent_) {
219 Send(CreateGesture(ET_GESTURE_PINCH_BEGIN,
220 e.GetId(),
221 e.GetToolType(),
222 detector.GetEventTime(),
223 detector.GetFocusX(),
224 detector.GetFocusY(),
225 detector.GetFocusX() + e.GetRawOffsetX(),
226 detector.GetFocusY() + e.GetRawOffsetY(),
227 e.GetPointerCount(),
228 GetBoundingBox(e, ET_GESTURE_PINCH_BEGIN),
229 e.GetFlags()));
232 if (std::abs(detector.GetCurrentSpan() - detector.GetPreviousSpan()) <
233 config_.scale_gesture_detector_config.min_pinch_update_span_delta) {
234 return false;
237 float scale = detector.GetScaleFactor();
238 if (scale == 1)
239 return true;
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.
247 float dy =
248 (detector.GetCurrentSpanY() - detector.GetPreviousSpanY()) * 0.5f;
249 scale = std::pow(scale > 1 ? 1.0f + kDoubleTapDragZoomSpeed
250 : 1.0f - kDoubleTapDragZoomSpeed,
251 std::abs(dy));
253 GestureEventDetails pinch_details(ET_GESTURE_PINCH_UPDATE);
254 pinch_details.set_scale(scale);
255 Send(CreateGesture(pinch_details,
256 e.GetId(),
257 e.GetToolType(),
258 detector.GetEventTime(),
259 detector.GetFocusX(),
260 detector.GetFocusY(),
261 detector.GetFocusX() + e.GetRawOffsetX(),
262 detector.GetFocusY() + e.GetRawOffsetY(),
263 e.GetPointerCount(),
264 GetBoundingBox(e, pinch_details.type()),
265 e.GetFlags()));
266 return true;
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.
275 return true;
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_) {
285 // Remove the touch slop region from the first scroll event to avoid a
286 // jump.
287 float distance =
288 std::sqrt(distance_x * distance_x + distance_y * distance_y);
289 float epsilon = 1e-3f;
290 if (distance > epsilon) {
291 float ratio =
292 std::max(0.f,
293 distance - config_.gesture_detector_config.touch_slop) /
294 distance;
295 distance_x *= ratio;
296 distance_y *= ratio;
299 // Note that scroll start hints are in distance traveled, where
300 // scroll deltas are in the opposite direction.
301 GestureEventDetails scroll_details(
302 ET_GESTURE_SCROLL_BEGIN, -raw_distance_x, -raw_distance_y);
304 // Use the co-ordinates from the touch down, as these co-ordinates are
305 // used to determine which layer the scroll should affect.
306 Send(CreateGesture(scroll_details,
307 e2.GetId(),
308 e2.GetToolType(),
309 e2.GetEventTime(),
310 e1.GetX(),
311 e1.GetY(),
312 e1.GetRawX(),
313 e1.GetRawY(),
314 e2.GetPointerCount(),
315 GetBoundingBox(e2, scroll_details.type()),
316 e2.GetFlags()));
317 DCHECK(scroll_event_sent_);
320 snap_scroll_controller_.UpdateSnapScrollMode(distance_x, distance_y);
321 if (snap_scroll_controller_.IsSnappingScrolls()) {
322 if (snap_scroll_controller_.IsSnapHorizontal())
323 distance_y = 0;
324 else
325 distance_x = 0;
328 if (distance_x || distance_y) {
329 GestureEventDetails scroll_details(
330 ET_GESTURE_SCROLL_UPDATE, -distance_x, -distance_y);
331 const gfx::RectF bounding_box = GetBoundingBox(e2, scroll_details.type());
332 const gfx::PointF center = bounding_box.CenterPoint();
333 const gfx::PointF raw_center =
334 center + gfx::Vector2dF(e2.GetRawOffsetX(), e2.GetRawOffsetY());
335 Send(CreateGesture(scroll_details,
336 e2.GetId(),
337 e2.GetToolType(),
338 e2.GetEventTime(),
339 center.x(),
340 center.y(),
341 raw_center.x(),
342 raw_center.y(),
343 e2.GetPointerCount(),
344 bounding_box,
345 e2.GetFlags()));
348 return true;
351 bool OnFling(const MotionEvent& e1,
352 const MotionEvent& e2,
353 float velocity_x,
354 float velocity_y) override {
355 if (snap_scroll_controller_.IsSnappingScrolls()) {
356 if (snap_scroll_controller_.IsSnapHorizontal()) {
357 velocity_y = 0;
358 } else {
359 velocity_x = 0;
363 if (!velocity_x && !velocity_y)
364 return true;
366 if (!scroll_event_sent_) {
367 // The native side needs a ET_GESTURE_SCROLL_BEGIN before
368 // ET_SCROLL_FLING_START to send the fling to the correct target.
369 // The distance traveled in one second is a reasonable scroll start hint.
370 GestureEventDetails scroll_details(
371 ET_GESTURE_SCROLL_BEGIN, velocity_x, velocity_y);
372 Send(CreateGesture(scroll_details, e2));
375 GestureEventDetails fling_details(
376 ET_SCROLL_FLING_START, velocity_x, velocity_y);
377 Send(CreateGesture(fling_details, e2));
378 return true;
381 bool OnSwipe(const MotionEvent& e1,
382 const MotionEvent& e2,
383 float velocity_x,
384 float velocity_y) override {
385 GestureEventDetails swipe_details(ET_GESTURE_SWIPE, velocity_x, velocity_y);
386 Send(CreateGesture(swipe_details, e2));
387 return true;
390 bool OnTwoFingerTap(const MotionEvent& e1, const MotionEvent& e2) override {
391 // The location of the two finger tap event should be the location of the
392 // primary pointer.
393 GestureEventDetails two_finger_tap_details(
394 ET_GESTURE_TWO_FINGER_TAP, e1.GetTouchMajor(), e1.GetTouchMajor());
395 Send(CreateGesture(two_finger_tap_details,
396 e2.GetId(),
397 e2.GetToolType(),
398 e2.GetEventTime(),
399 e1.GetX(),
400 e1.GetY(),
401 e1.GetRawX(),
402 e1.GetRawY(),
403 e2.GetPointerCount(),
404 GetBoundingBox(e2, two_finger_tap_details.type()),
405 e2.GetFlags()));
406 return true;
409 void OnShowPress(const MotionEvent& e) override {
410 GestureEventDetails show_press_details(ET_GESTURE_SHOW_PRESS);
411 show_press_event_sent_ = true;
412 Send(CreateGesture(show_press_details, e));
415 bool OnSingleTapUp(const MotionEvent& e) override {
416 // This is a hack to address the issue where user hovers
417 // over a link for longer than double_tap_timeout_, then
418 // OnSingleTapConfirmed() is not triggered. But we still
419 // want to trigger the tap event at UP. So we override
420 // OnSingleTapUp() in this case. This assumes singleTapUp
421 // gets always called before singleTapConfirmed.
422 if (!ignore_single_tap_) {
423 if (e.GetEventTime() - current_down_time_ >
424 config_.gesture_detector_config.double_tap_timeout) {
425 return OnSingleTapConfirmed(e);
426 } else if (!IsDoubleTapEnabled() || config_.disable_click_delay) {
427 // If double-tap has been disabled, there is no need to wait
428 // for the double-tap timeout.
429 return OnSingleTapConfirmed(e);
430 } else {
431 // Notify Blink about this tapUp event anyway, when none of the above
432 // conditions applied.
433 Send(CreateTapGesture(ET_GESTURE_TAP_UNCONFIRMED, e));
437 if (e.GetAction() == MotionEvent::ACTION_UP &&
438 !current_longpress_time_.is_null() &&
439 !IsScaleGestureDetectionInProgress()) {
440 GestureEventDetails long_tap_details(ET_GESTURE_LONG_TAP);
441 Send(CreateGesture(long_tap_details, e));
442 return true;
445 return false;
448 // DoubleTapListener implementation.
449 bool OnSingleTapConfirmed(const MotionEvent& e) override {
450 // Long taps in the edges of the screen have their events delayed by
451 // ContentViewHolder for tab swipe operations. As a consequence of the delay
452 // this method might be called after receiving the up event.
453 // These corner cases should be ignored.
454 if (ignore_single_tap_)
455 return true;
457 ignore_single_tap_ = true;
459 Send(CreateTapGesture(ET_GESTURE_TAP, e));
460 return true;
463 bool OnDoubleTap(const MotionEvent& e) override {
464 return scale_gesture_detector_.OnDoubleTap(e);
467 bool OnDoubleTapEvent(const MotionEvent& e) override {
468 switch (e.GetAction()) {
469 case MotionEvent::ACTION_DOWN:
470 gesture_detector_.set_longpress_enabled(false);
471 break;
473 case MotionEvent::ACTION_UP:
474 if (!IsPinchInProgress() && !IsScrollInProgress()) {
475 Send(CreateTapGesture(ET_GESTURE_DOUBLE_TAP, e));
476 return true;
478 break;
480 default:
481 break;
483 return false;
486 void OnLongPress(const MotionEvent& e) override {
487 DCHECK(!IsDoubleTapInProgress());
488 SetIgnoreSingleTap(true);
489 GestureEventDetails long_press_details(ET_GESTURE_LONG_PRESS);
490 Send(CreateGesture(long_press_details, e));
493 GestureEventData CreateGesture(const GestureEventDetails& details,
494 int motion_event_id,
495 MotionEvent::ToolType primary_tool_type,
496 base::TimeTicks time,
497 float x,
498 float y,
499 float raw_x,
500 float raw_y,
501 size_t touch_point_count,
502 const gfx::RectF& bounding_box,
503 int flags) {
504 return GestureEventData(details,
505 motion_event_id,
506 primary_tool_type,
507 time,
510 raw_x,
511 raw_y,
512 touch_point_count,
513 bounding_box,
514 flags);
517 GestureEventData CreateGesture(EventType type,
518 int motion_event_id,
519 MotionEvent::ToolType primary_tool_type,
520 base::TimeTicks time,
521 float x,
522 float y,
523 float raw_x,
524 float raw_y,
525 size_t touch_point_count,
526 const gfx::RectF& bounding_box,
527 int flags) {
528 return GestureEventData(GestureEventDetails(type),
529 motion_event_id,
530 primary_tool_type,
531 time,
534 raw_x,
535 raw_y,
536 touch_point_count,
537 bounding_box,
538 flags);
541 GestureEventData CreateGesture(const GestureEventDetails& details,
542 const MotionEvent& event) {
543 return GestureEventData(details,
544 event.GetId(),
545 event.GetToolType(),
546 event.GetEventTime(),
547 event.GetX(),
548 event.GetY(),
549 event.GetRawX(),
550 event.GetRawY(),
551 event.GetPointerCount(),
552 GetBoundingBox(event, details.type()),
553 event.GetFlags());
556 GestureEventData CreateGesture(EventType type, const MotionEvent& event) {
557 return CreateGesture(GestureEventDetails(type), event);
560 GestureEventData CreateTapGesture(EventType type, const MotionEvent& event) {
561 // Set the tap count to 1 even for ET_GESTURE_DOUBLE_TAP, in order to be
562 // consistent with double tap behavior on a mobile viewport. See
563 // crbug.com/234986 for context.
564 GestureEventDetails details(type);
565 details.set_tap_count(1);
566 return CreateGesture(details, event);
569 gfx::RectF GetBoundingBox(const MotionEvent& event, EventType type) {
570 // Can't use gfx::RectF::Union, as it ignores touches with a radius of 0.
571 float left = std::numeric_limits<float>::max();
572 float top = std::numeric_limits<float>::max();
573 float right = -std::numeric_limits<float>::max();
574 float bottom = -std::numeric_limits<float>::max();
575 for (size_t i = 0; i < event.GetPointerCount(); ++i) {
576 float x, y, diameter;
577 // Only for the show press and tap events, the bounding box is calculated
578 // based on the touch start point and the maximum diameter before the
579 // show press event is sent.
580 if (type == ET_GESTURE_SHOW_PRESS || type == ET_GESTURE_TAP ||
581 type == ET_GESTURE_TAP_UNCONFIRMED) {
582 DCHECK_EQ(0U, i);
583 diameter = max_diameter_before_show_press_;
584 x = tap_down_point_.x();
585 y = tap_down_point_.y();
586 } else {
587 diameter = event.GetTouchMajor(i);
588 x = event.GetX(i);
589 y = event.GetY(i);
591 x = x - diameter / 2;
592 y = y - diameter / 2;
593 left = std::min(left, x);
594 right = std::max(right, x + diameter);
595 top = std::min(top, y);
596 bottom = std::max(bottom, y + diameter);
598 return gfx::RectF(left, top, right - left, bottom - top);
601 void SetDoubleTapEnabled(bool enabled) {
602 DCHECK(!IsDoubleTapInProgress());
603 gesture_detector_.SetDoubleTapListener(enabled ? this : NULL);
606 void SetMultiTouchZoomEnabled(bool enabled) {
607 // Note that returning false from |OnScaleBegin()| or |OnScale()| prevents
608 // the detector from emitting further scale updates for the current touch
609 // sequence. Thus, if multitouch events are enabled in the middle of a
610 // gesture, it will only take effect with the next gesture.
611 ignore_multitouch_zoom_events_ = !enabled;
614 bool IsDoubleTapInProgress() const {
615 return gesture_detector_.is_double_tapping() ||
616 (IsScaleGestureDetectionInProgress() && InDoubleTapMode());
619 bool IsScrollInProgress() const { return scroll_event_sent_; }
621 bool IsPinchInProgress() const { return pinch_event_sent_; }
623 private:
624 bool IsScaleGestureDetectionInProgress() const {
625 return scale_gesture_detector_.IsInProgress();
628 bool InDoubleTapMode() const {
629 return scale_gesture_detector_.InDoubleTapMode();
632 bool IsDoubleTapEnabled() const {
633 return gesture_detector_.has_doubletap_listener();
636 void SetIgnoreSingleTap(bool value) { ignore_single_tap_ = value; }
638 const GestureProvider::Config config_;
639 GestureProviderClient* const client_;
641 GestureDetector gesture_detector_;
642 ScaleGestureDetector scale_gesture_detector_;
643 SnapScrollController snap_scroll_controller_;
645 base::TimeTicks current_down_time_;
647 // Keeps track of the current GESTURE_LONG_PRESS event. If a context menu is
648 // opened after a GESTURE_LONG_PRESS, this is used to insert a
649 // GESTURE_TAP_CANCEL for removing any ::active styling.
650 base::TimeTicks current_longpress_time_;
652 // Completely silence multi-touch (pinch) scaling events. Used in WebView when
653 // zoom support is turned off.
654 bool ignore_multitouch_zoom_events_;
656 // TODO(klobag): This is to avoid a bug in GestureDetector. With multi-touch,
657 // always_in_tap_region_ is not reset. So when the last finger is up,
658 // |OnSingleTapUp()| will be mistakenly fired.
659 bool ignore_single_tap_;
661 // Tracks whether {PINCH|SCROLL}_BEGIN events have been forwarded for the
662 // current touch sequence.
663 bool pinch_event_sent_;
664 bool scroll_event_sent_;
666 // Only track the maximum diameter before the show press event has been
667 // sent and a tap must still be possible for this touch sequence.
668 float max_diameter_before_show_press_;
670 gfx::PointF tap_down_point_;
672 // Tracks whether an ET_GESTURE_SHOW_PRESS event has been sent for this touch
673 // sequence.
674 bool show_press_event_sent_;
676 DISALLOW_COPY_AND_ASSIGN(GestureListenerImpl);
679 // GestureProvider
681 GestureProvider::GestureProvider(const Config& config,
682 GestureProviderClient* client)
683 : double_tap_support_for_page_(true),
684 double_tap_support_for_platform_(
685 config.double_tap_support_for_platform_enabled),
686 gesture_begin_end_types_enabled_(config.gesture_begin_end_types_enabled) {
687 DCHECK(client);
688 DCHECK(!config.min_gesture_bounds_length ||
689 !config.max_gesture_bounds_length ||
690 config.min_gesture_bounds_length <= config.max_gesture_bounds_length);
691 TRACE_EVENT0("input", "GestureProvider::InitGestureDetectors");
692 gesture_listener_.reset(new GestureListenerImpl(config, client));
693 UpdateDoubleTapDetectionSupport();
696 GestureProvider::~GestureProvider() {
699 bool GestureProvider::OnTouchEvent(const MotionEvent& event) {
700 TRACE_EVENT1("input",
701 "GestureProvider::OnTouchEvent",
702 "action",
703 GetMotionEventActionName(event.GetAction()));
705 DCHECK_NE(0u, event.GetPointerCount());
707 if (!CanHandle(event))
708 return false;
710 OnTouchEventHandlingBegin(event);
711 gesture_listener_->OnTouchEvent(event);
712 OnTouchEventHandlingEnd(event);
713 uma_histogram_.RecordTouchEvent(event);
714 return true;
717 void GestureProvider::ResetDetection() {
718 MotionEventGeneric generic_cancel_event(MotionEvent::ACTION_CANCEL,
719 base::TimeTicks::Now(),
720 PointerProperties());
721 OnTouchEvent(generic_cancel_event);
724 void GestureProvider::SetMultiTouchZoomSupportEnabled(bool enabled) {
725 gesture_listener_->SetMultiTouchZoomEnabled(enabled);
728 void GestureProvider::SetDoubleTapSupportForPlatformEnabled(bool enabled) {
729 if (double_tap_support_for_platform_ == enabled)
730 return;
731 double_tap_support_for_platform_ = enabled;
732 UpdateDoubleTapDetectionSupport();
735 void GestureProvider::SetDoubleTapSupportForPageEnabled(bool enabled) {
736 if (double_tap_support_for_page_ == enabled)
737 return;
738 double_tap_support_for_page_ = enabled;
739 UpdateDoubleTapDetectionSupport();
742 bool GestureProvider::IsScrollInProgress() const {
743 return gesture_listener_->IsScrollInProgress();
746 bool GestureProvider::IsPinchInProgress() const {
747 return gesture_listener_->IsPinchInProgress();
750 bool GestureProvider::IsDoubleTapInProgress() const {
751 return gesture_listener_->IsDoubleTapInProgress();
754 bool GestureProvider::CanHandle(const MotionEvent& event) const {
755 // Aura requires one cancel event per touch point, whereas Android requires
756 // one cancel event per touch sequence. Thus we need to allow extra cancel
757 // events.
758 return current_down_event_ || event.GetAction() == MotionEvent::ACTION_DOWN ||
759 event.GetAction() == MotionEvent::ACTION_CANCEL;
762 void GestureProvider::OnTouchEventHandlingBegin(const MotionEvent& event) {
763 switch (event.GetAction()) {
764 case MotionEvent::ACTION_DOWN:
765 current_down_event_ = event.Clone();
766 if (gesture_begin_end_types_enabled_)
767 gesture_listener_->Send(
768 gesture_listener_->CreateGesture(ET_GESTURE_BEGIN, event));
769 break;
770 case MotionEvent::ACTION_POINTER_DOWN:
771 if (gesture_begin_end_types_enabled_) {
772 const int action_index = event.GetActionIndex();
773 gesture_listener_->Send(gesture_listener_->CreateGesture(
774 ET_GESTURE_BEGIN,
775 event.GetId(),
776 event.GetToolType(),
777 event.GetEventTime(),
778 event.GetX(action_index),
779 event.GetY(action_index),
780 event.GetRawX(action_index),
781 event.GetRawY(action_index),
782 event.GetPointerCount(),
783 gesture_listener_->GetBoundingBox(event, ET_GESTURE_BEGIN),
784 event.GetFlags()));
786 break;
787 case MotionEvent::ACTION_POINTER_UP:
788 case MotionEvent::ACTION_UP:
789 case MotionEvent::ACTION_CANCEL:
790 case MotionEvent::ACTION_MOVE:
791 break;
795 void GestureProvider::OnTouchEventHandlingEnd(const MotionEvent& event) {
796 switch (event.GetAction()) {
797 case MotionEvent::ACTION_UP:
798 case MotionEvent::ACTION_CANCEL: {
799 if (gesture_begin_end_types_enabled_)
800 gesture_listener_->Send(
801 gesture_listener_->CreateGesture(ET_GESTURE_END, event));
803 current_down_event_.reset();
805 UpdateDoubleTapDetectionSupport();
806 break;
808 case MotionEvent::ACTION_POINTER_UP:
809 if (gesture_begin_end_types_enabled_)
810 gesture_listener_->Send(
811 gesture_listener_->CreateGesture(ET_GESTURE_END, event));
812 break;
813 case MotionEvent::ACTION_DOWN:
814 case MotionEvent::ACTION_POINTER_DOWN:
815 case MotionEvent::ACTION_MOVE:
816 break;
820 void GestureProvider::UpdateDoubleTapDetectionSupport() {
821 // The GestureDetector requires that any provided DoubleTapListener remain
822 // attached to it for the duration of a touch sequence. Defer any potential
823 // null'ing of the listener until the sequence has ended.
824 if (current_down_event_)
825 return;
827 const bool double_tap_enabled =
828 double_tap_support_for_page_ && double_tap_support_for_platform_;
829 gesture_listener_->SetDoubleTapEnabled(double_tap_enabled);
832 } // namespace ui