| Index: ui/chromeos/touch_accessibility_enabler.cc
|
| diff --git a/ui/chromeos/touch_accessibility_enabler.cc b/ui/chromeos/touch_accessibility_enabler.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..675f396b08ec9b87654a3dda06eb868bc62a2412
|
| --- /dev/null
|
| +++ b/ui/chromeos/touch_accessibility_enabler.cc
|
| @@ -0,0 +1,147 @@
|
| +// Copyright 2016 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "ui/chromeos/touch_accessibility_enabler.h"
|
| +
|
| +#include <math.h>
|
| +
|
| +#include <utility>
|
| +
|
| +#include "base/logging.h"
|
| +#include "base/time/default_tick_clock.h"
|
| +#include "ui/aura/window.h"
|
| +#include "ui/aura/window_event_dispatcher.h"
|
| +#include "ui/aura/window_tree_host.h"
|
| +#include "ui/events/event.h"
|
| +#include "ui/events/event_processor.h"
|
| +#include "ui/events/event_utils.h"
|
| +
|
| +namespace ui {
|
| +
|
| +namespace {
|
| +
|
| +// Delay between timer callbacks. Each one plays a tick sound.
|
| +constexpr int kTimerDelayInMS = 500;
|
| +
|
| +// The number of ticks of the timer before toggling spoken feedback.
|
| +constexpr int kTimerTicksToToggleSpokenFeedback = 7;
|
| +
|
| +} // namespace
|
| +
|
| +TouchAccessibilityEnabler::TouchAccessibilityEnabler(
|
| + aura::Window* root_window,
|
| + TouchAccessibilityEnablerDelegate* delegate)
|
| + : root_window_(root_window),
|
| + delegate_(delegate),
|
| + state_(NO_FINGERS_DOWN),
|
| + tick_clock_(NULL) {
|
| + DCHECK(root_window);
|
| + DCHECK(delegate);
|
| + root_window_->AddPreTargetHandler(this);
|
| +}
|
| +
|
| +TouchAccessibilityEnabler::~TouchAccessibilityEnabler() {
|
| + root_window_->RemovePreTargetHandler(this);
|
| +}
|
| +
|
| +void TouchAccessibilityEnabler::OnTouchEvent(ui::TouchEvent* event) {
|
| + // Skip events rewritten by TouchExplorationController, it will hand
|
| + // us the unrewritten events directly.
|
| + if (!(event->flags() & ui::EF_TOUCH_ACCESSIBILITY))
|
| + HandleTouchEvent(*event);
|
| +}
|
| +
|
| +void TouchAccessibilityEnabler::HandleTouchEvent(const ui::TouchEvent& event) {
|
| + DCHECK(!(event.flags() & ui::EF_TOUCH_ACCESSIBILITY));
|
| + const ui::EventType type = event.type();
|
| + const gfx::PointF& location = event.location_f();
|
| + const int touch_id = event.touch_id();
|
| +
|
| + if (type == ui::ET_TOUCH_PRESSED) {
|
| + touch_locations_.insert(std::pair<int, gfx::PointF>(touch_id, location));
|
| + } else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
|
| + auto iter = touch_locations_.find(touch_id);
|
| +
|
| + // Can happen if this object is constructed while fingers were down.
|
| + if (iter == touch_locations_.end())
|
| + return;
|
| +
|
| + touch_locations_.erase(touch_id);
|
| + } else if (type == ui::ET_TOUCH_MOVED) {
|
| + auto iter = touch_locations_.find(touch_id);
|
| +
|
| + // Can happen if this object is constructed while fingers were down.
|
| + if (iter == touch_locations_.end())
|
| + return;
|
| +
|
| + float delta = (location - iter->second).Length();
|
| + if (delta > gesture_detector_config_.double_tap_slop) {
|
| + state_ = WAIT_FOR_NO_FINGERS;
|
| + CancelTimer();
|
| + return;
|
| + }
|
| + } else {
|
| + NOTREACHED() << "Unexpected event type received: " << event.name();
|
| + return;
|
| + }
|
| +
|
| + if (touch_locations_.size() == 0) {
|
| + state_ = NO_FINGERS_DOWN;
|
| + CancelTimer();
|
| + return;
|
| + }
|
| +
|
| + if (touch_locations_.size() > 2) {
|
| + state_ = WAIT_FOR_NO_FINGERS;
|
| + CancelTimer();
|
| + return;
|
| + }
|
| +
|
| + if (state_ == NO_FINGERS_DOWN && event.type() == ui::ET_TOUCH_PRESSED) {
|
| + state_ = ONE_FINGER_DOWN;
|
| + } else if (state_ == ONE_FINGER_DOWN &&
|
| + event.type() == ui::ET_TOUCH_PRESSED) {
|
| + state_ = TWO_FINGERS_DOWN;
|
| + two_finger_start_time_ = Now();
|
| + StartTimer();
|
| + }
|
| +}
|
| +
|
| +base::TimeTicks TouchAccessibilityEnabler::Now() {
|
| + if (tick_clock_) {
|
| + // This is the same as what EventTimeForNow() does, but here we do it
|
| + // with a clock that can be replaced with a simulated clock for tests.
|
| + return tick_clock_->NowTicks();
|
| + }
|
| + return ui::EventTimeForNow();
|
| +}
|
| +
|
| +void TouchAccessibilityEnabler::StartTimer() {
|
| + if (timer_.IsRunning())
|
| + return;
|
| +
|
| + timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kTimerDelayInMS),
|
| + this, &ui::TouchAccessibilityEnabler::OnTimer);
|
| +}
|
| +
|
| +void TouchAccessibilityEnabler::CancelTimer() {
|
| + if (timer_.IsRunning())
|
| + timer_.Stop();
|
| +}
|
| +
|
| +void TouchAccessibilityEnabler::OnTimer() {
|
| + base::TimeTicks now = Now();
|
| + double tick_count_f =
|
| + (now - two_finger_start_time_).InMillisecondsF() / kTimerDelayInMS;
|
| + int tick_count = roundf(tick_count_f);
|
| + if (tick_count >= 1 && tick_count < kTimerTicksToToggleSpokenFeedback) {
|
| + delegate_->PlaySpokenFeedbackToggleCountdown(tick_count);
|
| + }
|
| + if (tick_count == kTimerTicksToToggleSpokenFeedback) {
|
| + delegate_->ToggleSpokenFeedback();
|
| + state_ = WAIT_FOR_NO_FINGERS;
|
| + }
|
| +}
|
| +
|
| +} // namespace ui
|
|
|