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