gesture_provider.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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.
4
5#include "ui/events/gesture_detection/gesture_provider.h"
6
7#include <cmath>
8
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"
14
15namespace ui {
16namespace {
17
18// Double-tap drag zoom sensitivity (speed).
19const float kDoubleTapDragZoomSpeed = 0.005f;
20
21const char* GetMotionEventActionName(MotionEvent::Action action) {
22  switch(action) {
23    case MotionEvent::ACTION_POINTER_DOWN: return "ACTION_POINTER_DOWN";
24    case MotionEvent::ACTION_POINTER_UP:   return "ACTION_POINTER_UP";
25    case MotionEvent::ACTION_DOWN:         return "ACTION_DOWN";
26    case MotionEvent::ACTION_UP:           return "ACTION_UP";
27    case MotionEvent::ACTION_CANCEL:       return "ACTION_CANCEL";
28    case MotionEvent::ACTION_MOVE:         return "ACTION_MOVE";
29  }
30  return "";
31}
32
33GestureEventData CreateGesture(EventType type,
34                               base::TimeTicks time,
35                               float x,
36                               float y,
37                               const GestureEventData::Details& details) {
38  return GestureEventData(type, time, x, y, details);
39}
40
41GestureEventData CreateGesture(EventType type,
42                               base::TimeTicks time,
43                               float x,
44                               float y) {
45  return CreateGesture(type, time, x, y, GestureEventData::Details());
46}
47
48GestureEventData CreateGesture(EventType type,
49                               const MotionEvent& event,
50                               const GestureEventData::Details& details) {
51  return CreateGesture(
52      type, event.GetEventTime(), event.GetX(), event.GetY(), details);
53}
54
55GestureEventData CreateGesture(EventType type,
56                               const MotionEvent& event) {
57  return CreateGesture(type, event, GestureEventData::Details());
58}
59
60float Round(float f) {
61  return (f > 0.f) ? std::floor(f + 0.5f) : std::ceil(f - 0.5f);
62}
63
64GestureEventData::Details CreateTapGestureDetails(const MotionEvent& event) {
65  GestureEventData::Details tap_details;
66  // Set the tap count to 1 even for ET_GESTURE_DOUBLE_TAP, in order to be
67  // consistent with double tap behavior on a mobile viewport. See
68  // crbug.com/234986 for context.
69  tap_details.tap.tap_count = 1;
70  tap_details.tap.width = tap_details.tap.height = event.GetTouchMajor();
71  return tap_details;
72}
73
74}  // namespace
75
76// GestureProvider:::Config
77
78GestureProvider::Config::Config() : disable_click_delay(false) {}
79
80GestureProvider::Config::~Config() {}
81
82// GestureProvider::ScaleGestureListener
83
84class GestureProvider::ScaleGestureListenerImpl
85    : public ScaleGestureDetector::ScaleGestureListener {
86 public:
87  ScaleGestureListenerImpl(const ScaleGestureDetector::Config& config,
88                           GestureProvider* provider)
89      : scale_gesture_detector_(config, this),
90        provider_(provider),
91        ignore_detector_events_(false),
92        pinch_event_sent_(false) {}
93
94  bool OnTouchEvent(const MotionEvent& event) {
95    // TODO: Need to deal with multi-touch transition.
96    const bool in_scale_gesture = IsScaleGestureDetectionInProgress();
97    bool handled = scale_gesture_detector_.OnTouchEvent(event);
98    if (!in_scale_gesture &&
99        (event.GetAction() == MotionEvent::ACTION_UP ||
100         event.GetAction() == MotionEvent::ACTION_CANCEL)) {
101      return false;
102    }
103    return handled;
104  }
105
106  // ScaleGestureDetector::ScaleGestureListener implementation.
107  virtual bool OnScaleBegin(const ScaleGestureDetector& detector) OVERRIDE {
108    if (ignore_detector_events_)
109      return false;
110    pinch_event_sent_ = false;
111    return true;
112  }
113
114  virtual void OnScaleEnd(const ScaleGestureDetector& detector) OVERRIDE {
115    if (!pinch_event_sent_)
116      return;
117    provider_->Send(
118        CreateGesture(ET_GESTURE_PINCH_END, detector.GetEventTime(), 0, 0));
119    pinch_event_sent_ = false;
120  }
121
122  virtual bool OnScale(const ScaleGestureDetector& detector) OVERRIDE {
123    if (ignore_detector_events_)
124      return false;
125    if (!pinch_event_sent_) {
126      pinch_event_sent_ = true;
127      provider_->Send(CreateGesture(ET_GESTURE_PINCH_BEGIN,
128                                    detector.GetEventTime(),
129                                    detector.GetFocusX(),
130                                    detector.GetFocusY()));
131    }
132    GestureEventData::Details pinch_details;
133    pinch_details.pinch_update.scale = detector.GetScaleFactor();
134    provider_->Send(CreateGesture(ET_GESTURE_PINCH_UPDATE,
135                                  detector.GetEventTime(),
136                                  detector.GetFocusX(),
137                                  detector.GetFocusY(),
138                                  pinch_details));
139    return true;
140  }
141
142  bool IsScaleGestureDetectionInProgress() const {
143    return !ignore_detector_events_ && scale_gesture_detector_.IsInProgress();
144  }
145
146  void set_ignore_detector_events(bool value) {
147    // Note that returning false from OnScaleBegin / OnScale makes the
148    // gesture detector not to emit further scaling notifications
149    // related to this gesture. Thus, if detector events are enabled in
150    // the middle of the gesture, we don't need to do anything.
151    ignore_detector_events_ = value;
152  }
153
154 private:
155  ScaleGestureDetector scale_gesture_detector_;
156
157  GestureProvider* const provider_;
158
159  // Completely silence scaling events. Used in WebView when zoom support
160  // is turned off.
161  bool ignore_detector_events_;
162
163  // Whether any pinch zoom event has been sent to native.
164  bool pinch_event_sent_;
165
166  DISALLOW_COPY_AND_ASSIGN(ScaleGestureListenerImpl);
167};
168
169// GestureProvider::GestureListener
170
171class GestureProvider::GestureListenerImpl
172    : public GestureDetector::GestureListener,
173      public GestureDetector::DoubleTapListener {
174 public:
175  GestureListenerImpl(
176      const GestureDetector::Config& gesture_detector_config,
177      const SnapScrollController::Config& snap_scroll_controller_config,
178      bool disable_click_delay,
179      GestureProvider* provider)
180      : gesture_detector_(gesture_detector_config, this, this),
181        snap_scroll_controller_(snap_scroll_controller_config),
182        provider_(provider),
183        px_to_dp_(1.0f / snap_scroll_controller_config.device_scale_factor),
184        disable_click_delay_(disable_click_delay),
185        scaled_touch_slop_(gesture_detector_config.scaled_touch_slop),
186        scaled_touch_slop_square_(scaled_touch_slop_ * scaled_touch_slop_),
187        double_tap_timeout_(gesture_detector_config.double_tap_timeout),
188        ignore_single_tap_(false),
189        seen_first_scroll_event_(false),
190        double_tap_mode_(DOUBLE_TAP_MODE_NONE),
191        double_tap_y_(0),
192        double_tap_support_enabled_(true),
193        double_tap_drag_zoom_anchor_x_(0),
194        double_tap_drag_zoom_anchor_y_(0),
195        last_raw_x_(0),
196        last_raw_y_(0),
197        accumulated_scroll_error_x_(0),
198        accumulated_scroll_error_y_(0) {
199    UpdateDoubleTapListener();
200  }
201
202  virtual ~GestureListenerImpl() {}
203
204  bool OnTouchEvent(const MotionEvent& e,
205                    bool is_scale_gesture_detection_in_progress) {
206    snap_scroll_controller_.SetSnapScrollingMode(
207        e, is_scale_gesture_detection_in_progress);
208
209    if (is_scale_gesture_detection_in_progress)
210      SetIgnoreSingleTap(true);
211
212    if (e.GetAction() == MotionEvent::ACTION_POINTER_DOWN ||
213        e.GetAction() == MotionEvent::ACTION_CANCEL) {
214      EndDoubleTapDragIfNecessary(e);
215    } else if (e.GetAction() == MotionEvent::ACTION_DOWN) {
216      gesture_detector_.set_is_longpress_enabled(true);
217    }
218
219    return gesture_detector_.OnTouchEvent(e);
220  }
221
222  // GestureDetector::GestureListener implementation.
223  virtual bool OnDown(const MotionEvent& e) OVERRIDE {
224    current_down_time_ = e.GetEventTime();
225    ignore_single_tap_ = false;
226    seen_first_scroll_event_ = false;
227    last_raw_x_ = e.GetRawX();
228    last_raw_y_ = e.GetRawY();
229    accumulated_scroll_error_x_ = 0;
230    accumulated_scroll_error_y_ = 0;
231
232    GestureEventData::Details tap_details;
233    tap_details.tap.width = tap_details.tap.height = e.GetTouchMajor();
234    provider_->Send(CreateGesture(ET_GESTURE_TAP_DOWN, e, tap_details));
235
236    // Return true to indicate that we want to handle touch.
237    return true;
238  }
239
240  virtual bool OnScroll(const MotionEvent& e1,
241                        const MotionEvent& e2,
242                        float raw_distance_x,
243                        float raw_distance_y) OVERRIDE {
244    float distance_x = raw_distance_x;
245    float distance_y = raw_distance_y;
246    if (!seen_first_scroll_event_) {
247      // Remove the touch slop region from the first scroll event to avoid a
248      // jump.
249      seen_first_scroll_event_ = true;
250      double distance =
251          std::sqrt(distance_x * distance_x + distance_y * distance_y);
252      double epsilon = 1e-3;
253      if (distance > epsilon) {
254        double ratio = std::max(0., distance - scaled_touch_slop_) / distance;
255        distance_x *= ratio;
256        distance_y *= ratio;
257      }
258    }
259    snap_scroll_controller_.UpdateSnapScrollMode(distance_x, distance_y);
260    if (snap_scroll_controller_.IsSnappingScrolls()) {
261      if (snap_scroll_controller_.IsSnapHorizontal()) {
262        distance_y = 0;
263      } else {
264        distance_x = 0;
265      }
266    }
267
268    last_raw_x_ = e2.GetRawX();
269    last_raw_y_ = e2.GetRawY();
270    if (!provider_->IsScrollInProgress()) {
271      // Note that scroll start hints are in distance traveled, where
272      // scroll deltas are in the opposite direction.
273      GestureEventData::Details scroll_details;
274      scroll_details.scroll_begin.delta_x_hint = -raw_distance_x;
275      scroll_details.scroll_begin.delta_y_hint = -raw_distance_y;
276      provider_->Send(CreateGesture(ET_GESTURE_SCROLL_BEGIN,
277                                    e2.GetEventTime(),
278                                    e1.GetX(),
279                                    e1.GetY(),
280                                    scroll_details));
281    }
282
283    // distance_x and distance_y is the scrolling offset since last OnScroll.
284    // Because we are passing integers to Blink, this could introduce
285    // rounding errors. The rounding errors will accumulate overtime.
286    // To solve this, we should be adding back the rounding errors each time
287    // when we calculate the new offset.
288    // TODO(jdduke): Determine if we can simpy use floating point deltas, as
289    // WebGestureEvent also takes floating point deltas for GestureScrollUpdate.
290    int dx = (int)(distance_x + accumulated_scroll_error_x_);
291    int dy = (int)(distance_y + accumulated_scroll_error_y_);
292    accumulated_scroll_error_x_ += (distance_x - dx);
293    accumulated_scroll_error_y_ += (distance_y - dy);
294
295    if (dx || dy) {
296      GestureEventData::Details scroll_details;
297      scroll_details.scroll_update.delta_x = -dx;
298      scroll_details.scroll_update.delta_y = -dy;
299      provider_->Send(
300          CreateGesture(ET_GESTURE_SCROLL_UPDATE, e2, scroll_details));
301    }
302
303    return true;
304  }
305
306  virtual bool OnFling(const MotionEvent& e1,
307                       const MotionEvent& e2,
308                       float velocity_x,
309                       float velocity_y) OVERRIDE {
310    if (snap_scroll_controller_.IsSnappingScrolls()) {
311      if (snap_scroll_controller_.IsSnapHorizontal()) {
312        velocity_y = 0;
313      } else {
314        velocity_x = 0;
315      }
316    }
317
318    provider_->Fling(
319        e2.GetEventTime(), e1.GetX(), e1.GetY(), velocity_x, velocity_y);
320    return true;
321  }
322
323  virtual void OnShowPress(const MotionEvent& e) OVERRIDE {
324    GestureEventData::Details show_press_details;
325    // TODO(jdduke): Expose minor axis length and rotation in |MotionEvent|.
326    show_press_details.show_press.width = e.GetTouchMajor();
327    show_press_details.show_press.height = show_press_details.show_press.width;
328    provider_->Send(
329        CreateGesture(ET_GESTURE_SHOW_PRESS, e, show_press_details));
330  }
331
332  virtual bool OnSingleTapUp(const MotionEvent& e) OVERRIDE {
333    if (IsPointOutsideCurrentSlopRegion(e.GetRawX(), e.GetRawY())) {
334      provider_->SendTapCancelIfNecessary(e);
335      ignore_single_tap_ = true;
336      return true;
337    }
338    // This is a hack to address the issue where user hovers
339    // over a link for longer than double_tap_timeout_, then
340    // OnSingleTapConfirmed() is not triggered. But we still
341    // want to trigger the tap event at UP. So we override
342    // OnSingleTapUp() in this case. This assumes singleTapUp
343    // gets always called before singleTapConfirmed.
344    if (!ignore_single_tap_) {
345      if (e.GetEventTime() - current_down_time_ > double_tap_timeout_) {
346        return OnSingleTapConfirmed(e);
347      } else if (IsDoubleTapDisabled() || disable_click_delay_) {
348        // If double-tap has been disabled, there is no need to wait
349        // for the double-tap timeout.
350        return OnSingleTapConfirmed(e);
351      } else {
352        // Notify Blink about this tapUp event anyway, when none of the above
353        // conditions applied.
354        provider_->Send(CreateGesture(
355            ET_GESTURE_TAP_UNCONFIRMED, e, CreateTapGestureDetails(e)));
356      }
357    }
358
359    return provider_->SendLongTapIfNecessary(e);
360  }
361
362  // GestureDetector::DoubleTapListener implementation.
363  virtual bool OnSingleTapConfirmed(const MotionEvent& e) OVERRIDE {
364    // Long taps in the edges of the screen have their events delayed by
365    // ContentViewHolder for tab swipe operations. As a consequence of the delay
366    // this method might be called after receiving the up event.
367    // These corner cases should be ignored.
368    if (ignore_single_tap_)
369      return true;
370
371    ignore_single_tap_ = true;
372
373    provider_->Send(
374        CreateGesture(ET_GESTURE_TAP, e, CreateTapGestureDetails(e)));
375    return true;
376  }
377
378  virtual bool OnDoubleTap(const MotionEvent& e) OVERRIDE { return false; }
379
380  virtual bool OnDoubleTapEvent(const MotionEvent& e) OVERRIDE {
381    switch (e.GetAction()) {
382      case MotionEvent::ACTION_DOWN:
383        // Note that this will be called before the corresponding |onDown()|
384        // of the same ACTION_DOWN event.  Thus, the preceding TAP_DOWN
385        // should be cancelled prior to sending a new one (in |onDown()|).
386        double_tap_drag_zoom_anchor_x_ = e.GetX();
387        double_tap_drag_zoom_anchor_y_ = e.GetY();
388        double_tap_mode_ = DOUBLE_TAP_MODE_DRAG_DETECTION_IN_PROGRESS;
389        // If a long-press fires during a double-tap, the GestureDetector
390        // will stop feeding MotionEvents to |onDoubleTapEvent()|,
391        // preventing double-tap drag zoom. Long press detection will be
392        // re-enabled on the next ACTION_DOWN.
393        gesture_detector_.set_is_longpress_enabled(false);
394        break;
395      case MotionEvent::ACTION_MOVE:
396        if (double_tap_mode_ == DOUBLE_TAP_MODE_DRAG_DETECTION_IN_PROGRESS) {
397          float distance_x = double_tap_drag_zoom_anchor_x_ - e.GetX();
398          float distance_y = double_tap_drag_zoom_anchor_y_ - e.GetY();
399
400          // Begin double-tap drag zoom mode if the move distance is
401          // further than the threshold.
402          if (IsDistanceGreaterThanTouchSlop(distance_x, distance_y)) {
403            GestureEventData::Details scroll_details;
404            scroll_details.scroll_begin.delta_x_hint = -distance_x;
405            scroll_details.scroll_begin.delta_y_hint = -distance_y;
406            provider_->Send(
407                CreateGesture(ET_GESTURE_SCROLL_BEGIN, e, scroll_details));
408            provider_->Send(
409                CreateGesture(ET_GESTURE_PINCH_BEGIN,
410                              e.GetEventTime(),
411                              Round(double_tap_drag_zoom_anchor_x_),
412                              Round(double_tap_drag_zoom_anchor_y_)));
413            double_tap_mode_ = DOUBLE_TAP_MODE_DRAG_ZOOM;
414          }
415        } else if (double_tap_mode_ == DOUBLE_TAP_MODE_DRAG_ZOOM) {
416          provider_->Send(CreateGesture(ET_GESTURE_SCROLL_UPDATE, e));
417
418          float dy = double_tap_y_ - e.GetY();
419          GestureEventData::Details pinch_details;
420          pinch_details.pinch_update.scale =
421              std::pow(dy > 0 ? 1.0f - kDoubleTapDragZoomSpeed
422                              : 1.0f + kDoubleTapDragZoomSpeed,
423                       std::abs(dy * px_to_dp_));
424          provider_->Send(CreateGesture(ET_GESTURE_PINCH_UPDATE,
425                                        e.GetEventTime(),
426                                        Round(double_tap_drag_zoom_anchor_x_),
427                                        Round(double_tap_drag_zoom_anchor_y_),
428                                        pinch_details));
429        }
430        break;
431      case MotionEvent::ACTION_UP:
432        if (double_tap_mode_ != DOUBLE_TAP_MODE_DRAG_ZOOM) {
433          // Normal double-tap gesture.
434          provider_->Send(CreateGesture(
435              ET_GESTURE_DOUBLE_TAP, e, CreateTapGestureDetails(e)));
436        }
437        EndDoubleTapDragIfNecessary(e);
438        break;
439      case MotionEvent::ACTION_CANCEL:
440        EndDoubleTapDragIfNecessary(e);
441        break;
442      default:
443        NOTREACHED() << "Invalid double-tap event.";
444        break;
445    }
446    double_tap_y_ = e.GetY();
447    return true;
448  }
449
450  virtual bool OnLongPress(const MotionEvent& e) OVERRIDE {
451    DCHECK(!IsDoubleTapInProgress());
452    SetIgnoreSingleTap(true);
453
454    GestureEventData::Details long_press_details;
455    long_press_details.long_press.width = e.GetTouchMajor();
456    long_press_details.long_press.height = long_press_details.long_press.width;
457    provider_->Send(
458        CreateGesture(ET_GESTURE_LONG_PRESS, e, long_press_details));
459
460    // Returning true puts the GestureDetector in "longpress" mode, disabling
461    // further scrolling.  This is undesirable, as it is quite common for a
462    // longpress gesture to fire on content that won't trigger a context menu.
463    return false;
464  }
465
466  void SetDoubleTapSupportForPlatformEnabled(bool enabled) {
467    DCHECK(!IsDoubleTapInProgress());
468    DoubleTapMode double_tap_mode =
469        enabled ? DOUBLE_TAP_MODE_NONE : DOUBLE_TAP_MODE_DISABLED;
470    if (double_tap_mode_ == double_tap_mode)
471      return;
472    double_tap_mode_ = double_tap_mode;
473    UpdateDoubleTapListener();
474  }
475
476  void SetDoubleTapSupportForPageEnabled(bool enabled) {
477    if (double_tap_support_enabled_ == enabled)
478      return;
479    double_tap_support_enabled_ = enabled;
480    UpdateDoubleTapListener();
481  }
482
483  bool IsDoubleTapDisabled() const {
484    return double_tap_mode_ == DOUBLE_TAP_MODE_DISABLED ||
485           !double_tap_support_enabled_;
486  }
487
488  bool IsClickDelayDisabled() const { return disable_click_delay_; }
489
490  bool IsDoubleTapInProgress() const {
491    return double_tap_mode_ != DOUBLE_TAP_MODE_DISABLED &&
492           double_tap_mode_ != DOUBLE_TAP_MODE_NONE;
493  }
494
495 private:
496  enum DoubleTapMode {
497    DOUBLE_TAP_MODE_NONE,
498    DOUBLE_TAP_MODE_DRAG_DETECTION_IN_PROGRESS,
499    DOUBLE_TAP_MODE_DRAG_ZOOM,
500    DOUBLE_TAP_MODE_DISABLED
501  };
502
503  bool IsPointOutsideCurrentSlopRegion(float x, float y) const {
504    return IsDistanceGreaterThanTouchSlop(last_raw_x_ - x, last_raw_y_ - y);
505  }
506
507  bool IsDistanceGreaterThanTouchSlop(float distance_x,
508                                      float distance_y) const {
509    return distance_x * distance_x + distance_y * distance_y >
510           scaled_touch_slop_square_;
511  }
512
513  void SetIgnoreSingleTap(bool value) { ignore_single_tap_ = value; }
514
515  void EndDoubleTapDragIfNecessary(const MotionEvent& event) {
516    if (!IsDoubleTapInProgress())
517      return;
518    if (double_tap_mode_ == DOUBLE_TAP_MODE_DRAG_ZOOM) {
519      provider_->Send(CreateGesture(ET_GESTURE_PINCH_END, event));
520      provider_->Send(CreateGesture(ET_GESTURE_SCROLL_END, event));
521    }
522    double_tap_mode_ = DOUBLE_TAP_MODE_NONE;
523    UpdateDoubleTapListener();
524  }
525
526  void UpdateDoubleTapListener() {
527    if (IsDoubleTapDisabled()) {
528      // Defer nulling the DoubleTapListener until the double-tap gesture is
529      // complete.
530      if (IsDoubleTapInProgress())
531        return;
532      gesture_detector_.set_doubletap_listener(NULL);
533    } else {
534      gesture_detector_.set_doubletap_listener(this);
535    }
536  }
537
538  GestureDetector gesture_detector_;
539  SnapScrollController snap_scroll_controller_;
540
541  GestureProvider* const provider_;
542
543  const float px_to_dp_;
544
545  // Whether the click delay should always be disabled by sending clicks for
546  // double-tap gestures.
547  const bool disable_click_delay_;
548
549  const int scaled_touch_slop_;
550
551  // Cache of square of the scaled touch slop so we don't have to calculate it
552  // on every touch.
553  const int scaled_touch_slop_square_;
554
555  const base::TimeDelta double_tap_timeout_;
556
557  base::TimeTicks current_down_time_;
558
559  // TODO(klobag): This is to avoid a bug in GestureDetector. With multi-touch,
560  // always_in_tap_region_ is not reset. So when the last finger is up,
561  // OnSingleTapUp() will be mistakenly fired.
562  bool ignore_single_tap_;
563
564  // Used to remove the touch slop from the initial scroll event in a scroll
565  // gesture.
566  bool seen_first_scroll_event_;
567
568  // Indicate current double-tap mode state.
569  int double_tap_mode_;
570
571  // On double-tap this will store the y coordinates of the touch.
572  float double_tap_y_;
573
574  // The page's viewport and scale sometimes allow us to disable double-tap
575  // gesture detection,
576  // according to the logic in ContentViewCore.onRenderCoordinatesUpdated().
577  bool double_tap_support_enabled_;
578
579  // x, y coordinates for an Anchor on double-tap drag zoom.
580  float double_tap_drag_zoom_anchor_x_;
581  float double_tap_drag_zoom_anchor_y_;
582
583  // Used to track the last rawX/Y coordinates for moves.  This gives absolute
584  // scroll distance.
585  // Useful for full screen tracking.
586  float last_raw_x_;
587  float last_raw_y_;
588
589  // Used to track the accumulated scroll error over time. This is used to
590  // remove the
591  // rounding error we introduced by passing integers to webkit.
592  float accumulated_scroll_error_x_;
593  float accumulated_scroll_error_y_;
594
595  DISALLOW_COPY_AND_ASSIGN(GestureListenerImpl);
596};
597
598// GestureProvider
599
600GestureProvider::GestureProvider(const Config& config,
601                                 GestureProviderClient* client)
602    : client_(client),
603      needs_show_press_event_(false),
604      needs_tap_ending_event_(false),
605      touch_scroll_in_progress_(false),
606      pinch_in_progress_(false) {
607  DCHECK(client);
608  InitGestureDetectors(config);
609}
610
611GestureProvider::~GestureProvider() {}
612
613bool GestureProvider::OnTouchEvent(const MotionEvent& event) {
614  TRACE_EVENT1("input", "GestureProvider::OnTouchEvent",
615               "action", GetMotionEventActionName(event.GetAction()));
616  if (!CanHandle(event))
617    return false;
618
619  const bool was_touch_scrolling_ = touch_scroll_in_progress_;
620  const bool in_scale_gesture =
621      scale_gesture_listener_->IsScaleGestureDetectionInProgress();
622
623  if (event.GetAction() == MotionEvent::ACTION_DOWN) {
624    current_down_event_ = event.Clone();
625    touch_scroll_in_progress_ = false;
626    needs_show_press_event_ = true;
627    current_longpress_time_ = base::TimeTicks();
628    SendTapCancelIfNecessary(event);
629  }
630
631  bool handled = gesture_listener_->OnTouchEvent(event, in_scale_gesture);
632  handled |= scale_gesture_listener_->OnTouchEvent(event);
633
634  if (event.GetAction() == MotionEvent::ACTION_UP ||
635      event.GetAction() == MotionEvent::ACTION_CANCEL) {
636    // "Last finger raised" could be an end to movement, but it should
637    // only terminate scrolling if the event did not cause a fling.
638    if (was_touch_scrolling_ && !handled)
639      EndTouchScrollIfNecessary(event.GetEventTime(), true);
640
641    // We shouldn't necessarily cancel a tap on ACTION_UP, as the double-tap
642    // timeout may yet trigger a SINGLE_TAP.
643    if (event.GetAction() == MotionEvent::ACTION_CANCEL)
644      SendTapCancelIfNecessary(event);
645
646    current_down_event_.reset();
647  }
648
649  return true;
650}
651
652void GestureProvider::ResetGestureDetectors() {
653  if (!current_down_event_)
654    return;
655  scoped_ptr<MotionEvent> cancel_event = current_down_event_->Cancel();
656  gesture_listener_->OnTouchEvent(*cancel_event, false);
657  scale_gesture_listener_->OnTouchEvent(*cancel_event);
658}
659
660void GestureProvider::SetMultiTouchSupportEnabled(bool enabled) {
661  scale_gesture_listener_->set_ignore_detector_events(!enabled);
662}
663
664void GestureProvider::SetDoubleTapSupportForPlatformEnabled(bool enabled) {
665  gesture_listener_->SetDoubleTapSupportForPlatformEnabled(enabled);
666}
667
668void GestureProvider::SetDoubleTapSupportForPageEnabled(bool enabled) {
669  gesture_listener_->SetDoubleTapSupportForPageEnabled(enabled);
670}
671
672bool GestureProvider::IsScrollInProgress() const {
673  // TODO(wangxianzhu): Also return true when fling is active once the UI knows
674  // exactly when the fling ends.
675  return touch_scroll_in_progress_;
676}
677
678bool GestureProvider::IsPinchInProgress() const { return pinch_in_progress_; }
679
680bool GestureProvider::IsDoubleTapInProgress() const {
681  return gesture_listener_->IsDoubleTapInProgress();
682}
683
684bool GestureProvider::IsClickDelayDisabled() const {
685  return gesture_listener_->IsClickDelayDisabled();
686}
687
688void GestureProvider::InitGestureDetectors(const Config& config) {
689  TRACE_EVENT0("input", "GestureProvider::InitGestureDetectors");
690  gesture_listener_.reset(
691      new GestureListenerImpl(config.gesture_detector_config,
692                              config.snap_scroll_controller_config,
693                              config.disable_click_delay,
694                              this));
695
696  scale_gesture_listener_.reset(
697      new ScaleGestureListenerImpl(config.scale_gesture_detector_config, this));
698}
699
700bool GestureProvider::CanHandle(const MotionEvent& event) const {
701  return event.GetAction() == MotionEvent::ACTION_DOWN || current_down_event_;
702}
703
704void GestureProvider::Fling(base::TimeTicks time,
705                            float x,
706                            float y,
707                            float velocity_x,
708                            float velocity_y) {
709  if (!velocity_x && !velocity_y) {
710    EndTouchScrollIfNecessary(time, true);
711    return;
712  }
713
714  if (!touch_scroll_in_progress_) {
715    // The native side needs a ET_GESTURE_SCROLL_BEGIN before
716    // ET_SCROLL_FLING_START to send the fling to the correct target. Send if it
717    // has not sent.  The distance traveled in one second is a reasonable scroll
718    // start hint.
719    GestureEventData::Details scroll_details;
720    scroll_details.scroll_begin.delta_x_hint = velocity_x;
721    scroll_details.scroll_begin.delta_y_hint = velocity_y;
722    Send(CreateGesture(ET_GESTURE_SCROLL_BEGIN, time, x, y, scroll_details));
723  }
724  EndTouchScrollIfNecessary(time, false);
725
726  GestureEventData::Details fling_details;
727  fling_details.fling_start.velocity_x = velocity_x;
728  fling_details.fling_start.velocity_y = velocity_y;
729  Send(CreateGesture(ET_SCROLL_FLING_START, time, x, y, fling_details));
730}
731
732void GestureProvider::Send(const GestureEventData& gesture) {
733  DCHECK(!gesture.time.is_null());
734  // The only valid events that should be sent without an active touch sequence
735  // are SHOW_PRESS and TAP, potentially triggered by the double-tap
736  // delay timing out.
737  DCHECK(current_down_event_ || gesture.type == ET_GESTURE_TAP ||
738         gesture.type == ET_GESTURE_SHOW_PRESS);
739
740  switch (gesture.type) {
741    case ET_GESTURE_TAP_DOWN:
742      needs_tap_ending_event_ = true;
743      break;
744    case ET_GESTURE_TAP_UNCONFIRMED:
745      needs_show_press_event_ = false;
746      break;
747    case ET_GESTURE_TAP:
748      if (needs_show_press_event_)
749        Send(CreateGesture(
750            ET_GESTURE_SHOW_PRESS, gesture.time, gesture.x, gesture.y));
751      needs_tap_ending_event_ = false;
752      break;
753    case ET_GESTURE_DOUBLE_TAP:
754      needs_tap_ending_event_ = false;
755      break;
756    case ET_GESTURE_TAP_CANCEL:
757      if (!needs_tap_ending_event_)
758        return;
759      needs_tap_ending_event_ = false;
760      break;
761    case ET_GESTURE_SHOW_PRESS:
762      needs_show_press_event_ = false;
763      break;
764    case ET_GESTURE_LONG_PRESS:
765      DCHECK(!scale_gesture_listener_->IsScaleGestureDetectionInProgress());
766      current_longpress_time_ = gesture.time;
767      break;
768    case ET_GESTURE_LONG_TAP:
769      needs_tap_ending_event_ = false;
770      current_longpress_time_ = base::TimeTicks();
771      break;
772    case ET_GESTURE_SCROLL_BEGIN:
773      touch_scroll_in_progress_ = true;
774      SendTapCancelIfNecessary(*current_down_event_);
775      break;
776    case ET_GESTURE_SCROLL_END:
777      touch_scroll_in_progress_ = false;
778      break;
779    case ET_GESTURE_PINCH_BEGIN:
780      pinch_in_progress_ = true;
781      break;
782    case ET_GESTURE_PINCH_END:
783      pinch_in_progress_ = false;
784      break;
785    default:
786      break;
787  };
788
789  client_->OnGestureEvent(gesture);
790}
791
792void GestureProvider::SendTapCancelIfNecessary(const MotionEvent& event) {
793  if (!needs_tap_ending_event_)
794    return;
795  current_longpress_time_ = base::TimeTicks();
796  Send(CreateGesture(ET_GESTURE_TAP_CANCEL, event));
797}
798
799bool GestureProvider::SendLongTapIfNecessary(const MotionEvent& event) {
800  if (event.GetAction() == MotionEvent::ACTION_UP &&
801      !current_longpress_time_.is_null() &&
802      !scale_gesture_listener_->IsScaleGestureDetectionInProgress()) {
803    SendTapCancelIfNecessary(event);
804    GestureEventData::Details long_tap_details;
805    long_tap_details.long_press.width = event.GetTouchMajor();
806    long_tap_details.long_press.height = long_tap_details.long_press.width;
807    Send(CreateGesture(ET_GESTURE_LONG_TAP, event, long_tap_details));
808    return true;
809  }
810  return false;
811}
812
813void GestureProvider::EndTouchScrollIfNecessary(base::TimeTicks time,
814                                                bool send_scroll_end_event) {
815  if (!touch_scroll_in_progress_)
816    return;
817  touch_scroll_in_progress_ = false;
818  if (send_scroll_end_event)
819    Send(CreateGesture(ET_GESTURE_SCROLL_END, time, 0, 0));
820}
821
822}  //  namespace ui
823