touch_accessibility_enabler.cc 5.2 KB

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