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