• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 // MSVC++ requires this to be set before any other includes to get M_PI.
6 #define _USE_MATH_DEFINES
7 
8 #include "ui/events/gesture_detection/gesture_detector.h"
9 
10 #include <cmath>
11 
12 #include "base/timer/timer.h"
13 #include "ui/events/gesture_detection/motion_event.h"
14 
15 namespace ui {
16 namespace {
17 
18 // Using a small epsilon when comparing slop distances allows pixel perfect
19 // slop determination when using fractional DIP coordinates (assuming the slop
20 // region and DPI scale are reasonably proportioned).
21 const float kSlopEpsilon = .05f;
22 
23 // Minimum distance a scroll must have traveled from the last scroll/focal point
24 // to trigger an |OnScroll| callback.
25 const float kScrollEpsilon = .1f;
26 
27 const float kDegreesToRadians = static_cast<float>(M_PI) / 180.0f;
28 
29 // Constants used by TimeoutGestureHandler.
30 enum TimeoutEvent {
31   SHOW_PRESS = 0,
32   LONG_PRESS,
33   TAP,
34   TIMEOUT_EVENT_COUNT
35 };
36 
37 }  // namespace
38 
39 // Note: These constants were taken directly from the default (unscaled)
40 // versions found in Android's ViewConfiguration.
Config()41 GestureDetector::Config::Config()
42     : longpress_timeout(base::TimeDelta::FromMilliseconds(500)),
43       showpress_timeout(base::TimeDelta::FromMilliseconds(180)),
44       double_tap_timeout(base::TimeDelta::FromMilliseconds(300)),
45       double_tap_min_time(base::TimeDelta::FromMilliseconds(40)),
46       touch_slop(8),
47       double_tap_slop(100),
48       minimum_fling_velocity(50),
49       maximum_fling_velocity(8000),
50       swipe_enabled(false),
51       minimum_swipe_velocity(20),
52       maximum_swipe_deviation_angle(20.f),
53       two_finger_tap_enabled(false),
54       two_finger_tap_max_separation(300),
55       two_finger_tap_timeout(base::TimeDelta::FromMilliseconds(700)) {
56 }
57 
~Config()58 GestureDetector::Config::~Config() {}
59 
OnDown(const MotionEvent & e)60 bool GestureDetector::SimpleGestureListener::OnDown(const MotionEvent& e) {
61   return false;
62 }
63 
OnShowPress(const MotionEvent & e)64 void GestureDetector::SimpleGestureListener::OnShowPress(const MotionEvent& e) {
65 }
66 
OnSingleTapUp(const MotionEvent & e)67 bool GestureDetector::SimpleGestureListener::OnSingleTapUp(
68     const MotionEvent& e) {
69   return false;
70 }
71 
OnLongPress(const MotionEvent & e)72 void GestureDetector::SimpleGestureListener::OnLongPress(const MotionEvent& e) {
73 }
74 
OnScroll(const MotionEvent & e1,const MotionEvent & e2,float distance_x,float distance_y)75 bool GestureDetector::SimpleGestureListener::OnScroll(const MotionEvent& e1,
76                                                       const MotionEvent& e2,
77                                                       float distance_x,
78                                                       float distance_y) {
79   return false;
80 }
81 
OnFling(const MotionEvent & e1,const MotionEvent & e2,float velocity_x,float velocity_y)82 bool GestureDetector::SimpleGestureListener::OnFling(const MotionEvent& e1,
83                                                      const MotionEvent& e2,
84                                                      float velocity_x,
85                                                      float velocity_y) {
86   return false;
87 }
88 
OnSwipe(const MotionEvent & e1,const MotionEvent & e2,float velocity_x,float velocity_y)89 bool GestureDetector::SimpleGestureListener::OnSwipe(const MotionEvent& e1,
90                                                      const MotionEvent& e2,
91                                                      float velocity_x,
92                                                      float velocity_y) {
93   return false;
94 }
95 
OnTwoFingerTap(const MotionEvent & e1,const MotionEvent & e2)96 bool GestureDetector::SimpleGestureListener::OnTwoFingerTap(
97     const MotionEvent& e1,
98     const MotionEvent& e2) {
99   return false;
100 }
101 
OnSingleTapConfirmed(const MotionEvent & e)102 bool GestureDetector::SimpleGestureListener::OnSingleTapConfirmed(
103     const MotionEvent& e) {
104   return false;
105 }
106 
OnDoubleTap(const MotionEvent & e)107 bool GestureDetector::SimpleGestureListener::OnDoubleTap(const MotionEvent& e) {
108   return false;
109 }
110 
OnDoubleTapEvent(const MotionEvent & e)111 bool GestureDetector::SimpleGestureListener::OnDoubleTapEvent(
112     const MotionEvent& e) {
113   return false;
114 }
115 
116 class GestureDetector::TimeoutGestureHandler {
117  public:
TimeoutGestureHandler(const Config & config,GestureDetector * gesture_detector)118   TimeoutGestureHandler(const Config& config, GestureDetector* gesture_detector)
119       : gesture_detector_(gesture_detector) {
120     DCHECK(config.showpress_timeout <= config.longpress_timeout);
121 
122     timeout_callbacks_[SHOW_PRESS] = &GestureDetector::OnShowPressTimeout;
123     timeout_delays_[SHOW_PRESS] = config.showpress_timeout;
124 
125     timeout_callbacks_[LONG_PRESS] = &GestureDetector::OnLongPressTimeout;
126     timeout_delays_[LONG_PRESS] =
127         config.longpress_timeout + config.showpress_timeout;
128 
129     timeout_callbacks_[TAP] = &GestureDetector::OnTapTimeout;
130     timeout_delays_[TAP] = config.double_tap_timeout;
131   }
132 
~TimeoutGestureHandler()133   ~TimeoutGestureHandler() {
134     Stop();
135   }
136 
StartTimeout(TimeoutEvent event)137   void StartTimeout(TimeoutEvent event) {
138     timeout_timers_[event].Start(FROM_HERE,
139                                  timeout_delays_[event],
140                                  gesture_detector_,
141                                  timeout_callbacks_[event]);
142   }
143 
StopTimeout(TimeoutEvent event)144   void StopTimeout(TimeoutEvent event) { timeout_timers_[event].Stop(); }
145 
Stop()146   void Stop() {
147     for (size_t i = SHOW_PRESS; i < TIMEOUT_EVENT_COUNT; ++i)
148       timeout_timers_[i].Stop();
149   }
150 
HasTimeout(TimeoutEvent event) const151   bool HasTimeout(TimeoutEvent event) const {
152     return timeout_timers_[event].IsRunning();
153   }
154 
155  private:
156   typedef void (GestureDetector::*ReceiverMethod)();
157 
158   GestureDetector* const gesture_detector_;
159   base::OneShotTimer<GestureDetector> timeout_timers_[TIMEOUT_EVENT_COUNT];
160   ReceiverMethod timeout_callbacks_[TIMEOUT_EVENT_COUNT];
161   base::TimeDelta timeout_delays_[TIMEOUT_EVENT_COUNT];
162 };
163 
GestureDetector(const Config & config,GestureListener * listener,DoubleTapListener * optional_double_tap_listener)164 GestureDetector::GestureDetector(
165     const Config& config,
166     GestureListener* listener,
167     DoubleTapListener* optional_double_tap_listener)
168     : timeout_handler_(new TimeoutGestureHandler(config, this)),
169       listener_(listener),
170       double_tap_listener_(optional_double_tap_listener),
171       touch_slop_square_(0),
172       double_tap_touch_slop_square_(0),
173       double_tap_slop_square_(0),
174       two_finger_tap_distance_square_(0),
175       min_fling_velocity_(1),
176       max_fling_velocity_(1),
177       min_swipe_velocity_(0),
178       min_swipe_direction_component_ratio_(0),
179       still_down_(false),
180       defer_confirm_single_tap_(false),
181       always_in_tap_region_(false),
182       always_in_bigger_tap_region_(false),
183       two_finger_tap_allowed_for_gesture_(false),
184       is_double_tapping_(false),
185       last_focus_x_(0),
186       last_focus_y_(0),
187       down_focus_x_(0),
188       down_focus_y_(0),
189       longpress_enabled_(true),
190       swipe_enabled_(false),
191       two_finger_tap_enabled_(false) {
192   DCHECK(listener_);
193   Init(config);
194 }
195 
~GestureDetector()196 GestureDetector::~GestureDetector() {}
197 
OnTouchEvent(const MotionEvent & ev)198 bool GestureDetector::OnTouchEvent(const MotionEvent& ev) {
199   const MotionEvent::Action action = ev.GetAction();
200 
201   velocity_tracker_.AddMovement(ev);
202 
203   const bool pointer_up = action == MotionEvent::ACTION_POINTER_UP;
204   const int skip_index = pointer_up ? ev.GetActionIndex() : -1;
205 
206   // Determine focal point.
207   float sum_x = 0, sum_y = 0;
208   const int count = static_cast<int>(ev.GetPointerCount());
209   for (int i = 0; i < count; i++) {
210     if (skip_index == i)
211       continue;
212     sum_x += ev.GetX(i);
213     sum_y += ev.GetY(i);
214   }
215   const int div = pointer_up ? count - 1 : count;
216   const float focus_x = sum_x / div;
217   const float focus_y = sum_y / div;
218 
219   bool handled = false;
220 
221   switch (action) {
222     case MotionEvent::ACTION_POINTER_DOWN: {
223       down_focus_x_ = last_focus_x_ = focus_x;
224       down_focus_y_ = last_focus_y_ = focus_y;
225       // Cancel long press and taps.
226       CancelTaps();
227 
228       if (!two_finger_tap_allowed_for_gesture_)
229         break;
230 
231       const int action_index = ev.GetActionIndex();
232       const float dx = ev.GetX(action_index) - current_down_event_->GetX();
233       const float dy = ev.GetY(action_index) - current_down_event_->GetY();
234 
235       if (ev.GetPointerCount() == 2 &&
236           dx * dx + dy * dy < two_finger_tap_distance_square_) {
237         secondary_pointer_down_event_ = ev.Clone();
238       } else {
239         two_finger_tap_allowed_for_gesture_ = false;
240       }
241     } break;
242 
243     case MotionEvent::ACTION_POINTER_UP: {
244       down_focus_x_ = last_focus_x_ = focus_x;
245       down_focus_y_ = last_focus_y_ = focus_y;
246 
247       // Check the dot product of current velocities.
248       // If the pointer that left was opposing another velocity vector, clear.
249       velocity_tracker_.ComputeCurrentVelocity(1000, max_fling_velocity_);
250       const int up_index = ev.GetActionIndex();
251       const int id1 = ev.GetPointerId(up_index);
252       const float vx1 = velocity_tracker_.GetXVelocity(id1);
253       const float vy1 = velocity_tracker_.GetYVelocity(id1);
254       float vx_total = vx1;
255       float vy_total = vy1;
256       for (int i = 0; i < count; i++) {
257         if (i == up_index)
258           continue;
259 
260         const int id2 = ev.GetPointerId(i);
261         const float vx2 = velocity_tracker_.GetXVelocity(id2);
262         const float vy2 = velocity_tracker_.GetYVelocity(id2);
263         const float dot = vx1 * vx2 + vy1 * vy2;
264         if (dot < 0) {
265           vx_total = 0;
266           vy_total = 0;
267           velocity_tracker_.Clear();
268           break;
269         }
270         vx_total += vx2;
271         vy_total += vy2;
272       }
273 
274       handled = HandleSwipeIfNeeded(ev, vx_total / count, vy_total / count);
275 
276       if (two_finger_tap_allowed_for_gesture_ && ev.GetPointerCount() == 2 &&
277           (ev.GetEventTime() - secondary_pointer_down_event_->GetEventTime() <=
278            two_finger_tap_timeout_)) {
279         handled = listener_->OnTwoFingerTap(*current_down_event_, ev);
280       }
281       two_finger_tap_allowed_for_gesture_ = false;
282     } break;
283 
284     case MotionEvent::ACTION_DOWN:
285       if (double_tap_listener_) {
286         bool had_tap_message = timeout_handler_->HasTimeout(TAP);
287         if (had_tap_message)
288           timeout_handler_->StopTimeout(TAP);
289         if (current_down_event_ && previous_up_event_ && had_tap_message &&
290             IsConsideredDoubleTap(
291                 *current_down_event_, *previous_up_event_, ev)) {
292           // This is a second tap.
293           is_double_tapping_ = true;
294           // Give a callback with the first tap of the double-tap.
295           handled |= double_tap_listener_->OnDoubleTap(*current_down_event_);
296           // Give a callback with down event of the double-tap.
297           handled |= double_tap_listener_->OnDoubleTapEvent(ev);
298         } else {
299           // This is a first tap.
300           DCHECK(double_tap_timeout_ > base::TimeDelta());
301           timeout_handler_->StartTimeout(TAP);
302         }
303       }
304 
305       down_focus_x_ = last_focus_x_ = focus_x;
306       down_focus_y_ = last_focus_y_ = focus_y;
307       current_down_event_ = ev.Clone();
308 
309       secondary_pointer_down_event_.reset();
310       always_in_tap_region_ = true;
311       always_in_bigger_tap_region_ = true;
312       still_down_ = true;
313       defer_confirm_single_tap_ = false;
314       two_finger_tap_allowed_for_gesture_ = two_finger_tap_enabled_;
315 
316       // Always start the SHOW_PRESS timer before the LONG_PRESS timer to ensure
317       // proper timeout ordering.
318       timeout_handler_->StartTimeout(SHOW_PRESS);
319       if (longpress_enabled_)
320         timeout_handler_->StartTimeout(LONG_PRESS);
321       handled |= listener_->OnDown(ev);
322       break;
323 
324     case MotionEvent::ACTION_MOVE:
325       {
326         const float scroll_x = last_focus_x_ - focus_x;
327         const float scroll_y = last_focus_y_ - focus_y;
328         if (is_double_tapping_) {
329           // Give the move events of the double-tap.
330           DCHECK(double_tap_listener_);
331           handled |= double_tap_listener_->OnDoubleTapEvent(ev);
332         } else if (always_in_tap_region_) {
333           const float delta_x = focus_x - down_focus_x_;
334           const float delta_y = focus_y - down_focus_y_;
335           const float distance_square = delta_x * delta_x + delta_y * delta_y;
336           if (distance_square > touch_slop_square_) {
337             handled = listener_->OnScroll(
338                 *current_down_event_, ev, scroll_x, scroll_y);
339             last_focus_x_ = focus_x;
340             last_focus_y_ = focus_y;
341             always_in_tap_region_ = false;
342             timeout_handler_->Stop();
343           }
344           if (distance_square > double_tap_touch_slop_square_)
345             always_in_bigger_tap_region_ = false;
346         } else if (std::abs(scroll_x) > kScrollEpsilon ||
347                    std::abs(scroll_y) > kScrollEpsilon) {
348           handled =
349               listener_->OnScroll(*current_down_event_, ev, scroll_x, scroll_y);
350           last_focus_x_ = focus_x;
351           last_focus_y_ = focus_y;
352         }
353 
354         if (!two_finger_tap_allowed_for_gesture_)
355           break;
356 
357         // Two-finger tap should be prevented if either pointer exceeds its
358         // (independent) slop region.
359         const int id0 = current_down_event_->GetPointerId(0);
360         const int ev_idx0 = ev.GetPointerId(0) == id0 ? 0 : 1;
361 
362         // Check if the primary pointer exceeded the slop region.
363         float dx = current_down_event_->GetX() - ev.GetX(ev_idx0);
364         float dy = current_down_event_->GetY() - ev.GetY(ev_idx0);
365         if (dx * dx + dy * dy > touch_slop_square_) {
366           two_finger_tap_allowed_for_gesture_ = false;
367           break;
368         }
369         if (ev.GetPointerCount() == 2) {
370           // Check if the secondary pointer exceeded the slop region.
371           const int ev_idx1 = ev_idx0 == 0 ? 1 : 0;
372           const int idx1 = secondary_pointer_down_event_->GetActionIndex();
373           dx = secondary_pointer_down_event_->GetX(idx1) - ev.GetX(ev_idx1);
374           dy = secondary_pointer_down_event_->GetY(idx1) - ev.GetY(ev_idx1);
375           if (dx * dx + dy * dy > touch_slop_square_)
376             two_finger_tap_allowed_for_gesture_ = false;
377         }
378       }
379       break;
380 
381     case MotionEvent::ACTION_UP:
382       still_down_ = false;
383       {
384         if (is_double_tapping_) {
385           // Finally, give the up event of the double-tap.
386           DCHECK(double_tap_listener_);
387           handled |= double_tap_listener_->OnDoubleTapEvent(ev);
388         } else if (always_in_tap_region_) {
389           handled = listener_->OnSingleTapUp(ev);
390           if (defer_confirm_single_tap_ && double_tap_listener_ != NULL) {
391             double_tap_listener_->OnSingleTapConfirmed(ev);
392           }
393         } else {
394 
395           // A fling must travel the minimum tap distance.
396           const int pointer_id = ev.GetPointerId(0);
397           velocity_tracker_.ComputeCurrentVelocity(1000, max_fling_velocity_);
398           const float velocity_y = velocity_tracker_.GetYVelocity(pointer_id);
399           const float velocity_x = velocity_tracker_.GetXVelocity(pointer_id);
400 
401           if ((std::abs(velocity_y) > min_fling_velocity_) ||
402               (std::abs(velocity_x) > min_fling_velocity_)) {
403             handled = listener_->OnFling(
404                 *current_down_event_, ev, velocity_x, velocity_y);
405           }
406 
407           handled |= HandleSwipeIfNeeded(ev, velocity_x, velocity_y);
408         }
409 
410         previous_up_event_ = ev.Clone();
411 
412         velocity_tracker_.Clear();
413         is_double_tapping_ = false;
414         defer_confirm_single_tap_ = false;
415         timeout_handler_->StopTimeout(SHOW_PRESS);
416         timeout_handler_->StopTimeout(LONG_PRESS);
417       }
418       break;
419 
420     case MotionEvent::ACTION_CANCEL:
421       Cancel();
422       break;
423   }
424 
425   return handled;
426 }
427 
SetDoubleTapListener(DoubleTapListener * double_tap_listener)428 void GestureDetector::SetDoubleTapListener(
429     DoubleTapListener* double_tap_listener) {
430   if (double_tap_listener == double_tap_listener_)
431     return;
432 
433   DCHECK(!is_double_tapping_);
434 
435   // Null'ing the double-tap listener should flush an active tap timeout.
436   if (!double_tap_listener) {
437     if (timeout_handler_->HasTimeout(TAP)) {
438       timeout_handler_->StopTimeout(TAP);
439       OnTapTimeout();
440     }
441   }
442 
443   double_tap_listener_ = double_tap_listener;
444 }
445 
Init(const Config & config)446 void GestureDetector::Init(const Config& config) {
447   DCHECK(listener_);
448 
449   const float touch_slop = config.touch_slop + kSlopEpsilon;
450   const float double_tap_touch_slop = touch_slop;
451   const float double_tap_slop = config.double_tap_slop + kSlopEpsilon;
452   touch_slop_square_ = touch_slop * touch_slop;
453   double_tap_touch_slop_square_ = double_tap_touch_slop * double_tap_touch_slop;
454   double_tap_slop_square_ = double_tap_slop * double_tap_slop;
455   double_tap_timeout_ = config.double_tap_timeout;
456   double_tap_min_time_ = config.double_tap_min_time;
457   DCHECK(double_tap_min_time_ < double_tap_timeout_);
458   min_fling_velocity_ = config.minimum_fling_velocity;
459   max_fling_velocity_ = config.maximum_fling_velocity;
460 
461   swipe_enabled_ = config.swipe_enabled;
462   min_swipe_velocity_ = config.minimum_swipe_velocity;
463   DCHECK_GT(config.maximum_swipe_deviation_angle, 0);
464   DCHECK_LE(config.maximum_swipe_deviation_angle, 45);
465   const float maximum_swipe_deviation_angle =
466       std::min(45.f, std::max(0.001f, config.maximum_swipe_deviation_angle));
467   min_swipe_direction_component_ratio_ =
468       1.f / tan(maximum_swipe_deviation_angle * kDegreesToRadians);
469 
470   two_finger_tap_enabled_ = config.two_finger_tap_enabled;
471   two_finger_tap_distance_square_ = config.two_finger_tap_max_separation *
472                                     config.two_finger_tap_max_separation;
473   two_finger_tap_timeout_ = config.two_finger_tap_timeout;
474 }
475 
OnShowPressTimeout()476 void GestureDetector::OnShowPressTimeout() {
477   listener_->OnShowPress(*current_down_event_);
478 }
479 
OnLongPressTimeout()480 void GestureDetector::OnLongPressTimeout() {
481   timeout_handler_->StopTimeout(TAP);
482   defer_confirm_single_tap_ = false;
483   listener_->OnLongPress(*current_down_event_);
484 }
485 
OnTapTimeout()486 void GestureDetector::OnTapTimeout() {
487   if (!double_tap_listener_)
488     return;
489   if (!still_down_)
490     double_tap_listener_->OnSingleTapConfirmed(*current_down_event_);
491   else
492     defer_confirm_single_tap_ = true;
493 }
494 
Cancel()495 void GestureDetector::Cancel() {
496   CancelTaps();
497   velocity_tracker_.Clear();
498   still_down_ = false;
499 }
500 
CancelTaps()501 void GestureDetector::CancelTaps() {
502   timeout_handler_->Stop();
503   is_double_tapping_ = false;
504   always_in_tap_region_ = false;
505   always_in_bigger_tap_region_ = false;
506   defer_confirm_single_tap_ = false;
507 }
508 
IsConsideredDoubleTap(const MotionEvent & first_down,const MotionEvent & first_up,const MotionEvent & second_down) const509 bool GestureDetector::IsConsideredDoubleTap(
510     const MotionEvent& first_down,
511     const MotionEvent& first_up,
512     const MotionEvent& second_down) const {
513   if (!always_in_bigger_tap_region_)
514     return false;
515 
516   const base::TimeDelta delta_time =
517       second_down.GetEventTime() - first_up.GetEventTime();
518   if (delta_time < double_tap_min_time_ || delta_time > double_tap_timeout_)
519     return false;
520 
521   const float delta_x = first_down.GetX() - second_down.GetX();
522   const float delta_y = first_down.GetY() - second_down.GetY();
523   return (delta_x * delta_x + delta_y * delta_y < double_tap_slop_square_);
524 }
525 
HandleSwipeIfNeeded(const MotionEvent & up,float vx,float vy)526 bool GestureDetector::HandleSwipeIfNeeded(const MotionEvent& up,
527                                           float vx,
528                                           float vy) {
529   if (!swipe_enabled_ || (!vx && !vy))
530     return false;
531   float vx_abs = std::abs(vx);
532   float vy_abs = std::abs(vy);
533 
534   if (vx_abs < min_swipe_velocity_)
535     vx_abs = vx = 0;
536   if (vy_abs < min_swipe_velocity_)
537     vy_abs = vy = 0;
538 
539   // Note that the ratio will be 0 if both velocites are below the min.
540   float ratio = vx_abs > vy_abs ? vx_abs / std::max(vy_abs, 0.001f)
541                                 : vy_abs / std::max(vx_abs, 0.001f);
542 
543   if (ratio < min_swipe_direction_component_ratio_)
544     return false;
545 
546   if (vx_abs > vy_abs)
547     vy = 0;
548   else
549     vx = 0;
550   return listener_->OnSwipe(*current_down_event_, up, vx, vy);
551 }
552 
553 }  // namespace ui
554