Home | History | Annotate | Download | only in chromeos
      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   ui::EventDispatchDetails result ALLOW_UNUSED =
    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 const float TouchExplorationController::GetSplitTapTouchSlop() {
   1153   return gesture_detector_config_.touch_slop * 3;
   1154 }
   1155 
   1156 }  // namespace ui
   1157