Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / ui / chromeos / touch_exploration_controller.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 #include "ui/chromeos/touch_exploration_controller.h"
6
7 #include "base/logging.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "base/time/default_tick_clock.h"
10 #include "ui/aura/client/cursor_client.h"
11 #include "ui/aura/window.h"
12 #include "ui/aura/window_event_dispatcher.h"
13 #include "ui/aura/window_tree_host.h"
14 #include "ui/events/event.h"
15 #include "ui/events/event_processor.h"
16 #include "ui/events/event_utils.h"
17 #include "ui/gfx/geometry/rect.h"
18
19 #define SET_STATE(state) SetState(state, __func__)
20 #define VLOG_EVENT(event) if (VLOG_IS_ON(0)) VlogEvent(event, __func__)
21
22 namespace ui {
23
24 namespace {
25
26 // Delay between adjustment sounds.
27 const base::TimeDelta kSoundDelay = base::TimeDelta::FromMilliseconds(150);
28
29 // Delay before corner passthrough activates.
30 const base::TimeDelta kCornerPassthroughDelay =
31     base::TimeDelta::FromMilliseconds(700);
32
33 // In ChromeOS, VKEY_LWIN is synonymous for the search key.
34 const ui::KeyboardCode kChromeOSSearchKey = ui::VKEY_LWIN;
35 }  // namespace
36
37 TouchExplorationController::TouchExplorationController(
38     aura::Window* root_window,
39     TouchExplorationControllerDelegate* delegate)
40     : root_window_(root_window),
41       delegate_(delegate),
42       state_(NO_FINGERS_DOWN),
43       gesture_provider_(new GestureProviderAura(this)),
44       prev_state_(NO_FINGERS_DOWN),
45       VLOG_on_(true),
46       tick_clock_(NULL) {
47   CHECK(root_window);
48   root_window->GetHost()->GetEventSource()->AddEventRewriter(this);
49   InitializeSwipeGestureMaps();
50 }
51
52 TouchExplorationController::~TouchExplorationController() {
53   root_window_->GetHost()->GetEventSource()->RemoveEventRewriter(this);
54 }
55
56 ui::EventRewriteStatus TouchExplorationController::RewriteEvent(
57     const ui::Event& event,
58     scoped_ptr<ui::Event>* rewritten_event) {
59   if (!event.IsTouchEvent()) {
60     if (event.IsKeyEvent()) {
61       const ui::KeyEvent& key_event = static_cast<const ui::KeyEvent&>(event);
62       VLOG(0) << "\nKeyboard event: " << key_event.name()
63               << "\n Key code: " << key_event.key_code()
64               << ", Flags: " << key_event.flags()
65               << ", Is char: " << key_event.is_char();
66     }
67     return ui::EVENT_REWRITE_CONTINUE;
68   }
69   const ui::TouchEvent& touch_event = static_cast<const ui::TouchEvent&>(event);
70
71   // If the tap timer should have fired by now but hasn't, run it now and
72   // stop the timer. This is important so that behavior is consistent with
73   // the timestamps of the events, and not dependent on the granularity of
74   // the timer.
75   if (tap_timer_.IsRunning() &&
76       touch_event.time_stamp() - initial_press_->time_stamp() >
77           gesture_detector_config_.double_tap_timeout) {
78     tap_timer_.Stop();
79     OnTapTimerFired();
80     // Note: this may change the state. We should now continue and process
81     // this event under this new state.
82   }
83
84   if (passthrough_timer_.IsRunning() &&
85       event.time_stamp() - initial_press_->time_stamp() >
86           gesture_detector_config_.longpress_timeout) {
87     passthrough_timer_.Stop();
88     OnPassthroughTimerFired();
89   }
90
91   const ui::EventType type = touch_event.type();
92   const gfx::PointF& location = touch_event.location_f();
93   const int touch_id = touch_event.touch_id();
94
95   // Always update touch ids and touch locations, so we can use those
96   // no matter what state we're in.
97   if (type == ui::ET_TOUCH_PRESSED) {
98     current_touch_ids_.push_back(touch_id);
99     touch_locations_.insert(std::pair<int, gfx::PointF>(touch_id, location));
100   } else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
101     std::vector<int>::iterator it = std::find(
102         current_touch_ids_.begin(), current_touch_ids_.end(), touch_id);
103
104     // Can happen if touch exploration is enabled while fingers were down.
105     if (it == current_touch_ids_.end())
106       return ui::EVENT_REWRITE_CONTINUE;
107
108     current_touch_ids_.erase(it);
109     touch_locations_.erase(touch_id);
110   } else if (type == ui::ET_TOUCH_MOVED) {
111     std::vector<int>::iterator it = std::find(
112         current_touch_ids_.begin(), current_touch_ids_.end(), touch_id);
113
114     // Can happen if touch exploration is enabled while fingers were down.
115     if (it == current_touch_ids_.end())
116       return ui::EVENT_REWRITE_CONTINUE;
117
118     touch_locations_[*it] = location;
119   } else {
120     NOTREACHED() << "Unexpected event type received: " << event.name();
121     return ui::EVENT_REWRITE_CONTINUE;
122   }
123   VLOG_EVENT(touch_event);
124
125   // In order to avoid accidentally double tapping when moving off the edge
126   // of the screen, the state will be rewritten to NoFingersDown.
127   if ((type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) &&
128       FindEdgesWithinBounds(touch_event.location(), kLeavingScreenEdge) !=
129           NO_EDGE) {
130     if (VLOG_on_)
131       VLOG(0) << "Leaving screen";
132
133     // Indicates to the user that they are leaving the screen.
134     delegate_->PlayExitScreenEarcon();
135
136     if (current_touch_ids_.size() == 0) {
137       SET_STATE(NO_FINGERS_DOWN);
138       if (VLOG_on_) {
139         VLOG(0) << "Reset to no fingers in Rewrite event because the touch  "
140                    "release or cancel was on the edge of the screen.";
141       }
142       return ui::EVENT_REWRITE_DISCARD;
143     }
144   }
145
146   // If the user is in a gesture state, or if there is a possiblity that the
147   // user will enter it in the future, we send the event to the gesture
148   // provider so it can keep track of the state of the fingers. When the user
149   // leaves one of these states, SET_STATE will set the gesture provider to
150   // NULL.
151   if (gesture_provider_.get()) {
152     gesture_provider_->OnTouchEvent(touch_event);
153     gesture_provider_->OnTouchEventAck(false);
154     ProcessGestureEvents();
155   }
156
157   // The rest of the processing depends on what state we're in.
158   switch (state_) {
159     case NO_FINGERS_DOWN:
160       return InNoFingersDown(touch_event, rewritten_event);
161     case SINGLE_TAP_PRESSED:
162       return InSingleTapPressed(touch_event, rewritten_event);
163     case SINGLE_TAP_RELEASED:
164     case TOUCH_EXPLORE_RELEASED:
165       return InSingleTapOrTouchExploreReleased(touch_event, rewritten_event);
166     case DOUBLE_TAP_PENDING:
167       return InDoubleTapPending(touch_event, rewritten_event);
168     case TOUCH_RELEASE_PENDING:
169       return InTouchReleasePending(touch_event, rewritten_event);
170     case TOUCH_EXPLORATION:
171       return InTouchExploration(touch_event, rewritten_event);
172     case GESTURE_IN_PROGRESS:
173       return InGestureInProgress(touch_event, rewritten_event);
174     case TOUCH_EXPLORE_SECOND_PRESS:
175       return InTouchExploreSecondPress(touch_event, rewritten_event);
176     case SLIDE_GESTURE:
177       return InSlideGesture(touch_event, rewritten_event);
178     case ONE_FINGER_PASSTHROUGH:
179       return InOneFingerPassthrough(touch_event, rewritten_event);
180     case CORNER_PASSTHROUGH:
181       return InCornerPassthrough(touch_event, rewritten_event);
182     case WAIT_FOR_NO_FINGERS:
183       return InWaitForNoFingers(touch_event, rewritten_event);
184     case TWO_FINGER_TAP:
185       return InTwoFingerTap(touch_event, rewritten_event);
186   }
187   NOTREACHED();
188   return ui::EVENT_REWRITE_CONTINUE;
189 }
190
191 ui::EventRewriteStatus TouchExplorationController::NextDispatchEvent(
192     const ui::Event& last_event, scoped_ptr<ui::Event>* new_event) {
193   NOTREACHED();
194   return ui::EVENT_REWRITE_CONTINUE;
195 }
196
197 ui::EventRewriteStatus TouchExplorationController::InNoFingersDown(
198     const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event) {
199   const ui::EventType type = event.type();
200   if (type != ui::ET_TOUCH_PRESSED) {
201     NOTREACHED() << "Unexpected event type received: " << event.name();
202     return ui::EVENT_REWRITE_CONTINUE;
203   }
204
205   // If the user enters the screen from the edge then send an earcon.
206   int edge = FindEdgesWithinBounds(event.location(), kLeavingScreenEdge);
207   if (edge != NO_EDGE)
208     delegate_->PlayEnterScreenEarcon();
209
210   int location = FindEdgesWithinBounds(event.location(), kSlopDistanceFromEdge);
211   // If the press was at a corner, the user might go into corner passthrough
212   // instead.
213   bool in_a_bottom_corner =
214       (BOTTOM_LEFT_CORNER == location) || (BOTTOM_RIGHT_CORNER == location);
215   if (in_a_bottom_corner) {
216     passthrough_timer_.Start(
217         FROM_HERE,
218         gesture_detector_config_.longpress_timeout,
219         this,
220         &TouchExplorationController::OnPassthroughTimerFired);
221   }
222   initial_press_.reset(new TouchEvent(event));
223   initial_presses_[event.touch_id()] = event.location();
224   last_unused_finger_event_.reset(new TouchEvent(event));
225   StartTapTimer();
226   SET_STATE(SINGLE_TAP_PRESSED);
227   return ui::EVENT_REWRITE_DISCARD;
228 }
229
230 ui::EventRewriteStatus TouchExplorationController::InSingleTapPressed(
231     const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event) {
232   const ui::EventType type = event.type();
233
234   int location = FindEdgesWithinBounds(event.location(), kMaxDistanceFromEdge);
235   bool in_a_bottom_corner =
236       (location == BOTTOM_LEFT_CORNER) || (location == BOTTOM_RIGHT_CORNER);
237   // If the event is from the initial press and the location is no longer in the
238   // corner, then we are not waiting for a corner passthrough anymore.
239   if (event.touch_id() == initial_press_->touch_id() && !in_a_bottom_corner) {
240     if (passthrough_timer_.IsRunning()) {
241       passthrough_timer_.Stop();
242       // Since the long press timer has been running, it is possible that the
243       // tap timer has timed out before the long press timer has. If the tap
244       // timer timeout has elapsed, then fire the tap timer.
245       if (event.time_stamp() - initial_press_->time_stamp() >
246           gesture_detector_config_.double_tap_timeout) {
247         OnTapTimerFired();
248       }
249     }
250   }
251
252   if (type == ui::ET_TOUCH_PRESSED) {
253     initial_presses_[event.touch_id()] = event.location();
254     SET_STATE(TWO_FINGER_TAP);
255     return EVENT_REWRITE_DISCARD;
256   } else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
257     if (passthrough_timer_.IsRunning())
258       passthrough_timer_.Stop();
259     if (current_touch_ids_.size() == 0 &&
260         event.touch_id() == initial_press_->touch_id()) {
261       SET_STATE(SINGLE_TAP_RELEASED);
262     } else if (current_touch_ids_.size() == 0) {
263       SET_STATE(NO_FINGERS_DOWN);
264     }
265     return EVENT_REWRITE_DISCARD;
266   } else if (type == ui::ET_TOUCH_MOVED) {
267     float distance = (event.location() - initial_press_->location()).Length();
268     // If the user does not move far enough from the original position, then the
269     // resulting movement should not be considered to be a deliberate gesture or
270     // touch exploration.
271     if (distance <= gesture_detector_config_.touch_slop)
272       return EVENT_REWRITE_DISCARD;
273
274     float delta_time =
275         (event.time_stamp() - initial_press_->time_stamp()).InSecondsF();
276     float velocity = distance / delta_time;
277     if (VLOG_on_) {
278       VLOG(0) << "\n Delta time: " << delta_time << "\n Distance: " << distance
279               << "\n Velocity of click: " << velocity
280               << "\n Minimum swipe velocity: "
281               << gesture_detector_config_.minimum_swipe_velocity;
282     }
283     // Change to slide gesture if the slide occurred at the right edge.
284     int edge = FindEdgesWithinBounds(event.location(), kMaxDistanceFromEdge);
285     if (edge & RIGHT_EDGE && edge != BOTTOM_RIGHT_CORNER) {
286       SET_STATE(SLIDE_GESTURE);
287       return InSlideGesture(event, rewritten_event);
288     }
289
290     // If the user moves fast enough from the initial touch location, start
291     // gesture detection. Otherwise, jump to the touch exploration mode early.
292     if (velocity > gesture_detector_config_.minimum_swipe_velocity) {
293       SET_STATE(GESTURE_IN_PROGRESS);
294       return InGestureInProgress(event, rewritten_event);
295     }
296     EnterTouchToMouseMode();
297     SET_STATE(TOUCH_EXPLORATION);
298     return InTouchExploration(event, rewritten_event);
299   }
300   NOTREACHED();
301   return ui::EVENT_REWRITE_CONTINUE;
302 }
303
304 ui::EventRewriteStatus
305 TouchExplorationController::InSingleTapOrTouchExploreReleased(
306     const ui::TouchEvent& event,
307     scoped_ptr<ui::Event>* rewritten_event) {
308   const ui::EventType type = event.type();
309   // If there is more than one finger down, then discard to wait until no
310   // fingers are down.
311   if (current_touch_ids_.size() > 1) {
312     SET_STATE(WAIT_FOR_NO_FINGERS);
313     return ui::EVENT_REWRITE_DISCARD;
314   }
315   if (type == ui::ET_TOUCH_PRESSED) {
316     // If there is no touch exploration yet, we can't send a click, so discard.
317     if (!last_touch_exploration_) {
318       tap_timer_.Stop();
319       return ui::EVENT_REWRITE_DISCARD;
320     }
321     // This is the second tap in a double-tap (or double tap-hold).
322     // We set the tap timer. If it fires before the user lifts their finger,
323     // one-finger passthrough begins. Otherwise, there is a touch press and
324     // release at the location of the last touch exploration.
325     SET_STATE(DOUBLE_TAP_PENDING);
326     // The old tap timer (from the initial click) is stopped if it is still
327     // going, and the new one is set.
328     tap_timer_.Stop();
329     StartTapTimer();
330     // This will update as the finger moves before a possible passthrough, and
331     // will determine the offset.
332     last_unused_finger_event_.reset(new ui::TouchEvent(event));
333     return ui::EVENT_REWRITE_DISCARD;
334   } else if (type == ui::ET_TOUCH_RELEASED && !last_touch_exploration_) {
335     // If the previous press was discarded, we need to also handle its
336     // release.
337     if (current_touch_ids_.size() == 0) {
338       SET_STATE(NO_FINGERS_DOWN);
339     }
340     return ui::EVENT_REWRITE_DISCARD;
341   } else if (type == ui::ET_TOUCH_MOVED) {
342     return ui::EVENT_REWRITE_DISCARD;
343   }
344   NOTREACHED();
345   return ui::EVENT_REWRITE_CONTINUE;
346 }
347
348 ui::EventRewriteStatus TouchExplorationController::InDoubleTapPending(
349     const ui::TouchEvent& event,
350     scoped_ptr<ui::Event>* rewritten_event) {
351   const ui::EventType type = event.type();
352   if (type == ui::ET_TOUCH_PRESSED) {
353     return ui::EVENT_REWRITE_DISCARD;
354   } else if (type == ui::ET_TOUCH_MOVED) {
355     // If the user moves far enough from the initial touch location (outside
356     // the "slop" region, jump to passthrough mode early.
357     float delta = (event.location() - initial_press_->location()).Length();
358     if (delta > gesture_detector_config_.touch_slop) {
359       tap_timer_.Stop();
360       OnTapTimerFired();
361     }
362     return EVENT_REWRITE_DISCARD;
363   } else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
364     if (current_touch_ids_.size() != 0)
365       return EVENT_REWRITE_DISCARD;
366
367     scoped_ptr<ui::TouchEvent> touch_press;
368     touch_press.reset(new ui::TouchEvent(ui::ET_TOUCH_PRESSED,
369                                          last_touch_exploration_->location(),
370                                          initial_press_->touch_id(),
371                                          event.time_stamp()));
372     DispatchEvent(touch_press.get());
373
374     rewritten_event->reset(
375         new ui::TouchEvent(ui::ET_TOUCH_RELEASED,
376                            last_touch_exploration_->location(),
377                            initial_press_->touch_id(),
378                            event.time_stamp()));
379     (*rewritten_event)->set_flags(event.flags());
380     SET_STATE(NO_FINGERS_DOWN);
381     return ui::EVENT_REWRITE_REWRITTEN;
382   }
383   NOTREACHED();
384   return ui::EVENT_REWRITE_CONTINUE;
385 }
386
387 ui::EventRewriteStatus TouchExplorationController::InTouchReleasePending(
388     const ui::TouchEvent& event,
389     scoped_ptr<ui::Event>* rewritten_event) {
390   const ui::EventType type = event.type();
391   if (type == ui::ET_TOUCH_PRESSED || type == ui::ET_TOUCH_MOVED) {
392     return ui::EVENT_REWRITE_DISCARD;
393   } else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
394     if (current_touch_ids_.size() != 0)
395       return EVENT_REWRITE_DISCARD;
396
397     rewritten_event->reset(
398         new ui::TouchEvent(ui::ET_TOUCH_RELEASED,
399                            last_touch_exploration_->location(),
400                            initial_press_->touch_id(),
401                            event.time_stamp()));
402     (*rewritten_event)->set_flags(event.flags());
403     SET_STATE(NO_FINGERS_DOWN);
404     return ui::EVENT_REWRITE_REWRITTEN;
405   }
406   NOTREACHED();
407   return ui::EVENT_REWRITE_CONTINUE;
408 }
409
410 ui::EventRewriteStatus TouchExplorationController::InTouchExploration(
411     const ui::TouchEvent& event,
412     scoped_ptr<ui::Event>* rewritten_event) {
413   const ui::EventType type = event.type();
414   if (type == ui::ET_TOUCH_PRESSED) {
415     // Handle split-tap.
416     initial_press_.reset(new TouchEvent(event));
417     tap_timer_.Stop();
418     rewritten_event->reset(
419         new ui::TouchEvent(ui::ET_TOUCH_PRESSED,
420                            last_touch_exploration_->location(),
421                            event.touch_id(),
422                            event.time_stamp()));
423     (*rewritten_event)->set_flags(event.flags());
424     SET_STATE(TOUCH_EXPLORE_SECOND_PRESS);
425     return ui::EVENT_REWRITE_REWRITTEN;
426   } else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
427     initial_press_.reset(new TouchEvent(event));
428     StartTapTimer();
429     SET_STATE(TOUCH_EXPLORE_RELEASED);
430   } else if (type != ui::ET_TOUCH_MOVED) {
431     NOTREACHED();
432     return ui::EVENT_REWRITE_CONTINUE;
433   }
434
435   // Rewrite as a mouse-move event.
436   *rewritten_event = CreateMouseMoveEvent(event.location(), event.flags());
437   last_touch_exploration_.reset(new TouchEvent(event));
438   return ui::EVENT_REWRITE_REWRITTEN;
439 }
440
441 ui::EventRewriteStatus TouchExplorationController::InGestureInProgress(
442     const ui::TouchEvent& event,
443     scoped_ptr<ui::Event>* rewritten_event) {
444   // The events were sent to the gesture provider in RewriteEvent already.
445   // If no gesture is registered before the tap timer times out, the state
446   // will change to "wait for no fingers down" or "touch exploration" depending
447   // on the number of fingers down, and this function will stop being called.
448   if (current_touch_ids_.size() == 0) {
449     SET_STATE(NO_FINGERS_DOWN);
450   }
451   return ui::EVENT_REWRITE_DISCARD;
452 }
453
454 ui::EventRewriteStatus TouchExplorationController::InCornerPassthrough(
455     const ui::TouchEvent& event,
456     scoped_ptr<ui::Event>* rewritten_event) {
457   ui::EventType type = event.type();
458
459   // If the first finger has left the corner, then exit passthrough.
460   if (event.touch_id() == initial_press_->touch_id()) {
461     int edges = FindEdgesWithinBounds(event.location(), kSlopDistanceFromEdge);
462     bool in_a_bottom_corner = (edges == BOTTOM_LEFT_CORNER) ||
463                               (edges == BOTTOM_RIGHT_CORNER);
464     if (type == ui::ET_TOUCH_MOVED && in_a_bottom_corner)
465       return ui::EVENT_REWRITE_DISCARD;
466
467     if (current_touch_ids_.size() == 0) {
468       SET_STATE(NO_FINGERS_DOWN);
469       return ui::EVENT_REWRITE_DISCARD;
470     }
471     SET_STATE(WAIT_FOR_NO_FINGERS);
472     return ui::EVENT_REWRITE_DISCARD;
473   }
474
475   rewritten_event->reset(new ui::TouchEvent(
476       type, event.location(), event.touch_id(), event.time_stamp()));
477   (*rewritten_event)->set_flags(event.flags());
478
479   if (current_touch_ids_.size() == 0)
480     SET_STATE(NO_FINGERS_DOWN);
481
482   return ui::EVENT_REWRITE_REWRITTEN;
483 }
484
485 ui::EventRewriteStatus TouchExplorationController::InOneFingerPassthrough(
486     const ui::TouchEvent& event,
487     scoped_ptr<ui::Event>* rewritten_event) {
488   if (event.touch_id() != initial_press_->touch_id()) {
489     if (current_touch_ids_.size() == 0) {
490       SET_STATE(NO_FINGERS_DOWN);
491     }
492     return ui::EVENT_REWRITE_DISCARD;
493   }
494   rewritten_event->reset(
495       new ui::TouchEvent(event.type(),
496                          event.location() - passthrough_offset_,
497                          event.touch_id(),
498                          event.time_stamp()));
499
500   (*rewritten_event)->set_flags(event.flags());
501   if (current_touch_ids_.size() == 0) {
502     SET_STATE(NO_FINGERS_DOWN);
503   }
504   return ui::EVENT_REWRITE_REWRITTEN;
505 }
506
507 ui::EventRewriteStatus TouchExplorationController::InTouchExploreSecondPress(
508     const ui::TouchEvent& event,
509     scoped_ptr<ui::Event>* rewritten_event) {
510   ui::EventType type = event.type();
511   gfx::PointF location = event.location_f();
512   if (type == ui::ET_TOUCH_PRESSED) {
513     // A third finger being pressed means that a split tap can no longer go
514     // through. The user enters the wait state, Since there has already been
515     // a press dispatched when split tap began, the touch needs to be
516     // cancelled.
517     rewritten_event->reset(
518         new ui::TouchEvent(ui::ET_TOUCH_CANCELLED,
519                            last_touch_exploration_->location(),
520                            initial_press_->touch_id(),
521                            event.time_stamp()));
522     (*rewritten_event)->set_flags(event.flags());
523     SET_STATE(WAIT_FOR_NO_FINGERS);
524     return ui::EVENT_REWRITE_REWRITTEN;
525   } else if (type == ui::ET_TOUCH_MOVED) {
526     // If the fingers have moved too far from their original locations,
527     // the user can no longer split tap.
528     ui::TouchEvent* original_touch;
529     if (event.touch_id() == last_touch_exploration_->touch_id())
530       original_touch = last_touch_exploration_.get();
531     else if (event.touch_id() == initial_press_->touch_id())
532       original_touch = initial_press_.get();
533     else {
534       NOTREACHED();
535       SET_STATE(WAIT_FOR_NO_FINGERS);
536       return ui::EVENT_REWRITE_DISCARD;
537     }
538     // Check the distance between the current finger location and the original
539     // location. The slop for this is a bit more generous since keeping two
540     // fingers in place is a bit harder. If the user has left the slop, the
541     // split tap press (which was previous dispatched) is lifted with a touch
542     // cancelled, and the user enters the wait state.
543     if ((event.location() - original_touch->location()).Length() >
544         GetSplitTapTouchSlop()) {
545       rewritten_event->reset(
546           new ui::TouchEvent(ui::ET_TOUCH_CANCELLED,
547                              last_touch_exploration_->location(),
548                              initial_press_->touch_id(),
549                              event.time_stamp()));
550       (*rewritten_event)->set_flags(event.flags());
551       SET_STATE(WAIT_FOR_NO_FINGERS);
552       return ui::EVENT_REWRITE_REWRITTEN;
553     }
554     return ui::EVENT_REWRITE_DISCARD;
555   } else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
556     // If the touch exploration finger is lifted, there is no option to return
557     // to touch explore anymore. The remaining finger acts as a pending
558     // tap or long tap for the last touch explore location.
559     if (event.touch_id() == last_touch_exploration_->touch_id()){
560       SET_STATE(TOUCH_RELEASE_PENDING);
561       return EVENT_REWRITE_DISCARD;
562     }
563
564     // Continue to release the touch only if the touch explore finger is the
565     // only finger remaining.
566     if (current_touch_ids_.size() != 1)
567       return EVENT_REWRITE_DISCARD;
568
569     // Rewrite at location of last touch exploration.
570     rewritten_event->reset(
571         new ui::TouchEvent(ui::ET_TOUCH_RELEASED,
572                            last_touch_exploration_->location(),
573                            initial_press_->touch_id(),
574                            event.time_stamp()));
575     (*rewritten_event)->set_flags(event.flags());
576     SET_STATE(TOUCH_EXPLORATION);
577     EnterTouchToMouseMode();
578     return ui::EVENT_REWRITE_REWRITTEN;
579   }
580   NOTREACHED();
581   return ui::EVENT_REWRITE_CONTINUE;
582 }
583
584 ui::EventRewriteStatus TouchExplorationController::InWaitForNoFingers(
585     const ui::TouchEvent& event,
586     scoped_ptr<ui::Event>* rewritten_event) {
587   if (current_touch_ids_.size() == 0)
588     SET_STATE(NO_FINGERS_DOWN);
589   return EVENT_REWRITE_DISCARD;
590 }
591
592 void TouchExplorationController::PlaySoundForTimer() {
593   delegate_->PlayVolumeAdjustEarcon();
594 }
595
596 ui::EventRewriteStatus TouchExplorationController::InSlideGesture(
597     const ui::TouchEvent& event,
598     scoped_ptr<ui::Event>* rewritten_event) {
599   // The timer should not fire when sliding.
600   tap_timer_.Stop();
601
602   ui::EventType type = event.type();
603   // If additional fingers are added before a swipe gesture has been registered,
604   // then wait until all fingers have been lifted.
605   if (type == ui::ET_TOUCH_PRESSED ||
606       event.touch_id() != initial_press_->touch_id()) {
607     if (sound_timer_.IsRunning())
608       sound_timer_.Stop();
609     SET_STATE(WAIT_FOR_NO_FINGERS);
610     return EVENT_REWRITE_DISCARD;
611   }
612
613   // There should not be more than one finger down.
614   DCHECK(current_touch_ids_.size() <= 1);
615
616   // Allows user to return to the edge to adjust the sound if they have left the
617   // boundaries.
618   int edge = FindEdgesWithinBounds(event.location(), kSlopDistanceFromEdge);
619   if (!(edge & RIGHT_EDGE) && (type != ui::ET_TOUCH_RELEASED)) {
620     if (sound_timer_.IsRunning()) {
621       sound_timer_.Stop();
622     }
623     return EVENT_REWRITE_DISCARD;
624   }
625
626   // This can occur if the user leaves the screen edge and then returns to it to
627   // continue adjusting the sound.
628   if (!sound_timer_.IsRunning()) {
629     sound_timer_.Start(FROM_HERE,
630                        kSoundDelay,
631                        this,
632                        &ui::TouchExplorationController::PlaySoundForTimer);
633     delegate_->PlayVolumeAdjustEarcon();
634   }
635
636   if (current_touch_ids_.size() == 0) {
637     SET_STATE(NO_FINGERS_DOWN);
638   }
639   return ui::EVENT_REWRITE_DISCARD;
640 }
641
642 ui::EventRewriteStatus TouchExplorationController::InTwoFingerTap(
643     const ui::TouchEvent& event,
644     scoped_ptr<ui::Event>* rewritten_event) {
645   ui::EventType type = event.type();
646   if (type == ui::ET_TOUCH_PRESSED) {
647     // This is now a three finger gesture.
648     SET_STATE(GESTURE_IN_PROGRESS);
649     return ui::EVENT_REWRITE_DISCARD;
650   }
651
652   if (type == ui::ET_TOUCH_MOVED) {
653     // Determine if it was a swipe.
654     gfx::Point original_location = initial_presses_[event.touch_id()];
655     float distance = (event.location() - original_location).Length();
656     // If the user moves too far from the original position, consider the
657     // movement a swipe.
658     if (distance > gesture_detector_config_.touch_slop) {
659       SET_STATE(GESTURE_IN_PROGRESS);
660     }
661     return ui::EVENT_REWRITE_DISCARD;
662   }
663
664   if (current_touch_ids_.size() != 0)
665     return ui::EVENT_REWRITE_DISCARD;
666
667   if (type == ui::ET_TOUCH_RELEASED) {
668     // In ChromeVox, pressing control will stop ChromeVox from speaking.
669     ui::KeyEvent control_down(
670         ui::ET_KEY_PRESSED, ui::VKEY_CONTROL, ui::EF_CONTROL_DOWN);
671     ui::KeyEvent control_up(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL, ui::EF_NONE);
672
673     DispatchEvent(&control_down);
674     DispatchEvent(&control_up);
675     SET_STATE(NO_FINGERS_DOWN);
676     return ui::EVENT_REWRITE_DISCARD;
677   }
678   return ui::EVENT_REWRITE_DISCARD;
679 }
680
681 base::TimeDelta TouchExplorationController::Now() {
682   if (tick_clock_) {
683     // This is the same as what EventTimeForNow() does, but here we do it
684     // with a clock that can be replaced with a simulated clock for tests.
685     return base::TimeDelta::FromInternalValue(
686         tick_clock_->NowTicks().ToInternalValue());
687   }
688   return ui::EventTimeForNow();
689 }
690
691 void TouchExplorationController::StartTapTimer() {
692   tap_timer_.Start(FROM_HERE,
693                    gesture_detector_config_.double_tap_timeout,
694                    this,
695                    &TouchExplorationController::OnTapTimerFired);
696 }
697
698 void TouchExplorationController::OnTapTimerFired() {
699   switch (state_) {
700     case SINGLE_TAP_RELEASED:
701       SET_STATE(NO_FINGERS_DOWN);
702       break;
703     case TOUCH_EXPLORE_RELEASED:
704       SET_STATE(NO_FINGERS_DOWN);
705       last_touch_exploration_.reset(new TouchEvent(*initial_press_));
706       return;
707     case DOUBLE_TAP_PENDING: {
708       SET_STATE(ONE_FINGER_PASSTHROUGH);
709       passthrough_offset_ = last_unused_finger_event_->location() -
710                             last_touch_exploration_->location();
711       scoped_ptr<ui::TouchEvent> passthrough_press(
712           new ui::TouchEvent(ui::ET_TOUCH_PRESSED,
713                              last_touch_exploration_->location(),
714                              last_unused_finger_event_->touch_id(),
715                              Now()));
716       DispatchEvent(passthrough_press.get());
717       return;
718     }
719     case SINGLE_TAP_PRESSED:
720       if (passthrough_timer_.IsRunning())
721         return;
722     case GESTURE_IN_PROGRESS:
723       // If only one finger is down, go into touch exploration.
724       if (current_touch_ids_.size() == 1) {
725         EnterTouchToMouseMode();
726         SET_STATE(TOUCH_EXPLORATION);
727         break;
728       }
729       // Otherwise wait for all fingers to be lifted.
730       SET_STATE(WAIT_FOR_NO_FINGERS);
731       return;
732     case TWO_FINGER_TAP:
733       SET_STATE(WAIT_FOR_NO_FINGERS);
734       break;
735     default:
736       return;
737   }
738   EnterTouchToMouseMode();
739   scoped_ptr<ui::Event> mouse_move =
740       CreateMouseMoveEvent(initial_press_->location(), initial_press_->flags());
741   DispatchEvent(mouse_move.get());
742   last_touch_exploration_.reset(new TouchEvent(*initial_press_));
743 }
744
745 void TouchExplorationController::OnPassthroughTimerFired() {
746   // The passthrough timer will only fire if if the user has held a finger in
747   // one of the passthrough corners for the duration of the passthrough timeout.
748
749   // Check that initial press isn't null. Also a check that if the initial
750   // corner press was released, then it should not be in corner passthrough.
751   if (!initial_press_ ||
752       touch_locations_.find(initial_press_->touch_id()) !=
753           touch_locations_.end()) {
754     LOG(ERROR) << "No initial press or the initial press has been released.";
755   }
756
757   gfx::Point location =
758       ToRoundedPoint(touch_locations_[initial_press_->touch_id()]);
759   int corner = FindEdgesWithinBounds(location, kSlopDistanceFromEdge);
760   if (corner != BOTTOM_LEFT_CORNER && corner != BOTTOM_RIGHT_CORNER)
761     return;
762
763   if (sound_timer_.IsRunning())
764     sound_timer_.Stop();
765   delegate_->PlayPassthroughEarcon();
766   SET_STATE(CORNER_PASSTHROUGH);
767   return;
768 }
769
770 void TouchExplorationController::DispatchEvent(ui::Event* event) {
771   ignore_result(
772       root_window_->GetHost()->dispatcher()->OnEventFromSource(event));
773 }
774
775 // This is an override for a function that is only called for timer-based events
776 // like long press. Events that are created synchronously as a result of
777 // certain touch events are added to the vector accessible via
778 // GetAndResetPendingGestures(). We only care about swipes (which are created
779 // synchronously), so we ignore this callback.
780 void TouchExplorationController::OnGestureEvent(ui::GestureEvent* gesture) {
781 }
782
783 void TouchExplorationController::ProcessGestureEvents() {
784   scoped_ptr<ScopedVector<ui::GestureEvent> > gestures(
785       gesture_provider_->GetAndResetPendingGestures());
786   if (gestures) {
787     for (ScopedVector<GestureEvent>::iterator i = gestures->begin();
788          i != gestures->end();
789          ++i) {
790       if ((*i)->type() == ui::ET_GESTURE_SWIPE &&
791           state_ == GESTURE_IN_PROGRESS) {
792         OnSwipeEvent(*i);
793         // The tap timer to leave gesture state is ended, and we now wait for
794         // all fingers to be released.
795         tap_timer_.Stop();
796         SET_STATE(WAIT_FOR_NO_FINGERS);
797         return;
798       }
799       if (state_ == SLIDE_GESTURE && (*i)->IsScrollGestureEvent()) {
800         SideSlideControl(*i);
801       }
802     }
803   }
804 }
805
806 void TouchExplorationController::SideSlideControl(ui::GestureEvent* gesture) {
807   ui::EventType type = gesture->type();
808
809   if (type == ET_GESTURE_SCROLL_BEGIN) {
810     delegate_->PlayVolumeAdjustEarcon();
811   }
812
813   if (type == ET_GESTURE_SCROLL_END) {
814     if (sound_timer_.IsRunning())
815       sound_timer_.Stop();
816     delegate_->PlayVolumeAdjustEarcon();
817   }
818
819   // If the user is in the corner of the right side of the screen, the volume
820   // will be automatically set to 100% or muted depending on which corner they
821   // are in. Otherwise, the user will be able to adjust the volume by sliding
822   // their finger along the right side of the screen. Volume is relative to
823   // where they are on the right side of the screen.
824   gfx::Point location = gesture->location();
825   int edge = FindEdgesWithinBounds(location, kSlopDistanceFromEdge);
826   if (!(edge & RIGHT_EDGE))
827     return;
828
829   if (edge & TOP_EDGE) {
830     delegate_->SetOutputLevel(100);
831     return;
832   }
833   if (edge & BOTTOM_EDGE) {
834     delegate_->SetOutputLevel(0);
835     return;
836   }
837
838   location = gesture->location();
839   root_window_->GetHost()->ConvertPointFromNativeScreen(&location);
840   float volume_adjust_height =
841       root_window_->bounds().height() - 2 * kMaxDistanceFromEdge;
842   float ratio = (location.y() - kMaxDistanceFromEdge) / volume_adjust_height;
843   float volume = 100 - 100 * ratio;
844   if (VLOG_on_) {
845     VLOG(0) << "\n Volume = " << volume
846             << "\n Location = " << location.ToString()
847             << "\n Bounds = " << root_window_->bounds().right();
848   }
849   delegate_->SetOutputLevel(int(volume));
850 }
851
852 void TouchExplorationController::OnSwipeEvent(ui::GestureEvent* swipe_gesture) {
853   // A swipe gesture contains details for the direction in which the swipe
854   // occurred. TODO(evy) : Research which swipe results users most want and
855   // remap these swipes to the best events. Hopefully in the near future
856   // there will also be a menu for users to pick custom mappings.
857   GestureEventDetails event_details = swipe_gesture->details();
858   int num_fingers = event_details.touch_points();
859   if(VLOG_on_)
860     VLOG(0) << "\nSwipe with " << num_fingers << " fingers.";
861
862   if (num_fingers > 4)
863     return;
864
865   if (event_details.swipe_left() &&
866       !left_swipe_gestures_[num_fingers].is_null()) {
867     left_swipe_gestures_[num_fingers].Run();
868   } else if (event_details.swipe_right() &&
869              !right_swipe_gestures_[num_fingers].is_null()) {
870     right_swipe_gestures_[num_fingers].Run();
871   } else if (event_details.swipe_up() &&
872              !up_swipe_gestures_[num_fingers].is_null()) {
873     up_swipe_gestures_[num_fingers].Run();
874   } else if (event_details.swipe_down() &&
875              !down_swipe_gestures_[num_fingers].is_null()) {
876     down_swipe_gestures_[num_fingers].Run();
877   }
878 }
879
880 int TouchExplorationController::FindEdgesWithinBounds(gfx::Point point,
881                                                       float bounds) {
882   // Since GetBoundsInScreen is in DIPs but point is not, then point needs to be
883   // converted.
884   root_window_->GetHost()->ConvertPointFromNativeScreen(&point);
885   gfx::Rect window = root_window_->GetBoundsInScreen();
886
887   float left_edge_limit = window.x() + bounds;
888   float right_edge_limit = window.right() - bounds;
889   float top_edge_limit = window.y() + bounds;
890   float bottom_edge_limit = window.bottom() - bounds;
891
892   // Bitwise manipulation in order to determine where on the screen the point
893   // lies. If more than one bit is turned on, then it is a corner where the two
894   // bit/edges intersect. Otherwise, if no bits are turned on, the point must be
895   // in the center of the screen.
896   int result = NO_EDGE;
897   if (point.x() < left_edge_limit)
898     result |= LEFT_EDGE;
899   if (point.x() > right_edge_limit)
900     result |= RIGHT_EDGE;
901   if (point.y() < top_edge_limit)
902     result |= TOP_EDGE;
903   if (point.y() > bottom_edge_limit)
904     result |= BOTTOM_EDGE;
905   return result;
906 }
907
908 void TouchExplorationController::DispatchShiftSearchKeyEvent(
909     const ui::KeyboardCode third_key) {
910   // In order to activate the shortcut shift+search+<arrow key>
911   // three KeyPressed events must be dispatched in succession along
912   // with three KeyReleased events.
913
914   ui::KeyEvent shift_down(
915       ui::ET_KEY_PRESSED, ui::VKEY_SHIFT, ui::EF_SHIFT_DOWN);
916   ui::KeyEvent search_down(
917       ui::ET_KEY_PRESSED, kChromeOSSearchKey, ui::EF_SHIFT_DOWN);
918   ui::KeyEvent third_key_down(ui::ET_KEY_PRESSED, third_key, ui::EF_SHIFT_DOWN);
919
920   ui::KeyEvent third_key_up(ui::ET_KEY_RELEASED, third_key, ui::EF_SHIFT_DOWN);
921   ui::KeyEvent search_up(
922       ui::ET_KEY_RELEASED, kChromeOSSearchKey, ui::EF_SHIFT_DOWN);
923   ui ::KeyEvent shift_up(ui::ET_KEY_RELEASED, ui::VKEY_SHIFT, ui::EF_NONE);
924
925   DispatchEvent(&shift_down);
926   DispatchEvent(&search_down);
927   DispatchEvent(&third_key_down);
928   DispatchEvent(&third_key_up);
929   DispatchEvent(&search_up);
930   DispatchEvent(&shift_up);
931 }
932
933 base::Closure TouchExplorationController::BindShiftSearchKeyEvent(
934     const ui::KeyboardCode third_key) {
935   return base::Bind(&TouchExplorationController::DispatchShiftSearchKeyEvent,
936                     base::Unretained(this),
937                     third_key);
938 }
939
940 void TouchExplorationController::DispatchKeyWithFlags(
941     const ui::KeyboardCode key,
942     int flags) {
943   ui::KeyEvent key_down(ui::ET_KEY_PRESSED, key, flags);
944   ui::KeyEvent key_up(ui::ET_KEY_RELEASED, key, flags);
945   DispatchEvent(&key_down);
946   DispatchEvent(&key_up);
947   if(VLOG_on_) {
948     VLOG(0) << "\nKey down: key code : " << key_down.key_code()
949             << ", flags: " << key_down.flags()
950             << "\nKey up: key code : " << key_up.key_code()
951             << ", flags: " << key_up.flags();
952   }
953 }
954
955 base::Closure TouchExplorationController::BindKeyEventWithFlags(
956     const ui::KeyboardCode key,
957     int flags) {
958   return base::Bind(&TouchExplorationController::DispatchKeyWithFlags,
959                     base::Unretained(this),
960                     key,
961                     flags);
962 }
963
964 scoped_ptr<ui::Event> TouchExplorationController::CreateMouseMoveEvent(
965     const gfx::PointF& location,
966     int flags) {
967   // The "synthesized" flag should be set on all events that don't have a
968   // backing native event.
969   flags |= ui::EF_IS_SYNTHESIZED;
970
971   // This flag is used to identify mouse move events that were generated from
972   // touch exploration in Chrome code.
973   flags |= ui::EF_TOUCH_ACCESSIBILITY;
974
975   // TODO(dmazzoni) http://crbug.com/391008 - get rid of this hack.
976   // This is a short-term workaround for the limitation that we're using
977   // the ChromeVox content script to process touch exploration events, but
978   // ChromeVox needs a way to distinguish between a real mouse move and a
979   // mouse move generated from touch exploration, so we have touch exploration
980   // pretend that the command key was down (which becomes the "meta" key in
981   // JavaScript). We can remove this hack when the ChromeVox content script
982   // goes away and native accessibility code sends a touch exploration
983   // event to the new ChromeVox background page via the automation api.
984   flags |= ui::EF_COMMAND_DOWN;
985
986   return scoped_ptr<ui::Event>(
987       new ui::MouseEvent(ui::ET_MOUSE_MOVED, location, location, flags, 0));
988 }
989
990 void TouchExplorationController::EnterTouchToMouseMode() {
991   aura::client::CursorClient* cursor_client =
992       aura::client::GetCursorClient(root_window_);
993   if (cursor_client && !cursor_client->IsMouseEventsEnabled())
994     cursor_client->EnableMouseEvents();
995   if (cursor_client && cursor_client->IsCursorVisible())
996     cursor_client->HideCursor();
997 }
998
999 void TouchExplorationController::SetState(State new_state,
1000                                           const char* function_name) {
1001   state_ = new_state;
1002   VlogState(function_name);
1003   // These are the states the user can be in that will never result in a
1004   // gesture before the user returns to NO_FINGERS_DOWN. Therefore, if the
1005   // gesture provider still exists, it's reset to NULL until the user returns
1006   // to NO_FINGERS_DOWN.
1007   switch (new_state) {
1008     case SINGLE_TAP_RELEASED:
1009     case TOUCH_EXPLORE_RELEASED:
1010     case DOUBLE_TAP_PENDING:
1011     case TOUCH_RELEASE_PENDING:
1012     case TOUCH_EXPLORATION:
1013     case TOUCH_EXPLORE_SECOND_PRESS:
1014     case ONE_FINGER_PASSTHROUGH:
1015     case CORNER_PASSTHROUGH:
1016     case WAIT_FOR_NO_FINGERS:
1017       if (gesture_provider_.get())
1018         gesture_provider_.reset(NULL);
1019       break;
1020     case NO_FINGERS_DOWN:
1021       gesture_provider_.reset(new GestureProviderAura(this));
1022       if (sound_timer_.IsRunning())
1023         sound_timer_.Stop();
1024       tap_timer_.Stop();
1025       break;
1026     case SINGLE_TAP_PRESSED:
1027     case GESTURE_IN_PROGRESS:
1028     case SLIDE_GESTURE:
1029     case TWO_FINGER_TAP:
1030       break;
1031   }
1032 }
1033
1034 void TouchExplorationController::VlogState(const char* function_name) {
1035   if (!VLOG_on_)
1036     return;
1037   if (prev_state_ == state_)
1038     return;
1039   prev_state_ = state_;
1040   const char* state_string = EnumStateToString(state_);
1041   VLOG(0) << "\n Function name: " << function_name
1042           << "\n State: " << state_string;
1043 }
1044
1045 void TouchExplorationController::VlogEvent(const ui::TouchEvent& touch_event,
1046                                            const char* function_name) {
1047   if (!VLOG_on_)
1048     return;
1049
1050   if (prev_event_ != NULL &&
1051       prev_event_->type() == touch_event.type() &&
1052       prev_event_->touch_id() == touch_event.touch_id()){
1053     return;
1054   }
1055   // The above statement prevents events of the same type and id from being
1056   // printed in a row. However, if two fingers are down, they would both be
1057   // moving and alternating printing move events unless we check for this.
1058   if (prev_event_ != NULL &&
1059       prev_event_->type() == ET_TOUCH_MOVED &&
1060       touch_event.type() == ET_TOUCH_MOVED){
1061     return;
1062   }
1063
1064   const std::string& type = touch_event.name();
1065   const gfx::PointF& location = touch_event.location_f();
1066   const int touch_id = touch_event.touch_id();
1067
1068   VLOG(0) << "\n Function name: " << function_name
1069           << "\n Event Type: " << type
1070           << "\n Location: " << location.ToString()
1071           << "\n Touch ID: " << touch_id;
1072   prev_event_.reset(new TouchEvent(touch_event));
1073 }
1074
1075 const char* TouchExplorationController::EnumStateToString(State state) {
1076   switch (state) {
1077     case NO_FINGERS_DOWN:
1078       return "NO_FINGERS_DOWN";
1079     case SINGLE_TAP_PRESSED:
1080       return "SINGLE_TAP_PRESSED";
1081     case SINGLE_TAP_RELEASED:
1082       return "SINGLE_TAP_RELEASED";
1083     case TOUCH_EXPLORE_RELEASED:
1084       return "TOUCH_EXPLORE_RELEASED";
1085     case DOUBLE_TAP_PENDING:
1086       return "DOUBLE_TAP_PENDING";
1087     case TOUCH_RELEASE_PENDING:
1088       return "TOUCH_RELEASE_PENDING";
1089     case TOUCH_EXPLORATION:
1090       return "TOUCH_EXPLORATION";
1091     case GESTURE_IN_PROGRESS:
1092       return "GESTURE_IN_PROGRESS";
1093     case TOUCH_EXPLORE_SECOND_PRESS:
1094       return "TOUCH_EXPLORE_SECOND_PRESS";
1095     case CORNER_PASSTHROUGH:
1096       return "CORNER_PASSTHROUGH";
1097     case SLIDE_GESTURE:
1098       return "SLIDE_GESTURE";
1099     case ONE_FINGER_PASSTHROUGH:
1100       return "ONE_FINGER_PASSTHROUGH";
1101     case WAIT_FOR_NO_FINGERS:
1102       return "WAIT_FOR_NO_FINGERS";
1103     case TWO_FINGER_TAP:
1104       return "TWO_FINGER_TAP";
1105   }
1106   return "Not a state";
1107 }
1108
1109 // TODO(evy, lisayin) : Just call abstracted methods on the delegate (e.g.
1110 // Swipe(Direction direction, int num_fingers)), and add the DispatchXYZ
1111 // methods to the delegate. Avoid the middle step of dispatching keys at all,
1112 // and simply have ChromeVox/ChromeOS complete the required action.
1113
1114 void TouchExplorationController::InitializeSwipeGestureMaps() {
1115   // Gestures with one finger are used for navigation.
1116   left_swipe_gestures_[1] = BindShiftSearchKeyEvent(ui::VKEY_LEFT);
1117   right_swipe_gestures_[1] = BindShiftSearchKeyEvent(ui::VKEY_RIGHT);
1118   up_swipe_gestures_[1] = BindShiftSearchKeyEvent(ui::VKEY_UP);
1119   down_swipe_gestures_[1] = BindShiftSearchKeyEvent(ui::VKEY_DOWN);
1120
1121   // Gestures with two fingers.
1122   left_swipe_gestures_[2] =
1123       BindKeyEventWithFlags(ui::VKEY_BROWSER_BACK, ui::EF_NONE);
1124   right_swipe_gestures_[2] =
1125       BindKeyEventWithFlags(ui::VKEY_BROWSER_FORWARD, ui::EF_NONE);
1126   // Jump to top.
1127   up_swipe_gestures_[2] = BindShiftSearchKeyEvent(ui::VKEY_A);
1128   // Read from here.
1129   down_swipe_gestures_[2] = BindShiftSearchKeyEvent(ui::VKEY_R);
1130
1131   // Gestures with three fingers switch tabs left/right and scroll up/down.
1132   left_swipe_gestures_[3] = BindKeyEventWithFlags(
1133       ui::VKEY_TAB, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN);
1134   right_swipe_gestures_[3] =
1135       BindKeyEventWithFlags(ui::VKEY_TAB, ui::EF_CONTROL_DOWN);
1136   up_swipe_gestures_[3] = BindKeyEventWithFlags(ui::VKEY_NEXT, ui::EF_NONE);
1137   down_swipe_gestures_[3] = BindKeyEventWithFlags(ui::VKEY_PRIOR, ui::EF_NONE);
1138
1139   // Gestures with four fingers should probably eventually be used for rare
1140   // needs that are hard to access through menus.
1141   // Note that brightness levels are here because they can be important for low
1142   // vision users. However, none of these mappings are permanent.
1143   left_swipe_gestures_[4] =
1144       BindKeyEventWithFlags(ui::VKEY_BRIGHTNESS_DOWN, ui::EF_NONE);
1145   right_swipe_gestures_[4] =
1146       BindKeyEventWithFlags(VKEY_BRIGHTNESS_UP, ui::EF_NONE);
1147   up_swipe_gestures_[4] = BindKeyEventWithFlags(VKEY_BROWSER_HOME, ui::EF_NONE);
1148   down_swipe_gestures_[4] =
1149       BindKeyEventWithFlags(VKEY_BROWSER_REFRESH, ui::EF_NONE);
1150 }
1151
1152 float TouchExplorationController::GetSplitTapTouchSlop() {
1153   return gesture_detector_config_.touch_slop * 3;
1154 }
1155
1156 }  // namespace ui