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