OLD | NEW |
| (Empty) |
1 // Copyright 2016 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_accessibility_enabler.h" | |
6 | |
7 #include <math.h> | |
8 | |
9 #include <utility> | |
10 | |
11 #include "base/logging.h" | |
12 #include "base/metrics/user_metrics.h" | |
13 #include "base/time/default_tick_clock.h" | |
14 #include "ui/aura/window.h" | |
15 #include "ui/aura/window_event_dispatcher.h" | |
16 #include "ui/aura/window_tree_host.h" | |
17 #include "ui/events/event.h" | |
18 #include "ui/events/event_processor.h" | |
19 #include "ui/events/event_utils.h" | |
20 | |
21 namespace ui { | |
22 | |
23 namespace { | |
24 | |
25 // Delay between timer callbacks. Each one plays a tick sound. | |
26 constexpr int kTimerDelayInMS = 500; | |
27 | |
28 // The number of ticks of the timer before the first sound is generated. | |
29 constexpr int kTimerTicksOfFirstSoundFeedback = 6; | |
30 | |
31 // The number of ticks of the timer before toggling spoken feedback. | |
32 constexpr int kTimerTicksToToggleSpokenFeedback = 10; | |
33 | |
34 } // namespace | |
35 | |
36 TouchAccessibilityEnabler::TouchAccessibilityEnabler( | |
37 aura::Window* root_window, | |
38 TouchAccessibilityEnablerDelegate* delegate) | |
39 : root_window_(root_window), | |
40 delegate_(delegate), | |
41 state_(NO_FINGERS_DOWN), | |
42 tick_clock_(NULL) { | |
43 DCHECK(root_window); | |
44 DCHECK(delegate); | |
45 root_window_->AddPreTargetHandler(this); | |
46 } | |
47 | |
48 TouchAccessibilityEnabler::~TouchAccessibilityEnabler() { | |
49 root_window_->RemovePreTargetHandler(this); | |
50 } | |
51 | |
52 void TouchAccessibilityEnabler::OnTouchEvent(ui::TouchEvent* event) { | |
53 // Skip events rewritten by TouchExplorationController, it will hand | |
54 // us the unrewritten events directly. | |
55 if (!(event->flags() & ui::EF_TOUCH_ACCESSIBILITY)) | |
56 HandleTouchEvent(*event); | |
57 } | |
58 | |
59 void TouchAccessibilityEnabler::HandleTouchEvent(const ui::TouchEvent& event) { | |
60 DCHECK(!(event.flags() & ui::EF_TOUCH_ACCESSIBILITY)); | |
61 const ui::EventType type = event.type(); | |
62 const gfx::PointF& location = event.location_f(); | |
63 const int touch_id = event.touch_id(); | |
64 | |
65 if (type == ui::ET_TOUCH_PRESSED) { | |
66 touch_locations_.insert(std::pair<int, gfx::PointF>(touch_id, location)); | |
67 } else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) { | |
68 auto iter = touch_locations_.find(touch_id); | |
69 | |
70 // Can happen if this object is constructed while fingers were down. | |
71 if (iter == touch_locations_.end()) | |
72 return; | |
73 | |
74 touch_locations_.erase(touch_id); | |
75 } else if (type == ui::ET_TOUCH_MOVED) { | |
76 auto iter = touch_locations_.find(touch_id); | |
77 | |
78 // Can happen if this object is constructed while fingers were down. | |
79 if (iter == touch_locations_.end()) | |
80 return; | |
81 | |
82 float delta = (location - iter->second).Length(); | |
83 if (delta > gesture_detector_config_.double_tap_slop) { | |
84 state_ = WAIT_FOR_NO_FINGERS; | |
85 CancelTimer(); | |
86 return; | |
87 } | |
88 } else { | |
89 NOTREACHED() << "Unexpected event type received: " << event.name(); | |
90 return; | |
91 } | |
92 | |
93 if (touch_locations_.size() == 0) { | |
94 state_ = NO_FINGERS_DOWN; | |
95 CancelTimer(); | |
96 return; | |
97 } | |
98 | |
99 if (touch_locations_.size() > 2) { | |
100 state_ = WAIT_FOR_NO_FINGERS; | |
101 CancelTimer(); | |
102 return; | |
103 } | |
104 | |
105 if (state_ == NO_FINGERS_DOWN && event.type() == ui::ET_TOUCH_PRESSED) { | |
106 state_ = ONE_FINGER_DOWN; | |
107 } else if (state_ == ONE_FINGER_DOWN && | |
108 event.type() == ui::ET_TOUCH_PRESSED) { | |
109 state_ = TWO_FINGERS_DOWN; | |
110 two_finger_start_time_ = Now(); | |
111 StartTimer(); | |
112 } | |
113 } | |
114 | |
115 base::TimeTicks TouchAccessibilityEnabler::Now() { | |
116 if (tick_clock_) { | |
117 // This is the same as what EventTimeForNow() does, but here we do it | |
118 // with a clock that can be replaced with a simulated clock for tests. | |
119 return tick_clock_->NowTicks(); | |
120 } | |
121 return ui::EventTimeForNow(); | |
122 } | |
123 | |
124 void TouchAccessibilityEnabler::StartTimer() { | |
125 if (timer_.IsRunning()) | |
126 return; | |
127 | |
128 timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kTimerDelayInMS), | |
129 this, &ui::TouchAccessibilityEnabler::OnTimer); | |
130 } | |
131 | |
132 void TouchAccessibilityEnabler::CancelTimer() { | |
133 if (timer_.IsRunning()) | |
134 timer_.Stop(); | |
135 } | |
136 | |
137 void TouchAccessibilityEnabler::OnTimer() { | |
138 base::TimeTicks now = Now(); | |
139 double tick_count_f = | |
140 (now - two_finger_start_time_).InMillisecondsF() / kTimerDelayInMS; | |
141 int tick_count = roundf(tick_count_f); | |
142 | |
143 if (tick_count == kTimerTicksOfFirstSoundFeedback) { | |
144 base::RecordAction( | |
145 base::UserMetricsAction("Accessibility.TwoFingersHeldDown")); | |
146 } | |
147 | |
148 if (tick_count >= kTimerTicksOfFirstSoundFeedback && | |
149 tick_count < kTimerTicksToToggleSpokenFeedback) { | |
150 delegate_->PlaySpokenFeedbackToggleCountdown(tick_count); | |
151 } | |
152 if (tick_count == kTimerTicksToToggleSpokenFeedback) { | |
153 delegate_->ToggleSpokenFeedback(); | |
154 state_ = WAIT_FOR_NO_FINGERS; | |
155 } | |
156 } | |
157 | |
158 } // namespace ui | |
OLD | NEW |