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 #ifndef UI_CHROMEOS_TOUCH_EXPLORATION_CONTROLLER_H_
      6 #define UI_CHROMEOS_TOUCH_EXPLORATION_CONTROLLER_H_
      7 
      8 #include "base/timer/timer.h"
      9 #include "base/values.h"
     10 #include "ui/chromeos/ui_chromeos_export.h"
     11 #include "ui/events/event.h"
     12 #include "ui/events/event_rewriter.h"
     13 #include "ui/events/gesture_detection/gesture_detector.h"
     14 #include "ui/gfx/geometry/point.h"
     15 
     16 namespace aura {
     17 class Window;
     18 }
     19 
     20 namespace ui {
     21 
     22 class Event;
     23 class EventHandler;
     24 class TouchEvent;
     25 
     26 // TouchExplorationController is used in tandem with "Spoken Feedback" to
     27 // make the touch UI accessible.
     28 //
     29 // ** Short version **
     30 //
     31 // At a high-level, single-finger events are used for accessibility -
     32 // exploring the screen gets turned into mouse moves (which can then be
     33 // spoken by an accessibility service running), a double-tap simulates a
     34 // click, and gestures can be used to send high-level accessibility commands.
     35 // When two or more fingers are pressed initially, from then on the events
     36 // are passed through, but with the initial finger removed - so if you swipe
     37 // down with two fingers, the running app will see a one-finger swipe.
     38 //
     39 // ** Long version **
     40 //
     41 // Here are the details of the implementation:
     42 //
     43 // When the first touch is pressed, a 300 ms grace period timer starts.
     44 //
     45 // If the user keeps their finger down for more than 300 ms and doesn't
     46 // perform a supported accessibility gesture in that time (e.g. swipe right),
     47 // they enter touch exploration mode, and all movements are translated into
     48 // synthesized mouse move events.
     49 //
     50 // Also, if the user moves their single finger outside a certain slop region
     51 // (without performing a gesture), they enter touch exploration mode earlier
     52 // than 300 ms.
     53 //
     54 // If the user taps and releases their finger, after 300 ms from the initial
     55 // touch, a single mouse move is fired.
     56 //
     57 // If the user double-taps, the second tap is passed through, allowing the
     58 // user to click - however, the double-tap location is changed to the location
     59 // of the last successful touch exploration - that allows the user to explore
     60 // anywhere on the screen, hear its description, then double-tap anywhere
     61 // to activate it.
     62 //
     63 // If the user enters touch exploration mode, they can click without lifting
     64 // their touch exploration finger by tapping anywhere else on the screen with
     65 // a second finger, while the touch exploration finger is still pressed.
     66 //
     67 // If the user adds a second finger during the grace period, they enter
     68 // passthrough mode. In this mode, the first finger is ignored but all
     69 // additional touch events are mostly passed through unmodified. So a
     70 // two-finger scroll gets passed through as a one-finger scroll. However,
     71 // once in passthrough mode, if one finger is released, the remaining fingers
     72 // continue to pass through events, allowing the user to start a scroll
     73 // with two fingers but finish it with one. Sometimes this requires rewriting
     74 // the touch ids.
     75 //
     76 // Once either touch exploration or passthrough mode has been activated,
     77 // it remains in that mode until all fingers have been released.
     78 //
     79 // The caller is expected to retain ownership of instances of this class and
     80 // destroy them before |root_window| is destroyed.
     81 class UI_CHROMEOS_EXPORT TouchExplorationController :
     82     public ui::EventRewriter {
     83  public:
     84   explicit TouchExplorationController(aura::Window* root_window);
     85   virtual ~TouchExplorationController();
     86 
     87   void CallTapTimerNowForTesting();
     88   void SetEventHandlerForTesting(ui::EventHandler* event_handler_for_testing);
     89   bool IsInNoFingersDownStateForTesting() const;
     90 
     91  private:
     92   // Overridden from ui::EventRewriter
     93   virtual ui::EventRewriteStatus RewriteEvent(
     94       const ui::Event& event,
     95       scoped_ptr<ui::Event>* rewritten_event) OVERRIDE;
     96   virtual ui::EventRewriteStatus NextDispatchEvent(
     97       const ui::Event& last_event, scoped_ptr<ui::Event>* new_event) OVERRIDE;
     98 
     99   // Event handlers based on the current state - see State, below.
    100   ui::EventRewriteStatus InNoFingersDown(
    101       const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event);
    102   ui::EventRewriteStatus InSingleTapPressed(
    103       const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event);
    104   ui::EventRewriteStatus InSingleTapReleased(
    105       const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event);
    106   ui::EventRewriteStatus InDoubleTapPressed(
    107       const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event);
    108   ui::EventRewriteStatus InTouchExploration(
    109       const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event);
    110   ui::EventRewriteStatus InPassthroughMinusOne(
    111       const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event);
    112   ui::EventRewriteStatus InTouchExploreSecondPress(
    113       const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event);
    114   // This timer is started every time we get the first press event, and
    115   // it fires after the double-click timeout elapses (300 ms by default).
    116   // If the user taps and releases within 300 ms and doesn't press again,
    117   // we treat that as a single mouse move (touch exploration) event.
    118   void OnTapTimerFired();
    119 
    120   // Dispatch a new event outside of the event rewriting flow.
    121   void DispatchEvent(ui::Event* event);
    122 
    123   scoped_ptr<ui::Event> CreateMouseMoveEvent(const gfx::PointF& location,
    124                                              int flags);
    125 
    126   void EnterTouchToMouseMode();
    127 
    128   // Set the state to NO_FINGERS_DOWN and reset any other fields to their
    129   // default value.
    130   void ResetToNoFingersDown();
    131 
    132   enum State {
    133     // No fingers are down and no events are pending.
    134     NO_FINGERS_DOWN,
    135 
    136     // A single finger is down, but we're not yet sure if this is going
    137     // to be touch exploration or something else.
    138     SINGLE_TAP_PRESSED,
    139 
    140     // The user pressed and released a single finger - a tap - but we have
    141     // to wait until the end of the grace period to allow the user to tap the
    142     // second time. If the second tap doesn't occurs within the grace period,
    143     // we dispatch a mouse move at the location of the first tap.
    144     SINGLE_TAP_RELEASED,
    145 
    146     // The user tapped once, and before the grace period expired, pressed
    147     // one finger down to begin a double-tap, but has not released it yet.
    148     DOUBLE_TAP_PRESSED,
    149 
    150     // We're in touch exploration mode. Anything other than the first finger
    151     // is ignored, and movements of the first finger are rewritten as mouse
    152     // move events. This mode is entered if a single finger is pressed and
    153     // after the grace period the user hasn't added a second finger or
    154     // moved the finger outside of the slop region. We'll stay in this
    155     // mode until all fingers are lifted.
    156     TOUCH_EXPLORATION,
    157 
    158     // The user placed two or more fingers down within the grace period.
    159     // We're now in passthrough mode until all fingers are lifted. Initially
    160     // the first finger is ignored and other fingers are passed through
    161     // as-is. If a finger other than the initial one is the first to be
    162     // released, we rewrite the first finger with the touch id of the finger
    163     // that was released, from now on. The motivation for this is that if
    164     // the user starts a scroll with 2 fingers, they can release either one
    165     // and continue the scrolling.
    166     PASSTHROUGH_MINUS_ONE,
    167 
    168     // The user was in touch exploration, but has placed down another finger.
    169     // If the user releases the second finger, a touch press and release
    170     // will go through at the last touch explore location. If the user
    171     // releases the touch explore finger, the other finger will continue with
    172     // touch explore. Any fingers pressed past the first two are ignored.
    173     TOUCH_EXPLORE_SECOND_PRESS,
    174   };
    175 
    176   void VlogState(const char* function_name);
    177 
    178   void VlogEvent(const ui::TouchEvent& event, const char* function_name);
    179 
    180   // Gets enum name from integer value.
    181   const char* EnumStateToString(State state);
    182 
    183   std::string EnumEventTypeToString(ui::EventType type);
    184 
    185   aura::Window* root_window_;
    186 
    187   // A set of touch ids for fingers currently touching the screen.
    188   std::vector<int> current_touch_ids_;
    189 
    190   // Map of touch ids to their last known location.
    191   std::map<int, gfx::PointF> touch_locations_;
    192 
    193   // The touch id that any events on the initial finger should be rewritten
    194   // as in passthrough-minus-one mode. If kTouchIdUnassigned, events on the
    195   // initial finger are discarded. If kTouchIdNone, the initial finger
    196   // has been released and no more rewriting will be done.
    197   int initial_touch_id_passthrough_mapping_;
    198 
    199   // The current state.
    200   State state_;
    201 
    202   // A copy of the event from the initial touch press.
    203   scoped_ptr<ui::TouchEvent> initial_press_;
    204 
    205   // The last synthesized mouse move event. When the user double-taps,
    206   // we send the passed-through tap to the location of this event.
    207   scoped_ptr<ui::TouchEvent> last_touch_exploration_;
    208 
    209   // A timer to fire the mouse move event after the double-tap delay.
    210   base::OneShotTimer<TouchExplorationController> tap_timer_;
    211 
    212   // For testing only, an event handler to use for generated events
    213   // outside of the normal event rewriting flow.
    214   ui::EventHandler* event_handler_for_testing_;
    215 
    216   // A default gesture detector config, so we can share the same
    217   // timeout and pixel slop constants.
    218   ui::GestureDetector::Config gesture_detector_config_;
    219 
    220   // The previous state entered.
    221   State prev_state_;
    222 
    223   // A copy of the previous event passed.
    224   scoped_ptr<ui::TouchEvent> prev_event_;
    225 
    226   DISALLOW_COPY_AND_ASSIGN(TouchExplorationController);
    227 };
    228 
    229 }  // namespace ui
    230 
    231 #endif  // UI_CHROMEOS_TOUCH_EXPLORATION_CONTROLLER_H_
    232