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