gesture_provider.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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 33gfx::RectF GetBoundingBox(const MotionEvent& event) { 34 gfx::RectF bounds; 35 for (size_t i = 0; i < event.GetPointerCount(); ++i) { 36 float diameter = event.GetTouchMajor(i); 37 bounds.Union(gfx::RectF(event.GetX(i) - diameter / 2, 38 event.GetY(i) - diameter / 2, 39 diameter, 40 diameter)); 41 } 42 return bounds; 43} 44 45GestureEventData CreateGesture(EventType type, 46 int motion_event_id, 47 base::TimeTicks time, 48 float x, 49 float y, 50 size_t touch_point_count, 51 const gfx::RectF& bounding_box, 52 const GestureEventDetails& details) { 53 return GestureEventData(type, 54 motion_event_id, 55 time, 56 x, 57 y, 58 static_cast<int>(touch_point_count), 59 bounding_box, 60 details); 61} 62 63GestureEventData CreateGesture(EventType type, 64 int motion_event_id, 65 base::TimeTicks time, 66 float x, 67 float y, 68 size_t touch_point_count, 69 const gfx::RectF& bounding_box) { 70 return GestureEventData(type, 71 motion_event_id, 72 time, 73 x, 74 y, 75 static_cast<int>(touch_point_count), 76 bounding_box); 77} 78 79GestureEventData CreateGesture(EventType type, 80 const MotionEvent& event, 81 const GestureEventDetails& details) { 82 return CreateGesture(type, 83 event.GetId(), 84 event.GetEventTime(), 85 event.GetX(), 86 event.GetY(), 87 event.GetPointerCount(), 88 GetBoundingBox(event), 89 details); 90} 91 92GestureEventData CreateGesture(EventType type, 93 const MotionEvent& event) { 94 return CreateGesture(type, 95 event.GetId(), 96 event.GetEventTime(), 97 event.GetX(), 98 event.GetY(), 99 event.GetPointerCount(), 100 GetBoundingBox(event)); 101} 102 103GestureEventDetails CreateTapGestureDetails(EventType type, 104 const MotionEvent& event) { 105 // Set the tap count to 1 even for ET_GESTURE_DOUBLE_TAP, in order to be 106 // consistent with double tap behavior on a mobile viewport. See 107 // crbug.com/234986 for context. 108 GestureEventDetails tap_details(type, 1, 0); 109 return tap_details; 110} 111 112} // namespace 113 114// GestureProvider:::Config 115 116GestureProvider::Config::Config() 117 : display(gfx::Display::kInvalidDisplayID, gfx::Rect(1, 1)), 118 disable_click_delay(false), 119 gesture_begin_end_types_enabled(false) {} 120 121GestureProvider::Config::~Config() {} 122 123// GestureProvider::ScaleGestureListener 124 125class GestureProvider::ScaleGestureListenerImpl 126 : public ScaleGestureDetector::ScaleGestureListener { 127 public: 128 ScaleGestureListenerImpl(const ScaleGestureDetector::Config& config, 129 GestureProvider* provider) 130 : scale_gesture_detector_(config, this), 131 provider_(provider), 132 ignore_multitouch_events_(false), 133 pinch_event_sent_(false), 134 min_pinch_update_span_delta_(config.min_pinch_update_span_delta) {} 135 136 bool OnTouchEvent(const MotionEvent& event) { 137 // TODO: Need to deal with multi-touch transition. 138 const bool in_scale_gesture = IsScaleGestureDetectionInProgress(); 139 bool handled = scale_gesture_detector_.OnTouchEvent(event); 140 if (!in_scale_gesture && 141 (event.GetAction() == MotionEvent::ACTION_UP || 142 event.GetAction() == MotionEvent::ACTION_CANCEL)) { 143 return false; 144 } 145 return handled; 146 } 147 148 // ScaleGestureDetector::ScaleGestureListener implementation. 149 virtual bool OnScaleBegin(const ScaleGestureDetector& detector, 150 const MotionEvent& e) OVERRIDE { 151 if (ignore_multitouch_events_ && !detector.InDoubleTapMode()) 152 return false; 153 pinch_event_sent_ = false; 154 return true; 155 } 156 157 virtual void OnScaleEnd(const ScaleGestureDetector& detector, 158 const MotionEvent& e) OVERRIDE { 159 if (!pinch_event_sent_) 160 return; 161 provider_->Send(CreateGesture(ET_GESTURE_PINCH_END, 162 e.GetId(), 163 detector.GetEventTime(), 164 0, 165 0, 166 e.GetPointerCount(), 167 GetBoundingBox(e))); 168 pinch_event_sent_ = false; 169 } 170 171 virtual bool OnScale(const ScaleGestureDetector& detector, 172 const MotionEvent& e) OVERRIDE { 173 if (ignore_multitouch_events_ && !detector.InDoubleTapMode()) 174 return false; 175 if (!pinch_event_sent_) { 176 pinch_event_sent_ = true; 177 provider_->Send(CreateGesture(ET_GESTURE_PINCH_BEGIN, 178 e.GetId(), 179 detector.GetEventTime(), 180 detector.GetFocusX(), 181 detector.GetFocusY(), 182 e.GetPointerCount(), 183 GetBoundingBox(e))); 184 } 185 186 if (std::abs(detector.GetCurrentSpan() - detector.GetPreviousSpan()) < 187 min_pinch_update_span_delta_) { 188 return false; 189 } 190 191 float scale = detector.GetScaleFactor(); 192 if (scale == 1) 193 return true; 194 195 if (detector.InDoubleTapMode()) { 196 // Relative changes in the double-tap scale factor computed by |detector| 197 // diminish as the touch moves away from the original double-tap focus. 198 // For historical reasons, Chrome has instead adopted a scale factor 199 // computation that is invariant to the focal distance, where 200 // the scale delta remains constant if the touch velocity is constant. 201 float dy = 202 (detector.GetCurrentSpanY() - detector.GetPreviousSpanY()) * 0.5f; 203 scale = std::pow(scale > 1 ? 1.0f + kDoubleTapDragZoomSpeed 204 : 1.0f - kDoubleTapDragZoomSpeed, 205 std::abs(dy)); 206 } 207 GestureEventDetails pinch_details(ET_GESTURE_PINCH_UPDATE, scale, 0); 208 provider_->Send(CreateGesture(ET_GESTURE_PINCH_UPDATE, 209 e.GetId(), 210 detector.GetEventTime(), 211 detector.GetFocusX(), 212 detector.GetFocusY(), 213 e.GetPointerCount(), 214 GetBoundingBox(e), 215 pinch_details)); 216 return true; 217 } 218 219 void SetDoubleTapEnabled(bool enabled) { 220 DCHECK(!IsDoubleTapInProgress()); 221 scale_gesture_detector_.SetQuickScaleEnabled(enabled); 222 } 223 224 void SetMultiTouchEnabled(bool enabled) { 225 // Note that returning false from OnScaleBegin / OnScale makes the 226 // gesture detector not to emit further scaling notifications 227 // related to this gesture. Thus, if detector events are enabled in 228 // the middle of the gesture, we don't need to do anything. 229 ignore_multitouch_events_ = !enabled; 230 } 231 232 bool IsDoubleTapInProgress() const { 233 return IsScaleGestureDetectionInProgress() && InDoubleTapMode(); 234 } 235 236 bool IsScaleGestureDetectionInProgress() const { 237 return scale_gesture_detector_.IsInProgress(); 238 } 239 240 private: 241 bool InDoubleTapMode() const { 242 return scale_gesture_detector_.InDoubleTapMode(); 243 } 244 245 ScaleGestureDetector scale_gesture_detector_; 246 247 GestureProvider* const provider_; 248 249 // Completely silence multi-touch (pinch) scaling events. Used in WebView when 250 // zoom support is turned off. 251 bool ignore_multitouch_events_; 252 253 // Whether any pinch zoom event has been sent to native. 254 bool pinch_event_sent_; 255 256 // The minimum change in span required before this is considered a pinch. See 257 // crbug.com/373318. 258 float min_pinch_update_span_delta_; 259 260 DISALLOW_COPY_AND_ASSIGN(ScaleGestureListenerImpl); 261}; 262 263// GestureProvider::GestureListener 264 265class GestureProvider::GestureListenerImpl 266 : public GestureDetector::GestureListener, 267 public GestureDetector::DoubleTapListener { 268 public: 269 GestureListenerImpl( 270 const gfx::Display& display, 271 const GestureDetector::Config& gesture_detector_config, 272 bool disable_click_delay, 273 GestureProvider* provider) 274 : gesture_detector_(gesture_detector_config, this, this), 275 snap_scroll_controller_(display), 276 provider_(provider), 277 disable_click_delay_(disable_click_delay), 278 touch_slop_(gesture_detector_config.touch_slop), 279 double_tap_timeout_(gesture_detector_config.double_tap_timeout), 280 ignore_single_tap_(false), 281 seen_first_scroll_event_(false) {} 282 283 virtual ~GestureListenerImpl() {} 284 285 bool OnTouchEvent(const MotionEvent& e, 286 bool is_scale_gesture_detection_in_progress) { 287 snap_scroll_controller_.SetSnapScrollingMode( 288 e, is_scale_gesture_detection_in_progress); 289 290 if (is_scale_gesture_detection_in_progress) 291 SetIgnoreSingleTap(true); 292 293 if (e.GetAction() == MotionEvent::ACTION_DOWN) 294 gesture_detector_.set_longpress_enabled(true); 295 296 return gesture_detector_.OnTouchEvent(e); 297 } 298 299 // GestureDetector::GestureListener implementation. 300 virtual bool OnDown(const MotionEvent& e) OVERRIDE { 301 current_down_time_ = e.GetEventTime(); 302 ignore_single_tap_ = false; 303 seen_first_scroll_event_ = false; 304 305 GestureEventDetails tap_details(ET_GESTURE_TAP_DOWN, 0, 0); 306 provider_->Send(CreateGesture(ET_GESTURE_TAP_DOWN, e, tap_details)); 307 308 // Return true to indicate that we want to handle touch. 309 return true; 310 } 311 312 virtual bool OnScroll(const MotionEvent& e1, 313 const MotionEvent& e2, 314 float raw_distance_x, 315 float raw_distance_y) OVERRIDE { 316 float distance_x = raw_distance_x; 317 float distance_y = raw_distance_y; 318 if (!seen_first_scroll_event_) { 319 // Remove the touch slop region from the first scroll event to avoid a 320 // jump. 321 seen_first_scroll_event_ = true; 322 double distance = 323 std::sqrt(distance_x * distance_x + distance_y * distance_y); 324 double epsilon = 1e-3; 325 if (distance > epsilon) { 326 double ratio = std::max(0., distance - touch_slop_) / distance; 327 distance_x *= ratio; 328 distance_y *= ratio; 329 } 330 } 331 snap_scroll_controller_.UpdateSnapScrollMode(distance_x, distance_y); 332 if (snap_scroll_controller_.IsSnappingScrolls()) { 333 if (snap_scroll_controller_.IsSnapHorizontal()) { 334 distance_y = 0; 335 } else { 336 distance_x = 0; 337 } 338 } 339 340 if (!provider_->IsScrollInProgress()) { 341 // Note that scroll start hints are in distance traveled, where 342 // scroll deltas are in the opposite direction. 343 GestureEventDetails scroll_details( 344 ET_GESTURE_SCROLL_BEGIN, -raw_distance_x, -raw_distance_y); 345 346 // Use the co-ordinates from the touch down, as these co-ordinates are 347 // used to determine which layer the scroll should affect. 348 provider_->Send(CreateGesture(ET_GESTURE_SCROLL_BEGIN, 349 e2.GetId(), 350 e2.GetEventTime(), 351 e1.GetX(), 352 e1.GetY(), 353 e2.GetPointerCount(), 354 GetBoundingBox(e2), 355 scroll_details)); 356 } 357 358 if (distance_x || distance_y) { 359 GestureEventDetails scroll_details( 360 ET_GESTURE_SCROLL_UPDATE, -distance_x, -distance_y); 361 provider_->Send( 362 CreateGesture(ET_GESTURE_SCROLL_UPDATE, e2, scroll_details)); 363 } 364 365 return true; 366 } 367 368 virtual bool OnFling(const MotionEvent& e1, 369 const MotionEvent& e2, 370 float velocity_x, 371 float velocity_y) OVERRIDE { 372 if (snap_scroll_controller_.IsSnappingScrolls()) { 373 if (snap_scroll_controller_.IsSnapHorizontal()) { 374 velocity_y = 0; 375 } else { 376 velocity_x = 0; 377 } 378 } 379 380 provider_->Fling(e2, velocity_x, velocity_y); 381 return true; 382 } 383 384 virtual bool OnSwipe(const MotionEvent& e1, 385 const MotionEvent& e2, 386 float velocity_x, 387 float velocity_y) OVERRIDE { 388 GestureEventDetails swipe_details(ET_GESTURE_SWIPE, velocity_x, velocity_y); 389 provider_->Send(CreateGesture(ET_GESTURE_SWIPE, e2, swipe_details)); 390 return true; 391 } 392 393 virtual bool OnTwoFingerTap(const MotionEvent& e1, 394 const MotionEvent& e2) OVERRIDE { 395 // The location of the two finger tap event should be the location of the 396 // primary pointer. 397 GestureEventDetails two_finger_tap_details(ET_GESTURE_TWO_FINGER_TAP, 398 e1.GetTouchMajor(), 399 e1.GetTouchMajor()); 400 provider_->Send(CreateGesture(ET_GESTURE_TWO_FINGER_TAP, 401 e2.GetId(), 402 e2.GetEventTime(), 403 e1.GetX(), 404 e1.GetY(), 405 e2.GetPointerCount(), 406 GetBoundingBox(e2), 407 two_finger_tap_details)); 408 return true; 409 } 410 411 virtual void OnShowPress(const MotionEvent& e) OVERRIDE { 412 GestureEventDetails show_press_details(ET_GESTURE_SHOW_PRESS, 0, 0); 413 provider_->Send( 414 CreateGesture(ET_GESTURE_SHOW_PRESS, e, show_press_details)); 415 } 416 417 virtual bool OnSingleTapUp(const MotionEvent& e) OVERRIDE { 418 // This is a hack to address the issue where user hovers 419 // over a link for longer than double_tap_timeout_, then 420 // OnSingleTapConfirmed() is not triggered. But we still 421 // want to trigger the tap event at UP. So we override 422 // OnSingleTapUp() in this case. This assumes singleTapUp 423 // gets always called before singleTapConfirmed. 424 if (!ignore_single_tap_) { 425 if (e.GetEventTime() - current_down_time_ > double_tap_timeout_) { 426 return OnSingleTapConfirmed(e); 427 } else if (!IsDoubleTapEnabled() || disable_click_delay_) { 428 // If double-tap has been disabled, there is no need to wait 429 // for the double-tap timeout. 430 return OnSingleTapConfirmed(e); 431 } else { 432 // Notify Blink about this tapUp event anyway, when none of the above 433 // conditions applied. 434 provider_->Send(CreateGesture( 435 ET_GESTURE_TAP_UNCONFIRMED, 436 e, 437 CreateTapGestureDetails(ET_GESTURE_TAP_UNCONFIRMED, e))); 438 } 439 } 440 441 return provider_->SendLongTapIfNecessary(e); 442 } 443 444 // GestureDetector::DoubleTapListener implementation. 445 virtual bool OnSingleTapConfirmed(const MotionEvent& e) OVERRIDE { 446 // Long taps in the edges of the screen have their events delayed by 447 // ContentViewHolder for tab swipe operations. As a consequence of the delay 448 // this method might be called after receiving the up event. 449 // These corner cases should be ignored. 450 if (ignore_single_tap_) 451 return true; 452 453 ignore_single_tap_ = true; 454 455 provider_->Send(CreateGesture( 456 ET_GESTURE_TAP, e, CreateTapGestureDetails(ET_GESTURE_TAP, e))); 457 return true; 458 } 459 460 virtual bool OnDoubleTap(const MotionEvent& e) OVERRIDE { return false; } 461 462 virtual bool OnDoubleTapEvent(const MotionEvent& e) OVERRIDE { 463 switch (e.GetAction()) { 464 case MotionEvent::ACTION_DOWN: 465 gesture_detector_.set_longpress_enabled(false); 466 break; 467 468 case MotionEvent::ACTION_UP: 469 if (!provider_->IsPinchInProgress() && 470 !provider_->IsScrollInProgress()) { 471 provider_->Send( 472 CreateGesture(ET_GESTURE_DOUBLE_TAP, 473 e, 474 CreateTapGestureDetails(ET_GESTURE_DOUBLE_TAP, e))); 475 return true; 476 } 477 break; 478 default: 479 break; 480 } 481 return false; 482 } 483 484 virtual bool OnLongPress(const MotionEvent& e) OVERRIDE { 485 DCHECK(!IsDoubleTapInProgress()); 486 SetIgnoreSingleTap(true); 487 488 GestureEventDetails long_press_details(ET_GESTURE_LONG_PRESS, 0, 0); 489 provider_->Send( 490 CreateGesture(ET_GESTURE_LONG_PRESS, e, long_press_details)); 491 492 // Returning true puts the GestureDetector in "longpress" mode, disabling 493 // further scrolling. This is undesirable, as it is quite common for a 494 // longpress gesture to fire on content that won't trigger a context menu. 495 return false; 496 } 497 498 void SetDoubleTapEnabled(bool enabled) { 499 DCHECK(!IsDoubleTapInProgress()); 500 gesture_detector_.SetDoubleTapListener(enabled ? this : NULL); 501 } 502 503 bool IsDoubleTapInProgress() const { 504 return gesture_detector_.is_double_tapping(); 505 } 506 507 private: 508 void SetIgnoreSingleTap(bool value) { ignore_single_tap_ = value; } 509 510 bool IsDoubleTapEnabled() const { 511 return gesture_detector_.has_doubletap_listener(); 512 } 513 514 GestureDetector gesture_detector_; 515 SnapScrollController snap_scroll_controller_; 516 517 GestureProvider* const provider_; 518 519 // Whether the click delay should always be disabled by sending clicks for 520 // double-tap gestures. 521 const bool disable_click_delay_; 522 523 const float touch_slop_; 524 525 const base::TimeDelta double_tap_timeout_; 526 527 base::TimeTicks current_down_time_; 528 529 // TODO(klobag): This is to avoid a bug in GestureDetector. With multi-touch, 530 // always_in_tap_region_ is not reset. So when the last finger is up, 531 // OnSingleTapUp() will be mistakenly fired. 532 bool ignore_single_tap_; 533 534 // Used to remove the touch slop from the initial scroll event in a scroll 535 // gesture. 536 bool seen_first_scroll_event_; 537 538 DISALLOW_COPY_AND_ASSIGN(GestureListenerImpl); 539}; 540 541// GestureProvider 542 543GestureProvider::GestureProvider(const Config& config, 544 GestureProviderClient* client) 545 : client_(client), 546 touch_scroll_in_progress_(false), 547 pinch_in_progress_(false), 548 double_tap_support_for_page_(true), 549 double_tap_support_for_platform_(true), 550 gesture_begin_end_types_enabled_(config.gesture_begin_end_types_enabled) { 551 DCHECK(client); 552 InitGestureDetectors(config); 553} 554 555GestureProvider::~GestureProvider() {} 556 557bool GestureProvider::OnTouchEvent(const MotionEvent& event) { 558 TRACE_EVENT1("input", "GestureProvider::OnTouchEvent", 559 "action", GetMotionEventActionName(event.GetAction())); 560 561 DCHECK_NE(0u, event.GetPointerCount()); 562 563 if (!CanHandle(event)) 564 return false; 565 566 const bool in_scale_gesture = 567 scale_gesture_listener_->IsScaleGestureDetectionInProgress(); 568 569 OnTouchEventHandlingBegin(event); 570 gesture_listener_->OnTouchEvent(event, in_scale_gesture); 571 scale_gesture_listener_->OnTouchEvent(event); 572 OnTouchEventHandlingEnd(event); 573 return true; 574} 575 576void GestureProvider::SetMultiTouchZoomSupportEnabled(bool enabled) { 577 scale_gesture_listener_->SetMultiTouchEnabled(enabled); 578} 579 580void GestureProvider::SetDoubleTapSupportForPlatformEnabled(bool enabled) { 581 if (double_tap_support_for_platform_ == enabled) 582 return; 583 double_tap_support_for_platform_ = enabled; 584 UpdateDoubleTapDetectionSupport(); 585} 586 587void GestureProvider::SetDoubleTapSupportForPageEnabled(bool enabled) { 588 if (double_tap_support_for_page_ == enabled) 589 return; 590 double_tap_support_for_page_ = enabled; 591 UpdateDoubleTapDetectionSupport(); 592} 593 594bool GestureProvider::IsScrollInProgress() const { 595 // TODO(wangxianzhu): Also return true when fling is active once the UI knows 596 // exactly when the fling ends. 597 return touch_scroll_in_progress_; 598} 599 600bool GestureProvider::IsPinchInProgress() const { return pinch_in_progress_; } 601 602bool GestureProvider::IsDoubleTapInProgress() const { 603 return gesture_listener_->IsDoubleTapInProgress() || 604 scale_gesture_listener_->IsDoubleTapInProgress(); 605} 606 607void GestureProvider::InitGestureDetectors(const Config& config) { 608 TRACE_EVENT0("input", "GestureProvider::InitGestureDetectors"); 609 gesture_listener_.reset( 610 new GestureListenerImpl(config.display, 611 config.gesture_detector_config, 612 config.disable_click_delay, 613 this)); 614 615 scale_gesture_listener_.reset( 616 new ScaleGestureListenerImpl(config.scale_gesture_detector_config, this)); 617 618 UpdateDoubleTapDetectionSupport(); 619} 620 621bool GestureProvider::CanHandle(const MotionEvent& event) const { 622 return event.GetAction() == MotionEvent::ACTION_DOWN || current_down_event_; 623} 624 625void GestureProvider::Fling(const MotionEvent& event, 626 float velocity_x, 627 float velocity_y) { 628 if (!velocity_x && !velocity_y) { 629 EndTouchScrollIfNecessary(event, true); 630 return; 631 } 632 633 if (!touch_scroll_in_progress_) { 634 // The native side needs a ET_GESTURE_SCROLL_BEGIN before 635 // ET_SCROLL_FLING_START to send the fling to the correct target. Send if it 636 // has not sent. The distance traveled in one second is a reasonable scroll 637 // start hint. 638 GestureEventDetails scroll_details( 639 ET_GESTURE_SCROLL_BEGIN, velocity_x, velocity_y); 640 Send(CreateGesture(ET_GESTURE_SCROLL_BEGIN, event, scroll_details)); 641 } 642 EndTouchScrollIfNecessary(event, false); 643 644 GestureEventDetails fling_details( 645 ET_SCROLL_FLING_START, velocity_x, velocity_y); 646 Send(CreateGesture( 647 ET_SCROLL_FLING_START, event, fling_details)); 648} 649 650void GestureProvider::Send(const GestureEventData& gesture) { 651 DCHECK(!gesture.time.is_null()); 652 // The only valid events that should be sent without an active touch sequence 653 // are SHOW_PRESS and TAP, potentially triggered by the double-tap 654 // delay timing out. 655 DCHECK(current_down_event_ || gesture.type == ET_GESTURE_TAP || 656 gesture.type == ET_GESTURE_SHOW_PRESS); 657 658 switch (gesture.type) { 659 case ET_GESTURE_LONG_PRESS: 660 DCHECK(!scale_gesture_listener_->IsScaleGestureDetectionInProgress()); 661 current_longpress_time_ = gesture.time; 662 break; 663 case ET_GESTURE_LONG_TAP: 664 current_longpress_time_ = base::TimeTicks(); 665 break; 666 case ET_GESTURE_SCROLL_BEGIN: 667 DCHECK(!touch_scroll_in_progress_); 668 touch_scroll_in_progress_ = true; 669 break; 670 case ET_GESTURE_SCROLL_END: 671 DCHECK(touch_scroll_in_progress_); 672 if (pinch_in_progress_) 673 Send(CreateGesture(ET_GESTURE_PINCH_END, 674 gesture.motion_event_id, 675 gesture.time, 676 gesture.x, 677 gesture.y, 678 gesture.details.touch_points(), 679 gesture.details.bounding_box())); 680 touch_scroll_in_progress_ = false; 681 break; 682 case ET_GESTURE_PINCH_BEGIN: 683 DCHECK(!pinch_in_progress_); 684 if (!touch_scroll_in_progress_) 685 Send(CreateGesture(ET_GESTURE_SCROLL_BEGIN, 686 gesture.motion_event_id, 687 gesture.time, 688 gesture.x, 689 gesture.y, 690 gesture.details.touch_points(), 691 gesture.details.bounding_box())); 692 pinch_in_progress_ = true; 693 break; 694 case ET_GESTURE_PINCH_END: 695 DCHECK(pinch_in_progress_); 696 pinch_in_progress_ = false; 697 break; 698 case ET_GESTURE_SHOW_PRESS: 699 // It's possible that a double-tap drag zoom (from ScaleGestureDetector) 700 // will start before the press gesture fires (from GestureDetector), in 701 // which case the press should simply be dropped. 702 if (pinch_in_progress_ || touch_scroll_in_progress_) 703 return; 704 default: 705 break; 706 }; 707 708 client_->OnGestureEvent(gesture); 709} 710 711bool GestureProvider::SendLongTapIfNecessary(const MotionEvent& event) { 712 if (event.GetAction() == MotionEvent::ACTION_UP && 713 !current_longpress_time_.is_null() && 714 !scale_gesture_listener_->IsScaleGestureDetectionInProgress()) { 715 GestureEventDetails long_tap_details(ET_GESTURE_LONG_TAP, 0, 0); 716 Send(CreateGesture(ET_GESTURE_LONG_TAP, event, long_tap_details)); 717 return true; 718 } 719 return false; 720} 721 722void GestureProvider::EndTouchScrollIfNecessary(const MotionEvent& event, 723 bool send_scroll_end_event) { 724 if (!touch_scroll_in_progress_) 725 return; 726 if (send_scroll_end_event) 727 Send(CreateGesture(ET_GESTURE_SCROLL_END, event)); 728 touch_scroll_in_progress_ = false; 729} 730 731void GestureProvider::OnTouchEventHandlingBegin(const MotionEvent& event) { 732 switch (event.GetAction()) { 733 case MotionEvent::ACTION_DOWN: 734 current_down_event_ = event.Clone(); 735 touch_scroll_in_progress_ = false; 736 pinch_in_progress_ = false; 737 current_longpress_time_ = base::TimeTicks(); 738 if (gesture_begin_end_types_enabled_) 739 Send(CreateGesture(ET_GESTURE_BEGIN, event)); 740 break; 741 case MotionEvent::ACTION_POINTER_DOWN: 742 if (gesture_begin_end_types_enabled_) 743 Send(CreateGesture(ET_GESTURE_BEGIN, event)); 744 break; 745 case MotionEvent::ACTION_POINTER_UP: 746 case MotionEvent::ACTION_UP: 747 case MotionEvent::ACTION_CANCEL: 748 case MotionEvent::ACTION_MOVE: 749 break; 750 } 751} 752 753void GestureProvider::OnTouchEventHandlingEnd(const MotionEvent& event) { 754 switch (event.GetAction()) { 755 case MotionEvent::ACTION_UP: 756 case MotionEvent::ACTION_CANCEL: 757 // Note: This call will have no effect if a fling was just generated, as 758 // |Fling()| will have already signalled an end to touch-scrolling. 759 EndTouchScrollIfNecessary(event, true); 760 761 if (gesture_begin_end_types_enabled_) 762 Send(CreateGesture(ET_GESTURE_END, event)); 763 764 current_down_event_.reset(); 765 766 UpdateDoubleTapDetectionSupport(); 767 break; 768 case MotionEvent::ACTION_POINTER_UP: 769 if (gesture_begin_end_types_enabled_) 770 Send(CreateGesture(ET_GESTURE_END, event)); 771 break; 772 case MotionEvent::ACTION_DOWN: 773 case MotionEvent::ACTION_POINTER_DOWN: 774 case MotionEvent::ACTION_MOVE: 775 break; 776 } 777} 778 779void GestureProvider::UpdateDoubleTapDetectionSupport() { 780 // The GestureDetector requires that any provided DoubleTapListener remain 781 // attached to it for the duration of a touch sequence. Defer any potential 782 // null'ing of the listener until the sequence has ended. 783 if (current_down_event_) 784 return; 785 786 const bool double_tap_enabled = double_tap_support_for_page_ && 787 double_tap_support_for_platform_; 788 gesture_listener_->SetDoubleTapEnabled(double_tap_enabled); 789 scale_gesture_listener_->SetDoubleTapEnabled(double_tap_enabled); 790} 791 792} // namespace ui 793