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