OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ui/chromeos/touch_exploration_controller.h" | 5 #include "ui/chromeos/touch_exploration_controller.h" |
6 | 6 |
7 #include "base/test/simple_test_tick_clock.h" | 7 #include "base/test/simple_test_tick_clock.h" |
8 #include "base/time/time.h" | 8 #include "base/time/time.h" |
9 #include "ui/aura/client/cursor_client.h" | 9 #include "ui/aura/client/cursor_client.h" |
10 #include "ui/aura/test/aura_test_base.h" | 10 #include "ui/aura/test/aura_test_base.h" |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
68 if (n <= 0) | 68 if (n <= 0) |
69 return 0; | 69 return 0; |
70 if (n == 1) | 70 if (n == 1) |
71 return 1; | 71 return 1; |
72 return n * Factorial(n - 1); | 72 return n * Factorial(n - 1); |
73 } | 73 } |
74 | 74 |
75 class MockTouchExplorationControllerDelegate | 75 class MockTouchExplorationControllerDelegate |
76 : public ui::TouchExplorationControllerDelegate { | 76 : public ui::TouchExplorationControllerDelegate { |
77 public: | 77 public: |
78 virtual void PlayVolumeAdjustSound() OVERRIDE { | |
79 ++num_times_adjust_sound_played_; | |
80 } | |
81 virtual void SetOutputLevel(int volume) OVERRIDE { | 78 virtual void SetOutputLevel(int volume) OVERRIDE { |
82 volume_changes_.push_back(volume); | 79 volume_changes_.push_back(volume); |
83 } | 80 } |
84 virtual void SilenceSpokenFeedback() OVERRIDE { | 81 virtual void SilenceSpokenFeedback() OVERRIDE { |
85 } | 82 } |
| 83 virtual void PlayVolumeAdjustEarcon() OVERRIDE { |
| 84 ++num_times_adjust_sound_played_; |
| 85 } |
| 86 virtual void PlayPassthroughEarcon() OVERRIDE { |
| 87 ++num_times_passthrough_played_; |
| 88 } |
| 89 virtual void PlayExitScreenEarcon() OVERRIDE { |
| 90 ++num_times_exit_screen_played_; |
| 91 } |
| 92 virtual void PlayEnterScreenEarcon() OVERRIDE { |
| 93 ++num_times_enter_screen_played_; |
| 94 } |
86 | 95 |
87 const std::vector<float> VolumeChanges() { return volume_changes_; } | 96 const std::vector<float> VolumeChanges() { return volume_changes_; } |
88 const size_t NumAdjustSounds() { return num_times_adjust_sound_played_; } | 97 const size_t NumAdjustSounds() { return num_times_adjust_sound_played_; } |
| 98 const size_t NumPassthroughSounds() { return num_times_passthrough_played_; } |
| 99 const size_t NumExitScreenSounds() { return num_times_exit_screen_played_; } |
| 100 const size_t NumEnterScreenSounds() { |
| 101 return num_times_enter_screen_played_; |
| 102 } |
| 103 |
| 104 void ResetCountersToZero() { |
| 105 num_times_adjust_sound_played_ = 0; |
| 106 num_times_passthrough_played_ = 0; |
| 107 num_times_exit_screen_played_ = 0; |
| 108 num_times_enter_screen_played_ = 0; |
| 109 } |
89 | 110 |
90 private: | 111 private: |
91 std::vector<float> volume_changes_; | 112 std::vector<float> volume_changes_; |
92 size_t num_times_adjust_sound_played_ = 0; | 113 size_t num_times_adjust_sound_played_ = 0; |
| 114 size_t num_times_passthrough_played_ = 0; |
| 115 size_t num_times_exit_screen_played_ = 0; |
| 116 size_t num_times_enter_screen_played_ = 0; |
93 }; | 117 }; |
94 | 118 |
95 } // namespace | 119 } // namespace |
96 | 120 |
97 class TouchExplorationControllerTestApi { | 121 class TouchExplorationControllerTestApi { |
98 public: | 122 public: |
99 TouchExplorationControllerTestApi( | 123 TouchExplorationControllerTestApi( |
100 TouchExplorationController* touch_exploration_controller) { | 124 TouchExplorationController* touch_exploration_controller) { |
101 touch_exploration_controller_.reset(touch_exploration_controller); | 125 touch_exploration_controller_.reset(touch_exploration_controller); |
102 } | 126 } |
103 | 127 |
104 void CallTapTimerNowForTesting() { | 128 void CallTapTimerNowForTesting() { |
105 DCHECK(touch_exploration_controller_->tap_timer_.IsRunning()); | 129 DCHECK(touch_exploration_controller_->tap_timer_.IsRunning()); |
106 touch_exploration_controller_->tap_timer_.Stop(); | 130 touch_exploration_controller_->tap_timer_.Stop(); |
107 touch_exploration_controller_->OnTapTimerFired(); | 131 touch_exploration_controller_->OnTapTimerFired(); |
108 } | 132 } |
109 | 133 |
| 134 void CallPassthroughTimerNowForTesting() { |
| 135 DCHECK(touch_exploration_controller_->passthrough_timer_.IsRunning()); |
| 136 touch_exploration_controller_->passthrough_timer_.Stop(); |
| 137 touch_exploration_controller_->OnPassthroughTimerFired(); |
| 138 } |
| 139 |
110 void CallTapTimerNowIfRunningForTesting() { | 140 void CallTapTimerNowIfRunningForTesting() { |
111 if (touch_exploration_controller_->tap_timer_.IsRunning()) { | 141 if (touch_exploration_controller_->tap_timer_.IsRunning()) { |
112 touch_exploration_controller_->tap_timer_.Stop(); | 142 touch_exploration_controller_->tap_timer_.Stop(); |
113 touch_exploration_controller_->OnTapTimerFired(); | 143 touch_exploration_controller_->OnTapTimerFired(); |
114 } | 144 } |
115 } | 145 } |
116 | 146 |
117 bool IsInNoFingersDownStateForTesting() const { | 147 bool IsInNoFingersDownStateForTesting() const { |
118 return touch_exploration_controller_->state_ == | 148 return touch_exploration_controller_->state_ == |
119 touch_exploration_controller_->NO_FINGERS_DOWN; | 149 touch_exploration_controller_->NO_FINGERS_DOWN; |
120 } | 150 } |
121 | 151 |
122 bool IsInGestureInProgressStateForTesting() const { | 152 bool IsInGestureInProgressStateForTesting() const { |
123 return touch_exploration_controller_->state_ == | 153 return touch_exploration_controller_->state_ == |
124 touch_exploration_controller_->GESTURE_IN_PROGRESS; | 154 touch_exploration_controller_->GESTURE_IN_PROGRESS; |
125 } | 155 } |
126 | 156 |
127 bool IsInSlideGestureStateForTesting() const { | 157 bool IsInSlideGestureStateForTesting() const { |
128 return touch_exploration_controller_->state_ == | 158 return touch_exploration_controller_->state_ == |
129 touch_exploration_controller_->SLIDE_GESTURE; | 159 touch_exploration_controller_->SLIDE_GESTURE; |
130 } | 160 } |
131 | 161 |
132 bool IsInTwoFingerTapStateForTesting() const { | 162 bool IsInTwoFingerTapStateForTesting() const { |
133 return touch_exploration_controller_->state_ == | 163 return touch_exploration_controller_->state_ == |
134 touch_exploration_controller_->TWO_FINGER_TAP; | 164 touch_exploration_controller_->TWO_FINGER_TAP; |
135 } | 165 } |
| 166 bool IsInCornerPassthroughStateForTesting() const { |
| 167 return touch_exploration_controller_->state_ == |
| 168 touch_exploration_controller_->CORNER_PASSTHROUGH; |
| 169 } |
136 | 170 |
137 gfx::Rect BoundsOfRootWindowInDIPForTesting() const { | 171 gfx::Rect BoundsOfRootWindowInDIPForTesting() const { |
138 return touch_exploration_controller_->root_window_->GetBoundsInScreen(); | 172 return touch_exploration_controller_->root_window_->GetBoundsInScreen(); |
139 } | 173 } |
140 | 174 |
141 // VLOGs should be suppressed in tests that generate a lot of logs, | 175 // VLOGs should be suppressed in tests that generate a lot of logs, |
142 // for example permutations of nine touch events. | 176 // for example permutations of nine touch events. |
143 void SuppressVLOGsForTesting(bool suppress) { | 177 void SuppressVLOGsForTesting(bool suppress) { |
144 touch_exploration_controller_->VLOG_on_ = !suppress; | 178 touch_exploration_controller_->VLOG_on_ = !suppress; |
145 } | 179 } |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
233 void ClearCapturedEvents() { | 267 void ClearCapturedEvents() { |
234 event_capturer_.Reset(); | 268 event_capturer_.Reset(); |
235 } | 269 } |
236 | 270 |
237 void AdvanceSimulatedTimePastTapDelay() { | 271 void AdvanceSimulatedTimePastTapDelay() { |
238 simulated_clock_->Advance(gesture_detector_config_.double_tap_timeout); | 272 simulated_clock_->Advance(gesture_detector_config_.double_tap_timeout); |
239 simulated_clock_->Advance(base::TimeDelta::FromMilliseconds(1)); | 273 simulated_clock_->Advance(base::TimeDelta::FromMilliseconds(1)); |
240 touch_exploration_controller_->CallTapTimerNowForTesting(); | 274 touch_exploration_controller_->CallTapTimerNowForTesting(); |
241 } | 275 } |
242 | 276 |
| 277 void AdvanceSimulatedTimePastPassthroughDelay() { |
| 278 simulated_clock_->Advance(base::TimeDelta::FromMilliseconds(1000)); |
| 279 touch_exploration_controller_->CallPassthroughTimerNowForTesting(); |
| 280 } |
| 281 |
243 void AdvanceSimulatedTimePastPotentialTapDelay() { | 282 void AdvanceSimulatedTimePastPotentialTapDelay() { |
244 simulated_clock_->Advance(base::TimeDelta::FromMilliseconds(1000)); | 283 simulated_clock_->Advance(base::TimeDelta::FromMilliseconds(1000)); |
245 touch_exploration_controller_->CallTapTimerNowIfRunningForTesting(); | 284 touch_exploration_controller_->CallTapTimerNowIfRunningForTesting(); |
246 } | 285 } |
247 | 286 |
248 void SuppressVLOGs(bool suppress) { | 287 void SuppressVLOGs(bool suppress) { |
249 touch_exploration_controller_->SuppressVLOGsForTesting(suppress); | 288 touch_exploration_controller_->SuppressVLOGsForTesting(suppress); |
250 } | 289 } |
251 | 290 |
252 void SetTickClock() { | 291 void SetTickClock() { |
(...skipping 13 matching lines...) Expand all Loading... |
266 } | 305 } |
267 } | 306 } |
268 | 307 |
269 void EnterTouchExplorationModeAtLocation(gfx::Point tap_location) { | 308 void EnterTouchExplorationModeAtLocation(gfx::Point tap_location) { |
270 ui::TouchEvent touch_press(ui::ET_TOUCH_PRESSED, tap_location, 0, Now()); | 309 ui::TouchEvent touch_press(ui::ET_TOUCH_PRESSED, tap_location, 0, Now()); |
271 generator_->Dispatch(&touch_press); | 310 generator_->Dispatch(&touch_press); |
272 AdvanceSimulatedTimePastTapDelay(); | 311 AdvanceSimulatedTimePastTapDelay(); |
273 EXPECT_TRUE(IsInTouchToMouseMode()); | 312 EXPECT_TRUE(IsInTouchToMouseMode()); |
274 } | 313 } |
275 | 314 |
| 315 // Checks that Corner Passthrough is working. Assumes that corner is the |
| 316 // bottom left corner or the bottom right corner. |
| 317 void AssertCornerPassthroughWorking(gfx::Point corner) { |
| 318 ASSERT_EQ(0U, delegate_.NumPassthroughSounds()); |
| 319 |
| 320 ui::TouchEvent first_press(ui::ET_TOUCH_PRESSED, corner, 0, Now()); |
| 321 generator_->Dispatch(&first_press); |
| 322 |
| 323 AdvanceSimulatedTimePastPassthroughDelay(); |
| 324 EXPECT_FALSE(IsInGestureInProgressState()); |
| 325 EXPECT_FALSE(IsInSlideGestureState()); |
| 326 EXPECT_FALSE(IsInTouchToMouseMode()); |
| 327 EXPECT_TRUE(IsInCornerPassthroughState()); |
| 328 |
| 329 gfx::Rect window = BoundsOfRootWindowInDIP(); |
| 330 // The following events should be passed through. |
| 331 gfx::Point passthrough(window.right() / 2, window.bottom() / 2); |
| 332 ui::TouchEvent passthrough_press( |
| 333 ui::ET_TOUCH_PRESSED, passthrough, 1, Now()); |
| 334 ASSERT_EQ(1U, delegate_.NumPassthroughSounds()); |
| 335 generator_->Dispatch(&passthrough_press); |
| 336 generator_->ReleaseTouchId(1); |
| 337 generator_->PressTouchId(1); |
| 338 EXPECT_FALSE(IsInGestureInProgressState()); |
| 339 EXPECT_FALSE(IsInSlideGestureState()); |
| 340 EXPECT_TRUE(IsInCornerPassthroughState()); |
| 341 |
| 342 std::vector<ui::LocatedEvent*> captured_events = GetCapturedLocatedEvents(); |
| 343 ASSERT_EQ(3U, captured_events.size()); |
| 344 EXPECT_EQ(ui::ET_TOUCH_PRESSED, captured_events[0]->type()); |
| 345 EXPECT_EQ(ui::ET_TOUCH_RELEASED, captured_events[1]->type()); |
| 346 EXPECT_EQ(ui::ET_TOUCH_PRESSED, captured_events[2]->type()); |
| 347 generator_->ReleaseTouchId(1); |
| 348 ClearCapturedEvents(); |
| 349 |
| 350 generator_->ReleaseTouchId(0); |
| 351 captured_events = GetCapturedLocatedEvents(); |
| 352 ASSERT_EQ(0U, captured_events.size()); |
| 353 EXPECT_FALSE(IsInTouchToMouseMode()); |
| 354 EXPECT_FALSE(IsInCornerPassthroughState()); |
| 355 ClearCapturedEvents(); |
| 356 } |
| 357 |
276 bool IsInTouchToMouseMode() { | 358 bool IsInTouchToMouseMode() { |
277 aura::client::CursorClient* cursor_client = | 359 aura::client::CursorClient* cursor_client = |
278 aura::client::GetCursorClient(root_window()); | 360 aura::client::GetCursorClient(root_window()); |
279 return cursor_client && | 361 return cursor_client && |
280 cursor_client->IsMouseEventsEnabled() && | 362 cursor_client->IsMouseEventsEnabled() && |
281 !cursor_client->IsCursorVisible(); | 363 !cursor_client->IsCursorVisible(); |
282 } | 364 } |
283 | 365 |
284 bool IsInNoFingersDownState() { | 366 bool IsInNoFingersDownState() { |
285 return touch_exploration_controller_->IsInNoFingersDownStateForTesting(); | 367 return touch_exploration_controller_->IsInNoFingersDownStateForTesting(); |
286 } | 368 } |
287 | 369 |
288 bool IsInGestureInProgressState() { | 370 bool IsInGestureInProgressState() { |
289 return touch_exploration_controller_ | 371 return touch_exploration_controller_ |
290 ->IsInGestureInProgressStateForTesting(); | 372 ->IsInGestureInProgressStateForTesting(); |
291 } | 373 } |
292 | 374 |
293 bool IsInSlideGestureState() { | 375 bool IsInSlideGestureState() { |
294 return touch_exploration_controller_->IsInSlideGestureStateForTesting(); | 376 return touch_exploration_controller_->IsInSlideGestureStateForTesting(); |
295 } | 377 } |
296 | 378 |
297 bool IsInTwoFingerTapState() { | 379 bool IsInTwoFingerTapState() { |
298 return touch_exploration_controller_->IsInTwoFingerTapStateForTesting(); | 380 return touch_exploration_controller_->IsInTwoFingerTapStateForTesting(); |
299 } | 381 } |
300 | 382 |
| 383 bool IsInCornerPassthroughState() { |
| 384 return touch_exploration_controller_ |
| 385 ->IsInCornerPassthroughStateForTesting(); |
| 386 } |
| 387 |
301 gfx::Rect BoundsOfRootWindowInDIP() { | 388 gfx::Rect BoundsOfRootWindowInDIP() { |
302 return touch_exploration_controller_->BoundsOfRootWindowInDIPForTesting(); | 389 return touch_exploration_controller_->BoundsOfRootWindowInDIPForTesting(); |
303 } | 390 } |
304 | 391 |
305 float GetMaxDistanceFromEdge() const{ | 392 float GetMaxDistanceFromEdge() const { |
306 return touch_exploration_controller_->GetMaxDistanceFromEdge(); | 393 return touch_exploration_controller_->GetMaxDistanceFromEdge(); |
307 } | 394 } |
308 | 395 |
309 float GetSlopDistanceFromEdge() const{ | 396 float GetSlopDistanceFromEdge() const { |
310 return touch_exploration_controller_->GetSlopDistanceFromEdge(); | 397 return touch_exploration_controller_->GetSlopDistanceFromEdge(); |
311 } | 398 } |
312 | 399 |
313 base::TimeDelta Now() { | 400 base::TimeDelta Now() { |
314 // This is the same as what EventTimeForNow() does, but here we do it | 401 // This is the same as what EventTimeForNow() does, but here we do it |
315 // with our simulated clock. | 402 // with our simulated clock. |
316 return base::TimeDelta::FromInternalValue( | 403 return base::TimeDelta::FromInternalValue( |
317 simulated_clock_->NowTicks().ToInternalValue()); | 404 simulated_clock_->NowTicks().ToInternalValue()); |
318 } | 405 } |
319 | 406 |
(...skipping 1369 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1689 generator_->Dispatch(&first_press_id_2); | 1776 generator_->Dispatch(&first_press_id_2); |
1690 EXPECT_TRUE(IsInTwoFingerTapState()); | 1777 EXPECT_TRUE(IsInTwoFingerTapState()); |
1691 | 1778 |
1692 const ScopedVector<ui::Event>& captured_events = GetCapturedEvents(); | 1779 const ScopedVector<ui::Event>& captured_events = GetCapturedEvents(); |
1693 ASSERT_EQ(0U, captured_events.size()); | 1780 ASSERT_EQ(0U, captured_events.size()); |
1694 | 1781 |
1695 generator_->Dispatch(&out_slop_id_2); | 1782 generator_->Dispatch(&out_slop_id_2); |
1696 EXPECT_FALSE(IsInTwoFingerTapState()); | 1783 EXPECT_FALSE(IsInTwoFingerTapState()); |
1697 } | 1784 } |
1698 | 1785 |
| 1786 // Corner passthrough should turn on if the user first holds down on either the |
| 1787 // right or left corner past a delay and then places a finger anywhere else on |
| 1788 // the screen. |
| 1789 TEST_F(TouchExplorationTest, ActivateLeftCornerPassthrough) { |
| 1790 SwitchTouchExplorationMode(true); |
| 1791 |
| 1792 gfx::Rect window = BoundsOfRootWindowInDIP(); |
| 1793 gfx::Point left_corner(10, window.bottom() - GetMaxDistanceFromEdge() / 2); |
| 1794 AssertCornerPassthroughWorking(left_corner); |
| 1795 } |
| 1796 |
| 1797 TEST_F(TouchExplorationTest, ActivateRightCornerPassthrough) { |
| 1798 SwitchTouchExplorationMode(true); |
| 1799 |
| 1800 gfx::Rect window = BoundsOfRootWindowInDIP(); |
| 1801 gfx::Point right_corner(window.right() - GetMaxDistanceFromEdge() / 2, |
| 1802 window.bottom() - GetMaxDistanceFromEdge() / 2); |
| 1803 AssertCornerPassthroughWorking(right_corner); |
| 1804 } |
| 1805 |
| 1806 // Earcons should play if the user slides off the screen or enters the screen |
| 1807 // from the edge. |
| 1808 TEST_F(TouchExplorationTest, EnterEarconPlays) { |
| 1809 SwitchTouchExplorationMode(true); |
| 1810 |
| 1811 gfx::Rect window = BoundsOfRootWindowInDIP(); |
| 1812 |
| 1813 gfx::Point upper_left_corner(0, 0); |
| 1814 gfx::Point upper_right_corner(window.right(), 0); |
| 1815 gfx::Point lower_left_corner(0, window.bottom()); |
| 1816 gfx::Point lower_right_corner(window.right(), window.bottom()); |
| 1817 gfx::Point left_edge(0, 30); |
| 1818 gfx::Point right_edge(window.right(), 30); |
| 1819 gfx::Point top_edge(30, 0); |
| 1820 gfx::Point bottom_edge(30, window.bottom()); |
| 1821 |
| 1822 std::vector<gfx::Point> locations; |
| 1823 locations.push_back(upper_left_corner); |
| 1824 locations.push_back(upper_right_corner); |
| 1825 locations.push_back(lower_left_corner); |
| 1826 locations.push_back(lower_right_corner); |
| 1827 locations.push_back(left_edge); |
| 1828 locations.push_back(right_edge); |
| 1829 locations.push_back(top_edge); |
| 1830 locations.push_back(bottom_edge); |
| 1831 |
| 1832 for (std::vector<gfx::Point>::const_iterator point = locations.begin(); |
| 1833 point != locations.end(); |
| 1834 ++point) { |
| 1835 ui::TouchEvent touch_event(ui::ET_TOUCH_PRESSED, *point, 1, Now()); |
| 1836 |
| 1837 generator_->Dispatch(&touch_event); |
| 1838 ASSERT_EQ(1U, delegate_.NumEnterScreenSounds()); |
| 1839 generator_->ReleaseTouchId(1); |
| 1840 delegate_.ResetCountersToZero(); |
| 1841 } |
| 1842 } |
| 1843 |
| 1844 TEST_F(TouchExplorationTest, ExitEarconPlays) { |
| 1845 SwitchTouchExplorationMode(true); |
| 1846 |
| 1847 // On the device, it cannot actually tell if the finger has left the screen or |
| 1848 // not. If the finger has left the screen, it reads it as a release that |
| 1849 // occurred very close to the edge of the screen even if the finger is still |
| 1850 // technically touching the moniter. To simulate this, a release that occurs |
| 1851 // close to the edge is dispatched. |
| 1852 gfx::Point initial_press(100, 200); |
| 1853 gfx::Rect window = BoundsOfRootWindowInDIP(); |
| 1854 |
| 1855 gfx::Point upper_left_corner(0, 0); |
| 1856 gfx::Point upper_right_corner(window.right(), 0); |
| 1857 gfx::Point lower_left_corner(0, window.bottom()); |
| 1858 gfx::Point lower_right_corner(window.right(), window.bottom()); |
| 1859 gfx::Point left_edge(0, 30); |
| 1860 gfx::Point right_edge(window.right(), 30); |
| 1861 gfx::Point top_edge(30, 0); |
| 1862 gfx::Point bottom_edge(30, window.bottom()); |
| 1863 |
| 1864 std::vector<gfx::Point> locations; |
| 1865 locations.push_back(upper_left_corner); |
| 1866 locations.push_back(upper_right_corner); |
| 1867 locations.push_back(lower_left_corner); |
| 1868 locations.push_back(lower_right_corner); |
| 1869 locations.push_back(left_edge); |
| 1870 locations.push_back(right_edge); |
| 1871 locations.push_back(top_edge); |
| 1872 locations.push_back(bottom_edge); |
| 1873 |
| 1874 for (std::vector<gfx::Point>::const_iterator point = locations.begin(); |
| 1875 point != locations.end(); |
| 1876 ++point) { |
| 1877 generator_->PressTouch(); |
| 1878 generator_->MoveTouch(initial_press); |
| 1879 generator_->MoveTouch(*point); |
| 1880 generator_->ReleaseTouch(); |
| 1881 ASSERT_EQ(1U, delegate_.NumExitScreenSounds()); |
| 1882 delegate_.ResetCountersToZero(); |
| 1883 } |
| 1884 } |
| 1885 |
1699 } // namespace ui | 1886 } // namespace ui |
OLD | NEW |