gesture_provider.cc revision 46d4c2bc3267f3f028f39e7e311b0f89aba2e4fd
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 const gfx::RectF bounding_box = GetBoundingBox(e2); 360 GestureEventDetails scroll_details( 361 ET_GESTURE_SCROLL_UPDATE, -distance_x, -distance_y); 362 provider_->Send(CreateGesture(ET_GESTURE_SCROLL_UPDATE, 363 e2.GetId(), 364 e2.GetEventTime(), 365 bounding_box.CenterPoint().x(), 366 bounding_box.CenterPoint().y(), 367 e2.GetPointerCount(), 368 bounding_box, 369 scroll_details)); 370 } 371 372 return true; 373 } 374 375 virtual bool OnFling(const MotionEvent& e1, 376 const MotionEvent& e2, 377 float velocity_x, 378 float velocity_y) OVERRIDE { 379 if (snap_scroll_controller_.IsSnappingScrolls()) { 380 if (snap_scroll_controller_.IsSnapHorizontal()) { 381 velocity_y = 0; 382 } else { 383 velocity_x = 0; 384 } 385 } 386 387 provider_->Fling(e2, velocity_x, velocity_y); 388 return true; 389 } 390 391 virtual bool OnSwipe(const MotionEvent& e1, 392 const MotionEvent& e2, 393 float velocity_x, 394 float velocity_y) OVERRIDE { 395 GestureEventDetails swipe_details(ET_GESTURE_SWIPE, velocity_x, velocity_y); 396 provider_->Send(CreateGesture(ET_GESTURE_SWIPE, e2, swipe_details)); 397 return true; 398 } 399 400 virtual bool OnTwoFingerTap(const MotionEvent& e1, 401 const MotionEvent& e2) OVERRIDE { 402 // The location of the two finger tap event should be the location of the 403 // primary pointer. 404 GestureEventDetails two_finger_tap_details(ET_GESTURE_TWO_FINGER_TAP, 405 e1.GetTouchMajor(), 406 e1.GetTouchMajor()); 407 provider_->Send(CreateGesture(ET_GESTURE_TWO_FINGER_TAP, 408 e2.GetId(), 409 e2.GetEventTime(), 410 e1.GetX(), 411 e1.GetY(), 412 e2.GetPointerCount(), 413 GetBoundingBox(e2), 414 two_finger_tap_details)); 415 return true; 416 } 417 418 virtual void OnShowPress(const MotionEvent& e) OVERRIDE { 419 GestureEventDetails show_press_details(ET_GESTURE_SHOW_PRESS, 0, 0); 420 provider_->Send( 421 CreateGesture(ET_GESTURE_SHOW_PRESS, e, show_press_details)); 422 } 423 424 virtual bool OnSingleTapUp(const MotionEvent& e) OVERRIDE { 425 // This is a hack to address the issue where user hovers 426 // over a link for longer than double_tap_timeout_, then 427 // OnSingleTapConfirmed() is not triggered. But we still 428 // want to trigger the tap event at UP. So we override 429 // OnSingleTapUp() in this case. This assumes singleTapUp 430 // gets always called before singleTapConfirmed. 431 if (!ignore_single_tap_) { 432 if (e.GetEventTime() - current_down_time_ > double_tap_timeout_) { 433 return OnSingleTapConfirmed(e); 434 } else if (!IsDoubleTapEnabled() || disable_click_delay_) { 435 // If double-tap has been disabled, there is no need to wait 436 // for the double-tap timeout. 437 return OnSingleTapConfirmed(e); 438 } else { 439 // Notify Blink about this tapUp event anyway, when none of the above 440 // conditions applied. 441 provider_->Send(CreateGesture( 442 ET_GESTURE_TAP_UNCONFIRMED, 443 e, 444 CreateTapGestureDetails(ET_GESTURE_TAP_UNCONFIRMED, e))); 445 } 446 } 447 448 return provider_->SendLongTapIfNecessary(e); 449 } 450 451 // GestureDetector::DoubleTapListener implementation. 452 virtual bool OnSingleTapConfirmed(const MotionEvent& e) OVERRIDE { 453 // Long taps in the edges of the screen have their events delayed by 454 // ContentViewHolder for tab swipe operations. As a consequence of the delay 455 // this method might be called after receiving the up event. 456 // These corner cases should be ignored. 457 if (ignore_single_tap_) 458 return true; 459 460 ignore_single_tap_ = true; 461 462 provider_->Send(CreateGesture( 463 ET_GESTURE_TAP, e, CreateTapGestureDetails(ET_GESTURE_TAP, e))); 464 return true; 465 } 466 467 virtual bool OnDoubleTap(const MotionEvent& e) OVERRIDE { return false; } 468 469 virtual bool OnDoubleTapEvent(const MotionEvent& e) OVERRIDE { 470 switch (e.GetAction()) { 471 case MotionEvent::ACTION_DOWN: 472 gesture_detector_.set_longpress_enabled(false); 473 break; 474 475 case MotionEvent::ACTION_UP: 476 if (!provider_->IsPinchInProgress() && 477 !provider_->IsScrollInProgress()) { 478 provider_->Send( 479 CreateGesture(ET_GESTURE_DOUBLE_TAP, 480 e, 481 CreateTapGestureDetails(ET_GESTURE_DOUBLE_TAP, e))); 482 return true; 483 } 484 break; 485 default: 486 break; 487 } 488 return false; 489 } 490 491 virtual bool OnLongPress(const MotionEvent& e) OVERRIDE { 492 DCHECK(!IsDoubleTapInProgress()); 493 SetIgnoreSingleTap(true); 494 495 GestureEventDetails long_press_details(ET_GESTURE_LONG_PRESS, 0, 0); 496 provider_->Send( 497 CreateGesture(ET_GESTURE_LONG_PRESS, e, long_press_details)); 498 499 // Returning true puts the GestureDetector in "longpress" mode, disabling 500 // further scrolling. This is undesirable, as it is quite common for a 501 // longpress gesture to fire on content that won't trigger a context menu. 502 return false; 503 } 504 505 void SetDoubleTapEnabled(bool enabled) { 506 DCHECK(!IsDoubleTapInProgress()); 507 gesture_detector_.SetDoubleTapListener(enabled ? this : NULL); 508 } 509 510 bool IsDoubleTapInProgress() const { 511 return gesture_detector_.is_double_tapping(); 512 } 513 514 private: 515 void SetIgnoreSingleTap(bool value) { ignore_single_tap_ = value; } 516 517 bool IsDoubleTapEnabled() const { 518 return gesture_detector_.has_doubletap_listener(); 519 } 520 521 GestureDetector gesture_detector_; 522 SnapScrollController snap_scroll_controller_; 523 524 GestureProvider* const provider_; 525 526 // Whether the click delay should always be disabled by sending clicks for 527 // double-tap gestures. 528 const bool disable_click_delay_; 529 530 const float touch_slop_; 531 532 const base::TimeDelta double_tap_timeout_; 533 534 base::TimeTicks current_down_time_; 535 536 // TODO(klobag): This is to avoid a bug in GestureDetector. With multi-touch, 537 // always_in_tap_region_ is not reset. So when the last finger is up, 538 // OnSingleTapUp() will be mistakenly fired. 539 bool ignore_single_tap_; 540 541 // Used to remove the touch slop from the initial scroll event in a scroll 542 // gesture. 543 bool seen_first_scroll_event_; 544 545 DISALLOW_COPY_AND_ASSIGN(GestureListenerImpl); 546}; 547 548// GestureProvider 549 550GestureProvider::GestureProvider(const Config& config, 551 GestureProviderClient* client) 552 : client_(client), 553 touch_scroll_in_progress_(false), 554 pinch_in_progress_(false), 555 double_tap_support_for_page_(true), 556 double_tap_support_for_platform_(true), 557 gesture_begin_end_types_enabled_(config.gesture_begin_end_types_enabled) { 558 DCHECK(client); 559 InitGestureDetectors(config); 560} 561 562GestureProvider::~GestureProvider() {} 563 564bool GestureProvider::OnTouchEvent(const MotionEvent& event) { 565 TRACE_EVENT1("input", "GestureProvider::OnTouchEvent", 566 "action", GetMotionEventActionName(event.GetAction())); 567 568 DCHECK_NE(0u, event.GetPointerCount()); 569 570 if (!CanHandle(event)) 571 return false; 572 573 const bool in_scale_gesture = 574 scale_gesture_listener_->IsScaleGestureDetectionInProgress(); 575 576 OnTouchEventHandlingBegin(event); 577 gesture_listener_->OnTouchEvent(event, in_scale_gesture); 578 scale_gesture_listener_->OnTouchEvent(event); 579 OnTouchEventHandlingEnd(event); 580 return true; 581} 582 583void GestureProvider::SetMultiTouchZoomSupportEnabled(bool enabled) { 584 scale_gesture_listener_->SetMultiTouchEnabled(enabled); 585} 586 587void GestureProvider::SetDoubleTapSupportForPlatformEnabled(bool enabled) { 588 if (double_tap_support_for_platform_ == enabled) 589 return; 590 double_tap_support_for_platform_ = enabled; 591 UpdateDoubleTapDetectionSupport(); 592} 593 594void GestureProvider::SetDoubleTapSupportForPageEnabled(bool enabled) { 595 if (double_tap_support_for_page_ == enabled) 596 return; 597 double_tap_support_for_page_ = enabled; 598 UpdateDoubleTapDetectionSupport(); 599} 600 601bool GestureProvider::IsScrollInProgress() const { 602 // TODO(wangxianzhu): Also return true when fling is active once the UI knows 603 // exactly when the fling ends. 604 return touch_scroll_in_progress_; 605} 606 607bool GestureProvider::IsPinchInProgress() const { return pinch_in_progress_; } 608 609bool GestureProvider::IsDoubleTapInProgress() const { 610 return gesture_listener_->IsDoubleTapInProgress() || 611 scale_gesture_listener_->IsDoubleTapInProgress(); 612} 613 614void GestureProvider::InitGestureDetectors(const Config& config) { 615 TRACE_EVENT0("input", "GestureProvider::InitGestureDetectors"); 616 gesture_listener_.reset( 617 new GestureListenerImpl(config.display, 618 config.gesture_detector_config, 619 config.disable_click_delay, 620 this)); 621 622 scale_gesture_listener_.reset( 623 new ScaleGestureListenerImpl(config.scale_gesture_detector_config, this)); 624 625 UpdateDoubleTapDetectionSupport(); 626} 627 628bool GestureProvider::CanHandle(const MotionEvent& event) const { 629 return event.GetAction() == MotionEvent::ACTION_DOWN || current_down_event_; 630} 631 632void GestureProvider::Fling(const MotionEvent& event, 633 float velocity_x, 634 float velocity_y) { 635 if (!velocity_x && !velocity_y) { 636 EndTouchScrollIfNecessary(event, true); 637 return; 638 } 639 640 if (!touch_scroll_in_progress_) { 641 // The native side needs a ET_GESTURE_SCROLL_BEGIN before 642 // ET_SCROLL_FLING_START to send the fling to the correct target. Send if it 643 // has not sent. The distance traveled in one second is a reasonable scroll 644 // start hint. 645 GestureEventDetails scroll_details( 646 ET_GESTURE_SCROLL_BEGIN, velocity_x, velocity_y); 647 Send(CreateGesture(ET_GESTURE_SCROLL_BEGIN, event, scroll_details)); 648 } 649 EndTouchScrollIfNecessary(event, false); 650 651 GestureEventDetails fling_details( 652 ET_SCROLL_FLING_START, velocity_x, velocity_y); 653 Send(CreateGesture( 654 ET_SCROLL_FLING_START, event, fling_details)); 655} 656 657void GestureProvider::Send(const GestureEventData& gesture) { 658 DCHECK(!gesture.time.is_null()); 659 // The only valid events that should be sent without an active touch sequence 660 // are SHOW_PRESS and TAP, potentially triggered by the double-tap 661 // delay timing out. 662 DCHECK(current_down_event_ || gesture.type == ET_GESTURE_TAP || 663 gesture.type == ET_GESTURE_SHOW_PRESS); 664 665 switch (gesture.type) { 666 case ET_GESTURE_LONG_PRESS: 667 DCHECK(!scale_gesture_listener_->IsScaleGestureDetectionInProgress()); 668 current_longpress_time_ = gesture.time; 669 break; 670 case ET_GESTURE_LONG_TAP: 671 current_longpress_time_ = base::TimeTicks(); 672 break; 673 case ET_GESTURE_SCROLL_BEGIN: 674 DCHECK(!touch_scroll_in_progress_); 675 touch_scroll_in_progress_ = true; 676 break; 677 case ET_GESTURE_SCROLL_END: 678 DCHECK(touch_scroll_in_progress_); 679 if (pinch_in_progress_) 680 Send(CreateGesture(ET_GESTURE_PINCH_END, 681 gesture.motion_event_id, 682 gesture.time, 683 gesture.x, 684 gesture.y, 685 gesture.details.touch_points(), 686 gesture.details.bounding_box())); 687 touch_scroll_in_progress_ = false; 688 break; 689 case ET_GESTURE_PINCH_BEGIN: 690 DCHECK(!pinch_in_progress_); 691 if (!touch_scroll_in_progress_) 692 Send(CreateGesture(ET_GESTURE_SCROLL_BEGIN, 693 gesture.motion_event_id, 694 gesture.time, 695 gesture.x, 696 gesture.y, 697 gesture.details.touch_points(), 698 gesture.details.bounding_box())); 699 pinch_in_progress_ = true; 700 break; 701 case ET_GESTURE_PINCH_END: 702 DCHECK(pinch_in_progress_); 703 pinch_in_progress_ = false; 704 break; 705 case ET_GESTURE_SHOW_PRESS: 706 // It's possible that a double-tap drag zoom (from ScaleGestureDetector) 707 // will start before the press gesture fires (from GestureDetector), in 708 // which case the press should simply be dropped. 709 if (pinch_in_progress_ || touch_scroll_in_progress_) 710 return; 711 default: 712 break; 713 }; 714 715 client_->OnGestureEvent(gesture); 716} 717 718bool GestureProvider::SendLongTapIfNecessary(const MotionEvent& event) { 719 if (event.GetAction() == MotionEvent::ACTION_UP && 720 !current_longpress_time_.is_null() && 721 !scale_gesture_listener_->IsScaleGestureDetectionInProgress()) { 722 GestureEventDetails long_tap_details(ET_GESTURE_LONG_TAP, 0, 0); 723 Send(CreateGesture(ET_GESTURE_LONG_TAP, event, long_tap_details)); 724 return true; 725 } 726 return false; 727} 728 729void GestureProvider::EndTouchScrollIfNecessary(const MotionEvent& event, 730 bool send_scroll_end_event) { 731 if (!touch_scroll_in_progress_) 732 return; 733 if (send_scroll_end_event) 734 Send(CreateGesture(ET_GESTURE_SCROLL_END, event)); 735 touch_scroll_in_progress_ = false; 736} 737 738void GestureProvider::OnTouchEventHandlingBegin(const MotionEvent& event) { 739 switch (event.GetAction()) { 740 case MotionEvent::ACTION_DOWN: 741 current_down_event_ = event.Clone(); 742 touch_scroll_in_progress_ = false; 743 pinch_in_progress_ = false; 744 current_longpress_time_ = base::TimeTicks(); 745 if (gesture_begin_end_types_enabled_) 746 Send(CreateGesture(ET_GESTURE_BEGIN, event)); 747 break; 748 case MotionEvent::ACTION_POINTER_DOWN: 749 if (gesture_begin_end_types_enabled_) { 750 Send(CreateGesture(ET_GESTURE_BEGIN, 751 event.GetId(), 752 event.GetEventTime(), 753 event.GetX(event.GetActionIndex()), 754 event.GetY(event.GetActionIndex()), 755 event.GetPointerCount(), 756 GetBoundingBox(event))); 757 } 758 break; 759 case MotionEvent::ACTION_POINTER_UP: 760 case MotionEvent::ACTION_UP: 761 case MotionEvent::ACTION_CANCEL: 762 case MotionEvent::ACTION_MOVE: 763 break; 764 } 765} 766 767void GestureProvider::OnTouchEventHandlingEnd(const MotionEvent& event) { 768 switch (event.GetAction()) { 769 case MotionEvent::ACTION_UP: 770 case MotionEvent::ACTION_CANCEL: { 771 // Note: This call will have no effect if a fling was just generated, as 772 // |Fling()| will have already signalled an end to touch-scrolling. 773 EndTouchScrollIfNecessary(event, true); 774 775 const gfx::RectF bounding_box = GetBoundingBox(event); 776 777 if (gesture_begin_end_types_enabled_) { 778 for (size_t i = 0; i < event.GetPointerCount(); ++i) { 779 Send(CreateGesture(ET_GESTURE_END, 780 event.GetId(), 781 event.GetEventTime(), 782 event.GetX(i), 783 event.GetY(i), 784 event.GetPointerCount() - i, 785 bounding_box)); 786 } 787 } 788 789 current_down_event_.reset(); 790 791 UpdateDoubleTapDetectionSupport(); 792 break; 793 } 794 case MotionEvent::ACTION_POINTER_UP: 795 if (gesture_begin_end_types_enabled_) 796 Send(CreateGesture(ET_GESTURE_END, event)); 797 break; 798 case MotionEvent::ACTION_DOWN: 799 case MotionEvent::ACTION_POINTER_DOWN: 800 case MotionEvent::ACTION_MOVE: 801 break; 802 } 803} 804 805void GestureProvider::UpdateDoubleTapDetectionSupport() { 806 // The GestureDetector requires that any provided DoubleTapListener remain 807 // attached to it for the duration of a touch sequence. Defer any potential 808 // null'ing of the listener until the sequence has ended. 809 if (current_down_event_) 810 return; 811 812 const bool double_tap_enabled = double_tap_support_for_page_ && 813 double_tap_support_for_platform_; 814 gesture_listener_->SetDoubleTapEnabled(double_tap_enabled); 815 scale_gesture_listener_->SetDoubleTapEnabled(double_tap_enabled); 816} 817 818} // namespace ui 819