Home | History | Annotate | Download | only in gesture_detection
      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.
     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 
     58 GestureDetector::Config::~Config() {}
     59 
     60 bool GestureDetector::SimpleGestureListener::OnDown(const MotionEvent& e) {
     61   return false;
     62 }
     63 
     64 void GestureDetector::SimpleGestureListener::OnShowPress(const MotionEvent& e) {
     65 }
     66 
     67 bool GestureDetector::SimpleGestureListener::OnSingleTapUp(
     68     const MotionEvent& e) {
     69   return false;
     70 }
     71 
     72 void GestureDetector::SimpleGestureListener::OnLongPress(const MotionEvent& e) {
     73 }
     74 
     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 
     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 
     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 
     96 bool GestureDetector::SimpleGestureListener::OnTwoFingerTap(
     97     const MotionEvent& e1,
     98     const MotionEvent& e2) {
     99   return false;
    100 }
    101 
    102 bool GestureDetector::SimpleGestureListener::OnSingleTapConfirmed(
    103     const MotionEvent& e) {
    104   return false;
    105 }
    106 
    107 bool GestureDetector::SimpleGestureListener::OnDoubleTap(const MotionEvent& e) {
    108   return false;
    109 }
    110 
    111 bool GestureDetector::SimpleGestureListener::OnDoubleTapEvent(
    112     const MotionEvent& e) {
    113   return false;
    114 }
    115 
    116 class GestureDetector::TimeoutGestureHandler {
    117  public:
    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 
    133   ~TimeoutGestureHandler() {
    134     Stop();
    135   }
    136 
    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 
    144   void StopTimeout(TimeoutEvent event) { timeout_timers_[event].Stop(); }
    145 
    146   void Stop() {
    147     for (size_t i = SHOW_PRESS; i < TIMEOUT_EVENT_COUNT; ++i)
    148       timeout_timers_[i].Stop();
    149   }
    150 
    151   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 
    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 
    196 GestureDetector::~GestureDetector() {}
    197 
    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 
    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 
    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 
    476 void GestureDetector::OnShowPressTimeout() {
    477   listener_->OnShowPress(*current_down_event_);
    478 }
    479 
    480 void GestureDetector::OnLongPressTimeout() {
    481   timeout_handler_->StopTimeout(TAP);
    482   defer_confirm_single_tap_ = false;
    483   listener_->OnLongPress(*current_down_event_);
    484 }
    485 
    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 
    495 void GestureDetector::Cancel() {
    496   CancelTaps();
    497   velocity_tracker_.Clear();
    498   still_down_ = false;
    499 }
    500 
    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 
    509 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 
    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