Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / ui / events / gesture_detection / gesture_detector.cc
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 // MSVC++ requires this to be set before any other includes to get M_PI.
6 #define _USE_MATH_DEFINES
7
8 #include "ui/events/gesture_detection/gesture_detector.h"
9
10 #include <cmath>
11
12 #include "base/timer/timer.h"
13 #include "ui/events/gesture_detection/gesture_listeners.h"
14 #include "ui/events/gesture_detection/motion_event.h"
15
16 namespace ui {
17 namespace {
18
19 // Using a small epsilon when comparing slop distances allows pixel perfect
20 // slop determination when using fractional DIP coordinates (assuming the slop
21 // region and DPI scale are reasonably proportioned).
22 const float kSlopEpsilon = .05f;
23
24 // Minimum distance a scroll must have traveled from the last scroll/focal point
25 // to trigger an |OnScroll| callback.
26 const float kScrollEpsilon = .1f;
27
28 const float kDegreesToRadians = static_cast<float>(M_PI) / 180.0f;
29
30 // Constants used by TimeoutGestureHandler.
31 enum TimeoutEvent {
32   SHOW_PRESS = 0,
33   LONG_PRESS,
34   TAP,
35   TIMEOUT_EVENT_COUNT
36 };
37
38 }  // namespace
39
40 // Note: These constants were taken directly from the default (unscaled)
41 // versions found in Android's ViewConfiguration.
42 GestureDetector::Config::Config()
43     : longpress_timeout(base::TimeDelta::FromMilliseconds(500)),
44       showpress_timeout(base::TimeDelta::FromMilliseconds(180)),
45       double_tap_timeout(base::TimeDelta::FromMilliseconds(300)),
46       double_tap_min_time(base::TimeDelta::FromMilliseconds(40)),
47       touch_slop(8),
48       double_tap_slop(100),
49       minimum_fling_velocity(50),
50       maximum_fling_velocity(8000),
51       swipe_enabled(false),
52       minimum_swipe_velocity(20),
53       maximum_swipe_deviation_angle(20.f),
54       two_finger_tap_enabled(false),
55       two_finger_tap_max_separation(300),
56       two_finger_tap_timeout(base::TimeDelta::FromMilliseconds(700)),
57       velocity_tracker_strategy(VelocityTracker::Strategy::STRATEGY_DEFAULT) {
58 }
59
60 GestureDetector::Config::~Config() {}
61
62 class GestureDetector::TimeoutGestureHandler {
63  public:
64   TimeoutGestureHandler(const Config& config, GestureDetector* gesture_detector)
65       : gesture_detector_(gesture_detector) {
66     DCHECK(config.showpress_timeout <= config.longpress_timeout);
67
68     timeout_callbacks_[SHOW_PRESS] = &GestureDetector::OnShowPressTimeout;
69     timeout_delays_[SHOW_PRESS] = config.showpress_timeout;
70
71     timeout_callbacks_[LONG_PRESS] = &GestureDetector::OnLongPressTimeout;
72     timeout_delays_[LONG_PRESS] =
73         config.longpress_timeout + config.showpress_timeout;
74
75     timeout_callbacks_[TAP] = &GestureDetector::OnTapTimeout;
76     timeout_delays_[TAP] = config.double_tap_timeout;
77   }
78
79   ~TimeoutGestureHandler() {
80     Stop();
81   }
82
83   void StartTimeout(TimeoutEvent event) {
84     timeout_timers_[event].Start(FROM_HERE,
85                                  timeout_delays_[event],
86                                  gesture_detector_,
87                                  timeout_callbacks_[event]);
88   }
89
90   void StopTimeout(TimeoutEvent event) { timeout_timers_[event].Stop(); }
91
92   void Stop() {
93     for (size_t i = SHOW_PRESS; i < TIMEOUT_EVENT_COUNT; ++i)
94       timeout_timers_[i].Stop();
95   }
96
97   bool HasTimeout(TimeoutEvent event) const {
98     return timeout_timers_[event].IsRunning();
99   }
100
101  private:
102   typedef void (GestureDetector::*ReceiverMethod)();
103
104   GestureDetector* const gesture_detector_;
105   base::OneShotTimer<GestureDetector> timeout_timers_[TIMEOUT_EVENT_COUNT];
106   ReceiverMethod timeout_callbacks_[TIMEOUT_EVENT_COUNT];
107   base::TimeDelta timeout_delays_[TIMEOUT_EVENT_COUNT];
108 };
109
110 GestureDetector::GestureDetector(
111     const Config& config,
112     GestureListener* listener,
113     DoubleTapListener* optional_double_tap_listener)
114     : timeout_handler_(new TimeoutGestureHandler(config, this)),
115       listener_(listener),
116       double_tap_listener_(optional_double_tap_listener),
117       touch_slop_square_(0),
118       double_tap_touch_slop_square_(0),
119       double_tap_slop_square_(0),
120       two_finger_tap_distance_square_(0),
121       min_fling_velocity_(1),
122       max_fling_velocity_(1),
123       min_swipe_velocity_(0),
124       min_swipe_direction_component_ratio_(0),
125       still_down_(false),
126       defer_confirm_single_tap_(false),
127       always_in_tap_region_(false),
128       always_in_bigger_tap_region_(false),
129       two_finger_tap_allowed_for_gesture_(false),
130       is_double_tapping_(false),
131       last_focus_x_(0),
132       last_focus_y_(0),
133       down_focus_x_(0),
134       down_focus_y_(0),
135       longpress_enabled_(true),
136       showpress_enabled_(true),
137       swipe_enabled_(false),
138       two_finger_tap_enabled_(false),
139       velocity_tracker_(config.velocity_tracker_strategy) {
140   DCHECK(listener_);
141   Init(config);
142 }
143
144 GestureDetector::~GestureDetector() {}
145
146 bool GestureDetector::OnTouchEvent(const MotionEvent& ev) {
147   const MotionEvent::Action action = ev.GetAction();
148
149   velocity_tracker_.AddMovement(ev);
150
151   const bool pointer_up = action == MotionEvent::ACTION_POINTER_UP;
152   const int skip_index = pointer_up ? ev.GetActionIndex() : -1;
153
154   // Determine focal point.
155   float sum_x = 0, sum_y = 0;
156   const int count = static_cast<int>(ev.GetPointerCount());
157   for (int i = 0; i < count; i++) {
158     if (skip_index == i)
159       continue;
160     sum_x += ev.GetX(i);
161     sum_y += ev.GetY(i);
162   }
163   const int div = pointer_up ? count - 1 : count;
164   const float focus_x = sum_x / div;
165   const float focus_y = sum_y / div;
166
167   bool handled = false;
168
169   switch (action) {
170     case MotionEvent::ACTION_POINTER_DOWN: {
171       down_focus_x_ = last_focus_x_ = focus_x;
172       down_focus_y_ = last_focus_y_ = focus_y;
173       // Cancel long press and taps.
174       CancelTaps();
175
176       if (!two_finger_tap_allowed_for_gesture_)
177         break;
178
179       const int action_index = ev.GetActionIndex();
180       const float dx = ev.GetX(action_index) - current_down_event_->GetX();
181       const float dy = ev.GetY(action_index) - current_down_event_->GetY();
182
183       if (ev.GetPointerCount() == 2 &&
184           dx * dx + dy * dy < two_finger_tap_distance_square_) {
185         secondary_pointer_down_event_ = ev.Clone();
186       } else {
187         two_finger_tap_allowed_for_gesture_ = false;
188       }
189     } break;
190
191     case MotionEvent::ACTION_POINTER_UP: {
192       down_focus_x_ = last_focus_x_ = focus_x;
193       down_focus_y_ = last_focus_y_ = focus_y;
194
195       // Check the dot product of current velocities.
196       // If the pointer that left was opposing another velocity vector, clear.
197       velocity_tracker_.ComputeCurrentVelocity(1000, max_fling_velocity_);
198       const int up_index = ev.GetActionIndex();
199       const int id1 = ev.GetPointerId(up_index);
200       const float vx1 = velocity_tracker_.GetXVelocity(id1);
201       const float vy1 = velocity_tracker_.GetYVelocity(id1);
202       float vx_total = vx1;
203       float vy_total = vy1;
204       for (int i = 0; i < count; i++) {
205         if (i == up_index)
206           continue;
207
208         const int id2 = ev.GetPointerId(i);
209         const float vx2 = velocity_tracker_.GetXVelocity(id2);
210         const float vy2 = velocity_tracker_.GetYVelocity(id2);
211         const float dot = vx1 * vx2 + vy1 * vy2;
212         if (dot < 0) {
213           vx_total = 0;
214           vy_total = 0;
215           velocity_tracker_.Clear();
216           break;
217         }
218         vx_total += vx2;
219         vy_total += vy2;
220       }
221
222       handled = HandleSwipeIfNeeded(ev, vx_total / count, vy_total / count);
223
224       if (two_finger_tap_allowed_for_gesture_ && ev.GetPointerCount() == 2 &&
225           (ev.GetEventTime() - secondary_pointer_down_event_->GetEventTime() <=
226            two_finger_tap_timeout_)) {
227         handled = listener_->OnTwoFingerTap(*current_down_event_, ev);
228       }
229       two_finger_tap_allowed_for_gesture_ = false;
230     } break;
231
232     case MotionEvent::ACTION_DOWN:
233       if (double_tap_listener_) {
234         bool had_tap_message = timeout_handler_->HasTimeout(TAP);
235         if (had_tap_message)
236           timeout_handler_->StopTimeout(TAP);
237         if (current_down_event_ && previous_up_event_ && had_tap_message &&
238             IsConsideredDoubleTap(
239                 *current_down_event_, *previous_up_event_, ev)) {
240           // This is a second tap.
241           is_double_tapping_ = true;
242           // Give a callback with the first tap of the double-tap.
243           handled |= double_tap_listener_->OnDoubleTap(*current_down_event_);
244           // Give a callback with down event of the double-tap.
245           handled |= double_tap_listener_->OnDoubleTapEvent(ev);
246         } else {
247           // This is a first tap.
248           DCHECK(double_tap_timeout_ > base::TimeDelta());
249           timeout_handler_->StartTimeout(TAP);
250         }
251       }
252
253       down_focus_x_ = last_focus_x_ = focus_x;
254       down_focus_y_ = last_focus_y_ = focus_y;
255       current_down_event_ = ev.Clone();
256
257       secondary_pointer_down_event_.reset();
258       always_in_tap_region_ = true;
259       always_in_bigger_tap_region_ = true;
260       still_down_ = true;
261       defer_confirm_single_tap_ = false;
262       two_finger_tap_allowed_for_gesture_ = two_finger_tap_enabled_;
263
264       // Always start the SHOW_PRESS timer before the LONG_PRESS timer to ensure
265       // proper timeout ordering.
266       if (showpress_enabled_)
267         timeout_handler_->StartTimeout(SHOW_PRESS);
268       if (longpress_enabled_)
269         timeout_handler_->StartTimeout(LONG_PRESS);
270       handled |= listener_->OnDown(ev);
271       break;
272
273     case MotionEvent::ACTION_MOVE:
274       {
275         const float scroll_x = last_focus_x_ - focus_x;
276         const float scroll_y = last_focus_y_ - focus_y;
277         if (is_double_tapping_) {
278           // Give the move events of the double-tap.
279           DCHECK(double_tap_listener_);
280           handled |= double_tap_listener_->OnDoubleTapEvent(ev);
281         } else if (always_in_tap_region_) {
282           const float delta_x = focus_x - down_focus_x_;
283           const float delta_y = focus_y - down_focus_y_;
284           const float distance_square = delta_x * delta_x + delta_y * delta_y;
285           if (distance_square > touch_slop_square_) {
286             handled = listener_->OnScroll(
287                 *current_down_event_, ev, scroll_x, scroll_y);
288             last_focus_x_ = focus_x;
289             last_focus_y_ = focus_y;
290             always_in_tap_region_ = false;
291             timeout_handler_->Stop();
292           }
293           if (distance_square > double_tap_touch_slop_square_)
294             always_in_bigger_tap_region_ = false;
295         } else if (std::abs(scroll_x) > kScrollEpsilon ||
296                    std::abs(scroll_y) > kScrollEpsilon) {
297           handled =
298               listener_->OnScroll(*current_down_event_, ev, scroll_x, scroll_y);
299           last_focus_x_ = focus_x;
300           last_focus_y_ = focus_y;
301         }
302
303         if (!two_finger_tap_allowed_for_gesture_)
304           break;
305
306         // Two-finger tap should be prevented if either pointer exceeds its
307         // (independent) slop region.
308         const int id0 = current_down_event_->GetPointerId(0);
309         const int ev_idx0 = ev.GetPointerId(0) == id0 ? 0 : 1;
310
311         // Check if the primary pointer exceeded the slop region.
312         float dx = current_down_event_->GetX() - ev.GetX(ev_idx0);
313         float dy = current_down_event_->GetY() - ev.GetY(ev_idx0);
314         if (dx * dx + dy * dy > touch_slop_square_) {
315           two_finger_tap_allowed_for_gesture_ = false;
316           break;
317         }
318         if (ev.GetPointerCount() == 2) {
319           // Check if the secondary pointer exceeded the slop region.
320           const int ev_idx1 = ev_idx0 == 0 ? 1 : 0;
321           const int idx1 = secondary_pointer_down_event_->GetActionIndex();
322           dx = secondary_pointer_down_event_->GetX(idx1) - ev.GetX(ev_idx1);
323           dy = secondary_pointer_down_event_->GetY(idx1) - ev.GetY(ev_idx1);
324           if (dx * dx + dy * dy > touch_slop_square_)
325             two_finger_tap_allowed_for_gesture_ = false;
326         }
327       }
328       break;
329
330     case MotionEvent::ACTION_UP:
331       still_down_ = false;
332       {
333         if (is_double_tapping_) {
334           // Finally, give the up event of the double-tap.
335           DCHECK(double_tap_listener_);
336           handled |= double_tap_listener_->OnDoubleTapEvent(ev);
337         } else if (always_in_tap_region_) {
338           handled = listener_->OnSingleTapUp(ev);
339           if (defer_confirm_single_tap_ && double_tap_listener_ != NULL) {
340             double_tap_listener_->OnSingleTapConfirmed(ev);
341           }
342         } else {
343
344           // A fling must travel the minimum tap distance.
345           const int pointer_id = ev.GetPointerId(0);
346           velocity_tracker_.ComputeCurrentVelocity(1000, max_fling_velocity_);
347           const float velocity_y = velocity_tracker_.GetYVelocity(pointer_id);
348           const float velocity_x = velocity_tracker_.GetXVelocity(pointer_id);
349
350           if ((std::abs(velocity_y) > min_fling_velocity_) ||
351               (std::abs(velocity_x) > min_fling_velocity_)) {
352             handled = listener_->OnFling(
353                 *current_down_event_, ev, velocity_x, velocity_y);
354           }
355
356           handled |= HandleSwipeIfNeeded(ev, velocity_x, velocity_y);
357         }
358
359         previous_up_event_ = ev.Clone();
360
361         velocity_tracker_.Clear();
362         is_double_tapping_ = false;
363         defer_confirm_single_tap_ = false;
364         timeout_handler_->StopTimeout(SHOW_PRESS);
365         timeout_handler_->StopTimeout(LONG_PRESS);
366       }
367       break;
368
369     case MotionEvent::ACTION_CANCEL:
370       Cancel();
371       break;
372   }
373
374   return handled;
375 }
376
377 void GestureDetector::SetDoubleTapListener(
378     DoubleTapListener* double_tap_listener) {
379   if (double_tap_listener == double_tap_listener_)
380     return;
381
382   DCHECK(!is_double_tapping_);
383
384   // Null'ing the double-tap listener should flush an active tap timeout.
385   if (!double_tap_listener) {
386     if (timeout_handler_->HasTimeout(TAP)) {
387       timeout_handler_->StopTimeout(TAP);
388       OnTapTimeout();
389     }
390   }
391
392   double_tap_listener_ = double_tap_listener;
393 }
394
395 void GestureDetector::Init(const Config& config) {
396   DCHECK(listener_);
397
398   const float touch_slop = config.touch_slop + kSlopEpsilon;
399   const float double_tap_touch_slop = touch_slop;
400   const float double_tap_slop = config.double_tap_slop + kSlopEpsilon;
401   touch_slop_square_ = touch_slop * touch_slop;
402   double_tap_touch_slop_square_ = double_tap_touch_slop * double_tap_touch_slop;
403   double_tap_slop_square_ = double_tap_slop * double_tap_slop;
404   double_tap_timeout_ = config.double_tap_timeout;
405   double_tap_min_time_ = config.double_tap_min_time;
406   DCHECK(double_tap_min_time_ < double_tap_timeout_);
407   min_fling_velocity_ = config.minimum_fling_velocity;
408   max_fling_velocity_ = config.maximum_fling_velocity;
409
410   swipe_enabled_ = config.swipe_enabled;
411   min_swipe_velocity_ = config.minimum_swipe_velocity;
412   DCHECK_GT(config.maximum_swipe_deviation_angle, 0);
413   DCHECK_LE(config.maximum_swipe_deviation_angle, 45);
414   const float maximum_swipe_deviation_angle =
415       std::min(45.f, std::max(0.001f, config.maximum_swipe_deviation_angle));
416   min_swipe_direction_component_ratio_ =
417       1.f / tan(maximum_swipe_deviation_angle * kDegreesToRadians);
418
419   two_finger_tap_enabled_ = config.two_finger_tap_enabled;
420   two_finger_tap_distance_square_ = config.two_finger_tap_max_separation *
421                                     config.two_finger_tap_max_separation;
422   two_finger_tap_timeout_ = config.two_finger_tap_timeout;
423 }
424
425 void GestureDetector::OnShowPressTimeout() {
426   listener_->OnShowPress(*current_down_event_);
427 }
428
429 void GestureDetector::OnLongPressTimeout() {
430   timeout_handler_->StopTimeout(TAP);
431   defer_confirm_single_tap_ = false;
432   listener_->OnLongPress(*current_down_event_);
433 }
434
435 void GestureDetector::OnTapTimeout() {
436   if (!double_tap_listener_)
437     return;
438   if (!still_down_)
439     double_tap_listener_->OnSingleTapConfirmed(*current_down_event_);
440   else
441     defer_confirm_single_tap_ = true;
442 }
443
444 void GestureDetector::Cancel() {
445   CancelTaps();
446   velocity_tracker_.Clear();
447   still_down_ = false;
448 }
449
450 void GestureDetector::CancelTaps() {
451   timeout_handler_->Stop();
452   is_double_tapping_ = false;
453   always_in_tap_region_ = false;
454   always_in_bigger_tap_region_ = false;
455   defer_confirm_single_tap_ = false;
456 }
457
458 bool GestureDetector::IsConsideredDoubleTap(
459     const MotionEvent& first_down,
460     const MotionEvent& first_up,
461     const MotionEvent& second_down) const {
462   if (!always_in_bigger_tap_region_)
463     return false;
464
465   const base::TimeDelta delta_time =
466       second_down.GetEventTime() - first_up.GetEventTime();
467   if (delta_time < double_tap_min_time_ || delta_time > double_tap_timeout_)
468     return false;
469
470   const float delta_x = first_down.GetX() - second_down.GetX();
471   const float delta_y = first_down.GetY() - second_down.GetY();
472   return (delta_x * delta_x + delta_y * delta_y < double_tap_slop_square_);
473 }
474
475 bool GestureDetector::HandleSwipeIfNeeded(const MotionEvent& up,
476                                           float vx,
477                                           float vy) {
478   if (!swipe_enabled_ || (!vx && !vy))
479     return false;
480   float vx_abs = std::abs(vx);
481   float vy_abs = std::abs(vy);
482
483   if (vx_abs < min_swipe_velocity_)
484     vx_abs = vx = 0;
485   if (vy_abs < min_swipe_velocity_)
486     vy_abs = vy = 0;
487
488   // Note that the ratio will be 0 if both velocites are below the min.
489   float ratio = vx_abs > vy_abs ? vx_abs / std::max(vy_abs, 0.001f)
490                                 : vy_abs / std::max(vx_abs, 0.001f);
491
492   if (ratio < min_swipe_direction_component_ratio_)
493     return false;
494
495   if (vx_abs > vy_abs)
496     vy = 0;
497   else
498     vx = 0;
499   return listener_->OnSwipe(*current_down_event_, up, vx, vy);
500 }
501
502 }  // namespace ui