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