blob: 8b563740c692499e3ca2cd3366b95621afe92def [file] [log] [blame]
// Copyright 2018 The Fuchsia 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 "garnet/bin/a11y/talkback/gesture_detector.h"
namespace talkback {
GestureDetector::GestureDetector(GestureListener* listener)
: listener_(listener), tap_dispatcher_(async_get_default_dispatcher()) {}
void GestureDetector::OnInputEvent(fuchsia::ui::input::PointerEvent event) {
if (event.phase == fuchsia::ui::input::PointerEventPhase::CANCEL) {
CancelAndIdle();
}
switch (state_) {
case State::kIdle:
FromIdle(std::move(event));
break;
case State::kFirstTouchDown:
FromFirstTouchDown(std::move(event));
break;
case State::kFirstTouchUp:
FromFirstTouchUp(std::move(event));
break;
case State::kSecondTouchDown:
FromSecondTouchDown(std::move(event));
break;
case State::kTwoFingersDown:
FromTwoFingersDown(std::move(event));
break;
default:
FXL_LOG(FATAL) << "Unreachable state.";
break;
}
}
void GestureDetector::OnPresentationChangedEvent(
fuchsia::ui::viewsv1::ViewTreeToken token) {
token_ = token;
CancelAndIdle();
}
void GestureDetector::AfterTapDelay() {
if (state_ == State::kFirstTouchUp) {
FXL_VLOG(2) << "AfterTapDelay FirstTouchUp to Idle";
fuchsia::ui::viewsv1::ViewTreeToken clone_token;
token_.Clone(&clone_token);
fuchsia::ui::input::PointerEvent clone_event;
finger1_pointer_event_.Clone(&clone_event);
listener_->Tap(std::move(clone_token), std::move(clone_event));
state_ = State::kIdle;
}
}
void GestureDetector::FromIdle(fuchsia::ui::input::PointerEvent event) {
FXL_DCHECK(state_ == State::kIdle);
if (event.phase == fuchsia::ui::input::PointerEventPhase::DOWN) {
FXL_VLOG(2) << "Idle to FirstTouchDown";
finger1_pointer_id_ = event.pointer_id;
last_pointer_down_or_up_event_ = event.event_time;
// We set this to 0 rather than event.event_time because if it would
// render the |kLongPressDelay| obsolete for the first long press event
// when |kLongPressDelay| < |kMoveCallDelay|.
last_move_call_ = 0;
state_ = State::kFirstTouchDown;
event.Clone(&finger1_pointer_event_);
}
}
void GestureDetector::FromFirstTouchDown(
fuchsia::ui::input::PointerEvent event) {
FXL_DCHECK(state_ == State::kFirstTouchDown);
if (event.pointer_id != finger1_pointer_id_) {
// Register that two fingers are down.
if (event.phase == fuchsia::ui::input::PointerEventPhase::DOWN) {
FXL_VLOG(2) << "FirstTouchDown to TwoFingersDown";
state_ = State::kTwoFingersDown;
finger2_pointer_id_ = event.pointer_id;
finger2_pointer_event_ = event;
SimulateTouchDown();
}
return;
}
if (event.phase == fuchsia::ui::input::PointerEventPhase::MOVE) {
// Move a11y focus if finger has been down > |kLongPressDelay| ns and
// the last time focus was set > |kMoveCallDelay| ns.
if (event.event_time - last_pointer_down_or_up_event_ > kLongPressDelay &&
event.event_time - last_move_call_ > kMoveCallDelay) {
last_move_call_ = event.event_time;
fuchsia::ui::viewsv1::ViewTreeToken clone_token_;
token_.Clone(&clone_token_);
// TODO(SCN-883): Look into performance costs of setting a11y focus every
// move input event.
listener_->Move(std::move(clone_token_), std::move(event));
}
finger1_pointer_event_ = event;
} else if (event.phase == fuchsia::ui::input::PointerEventPhase::UP) {
// End event if finger has been down > |kLongPressDelay| ns.
if (event.event_time - last_pointer_down_or_up_event_ > kLongPressDelay) {
FXL_VLOG(2) << "FirstTouchDown to Idle";
state_ = State::kIdle;
} else {
// Continue checking for a double tap. Detect a single tap if the second
// tap does not start after |kTapDelay| ms.
FXL_VLOG(2) << "FirstTouchDown to FirstTouchUp";
state_ = State::kFirstTouchUp;
last_pointer_down_or_up_event_ = event.event_time;
async::PostDelayedTask(
tap_dispatcher_,
fit::bind_member(this, &GestureDetector::AfterTapDelay), kTapDelay);
}
}
}
void GestureDetector::FromFirstTouchUp(fuchsia::ui::input::PointerEvent event) {
FXL_DCHECK(state_ == State::kFirstTouchUp);
if (event.phase == fuchsia::ui::input::PointerEventPhase::DOWN) {
FXL_VLOG(2) << "FirstTouchUp to SecondTouchDown";
finger1_pointer_id_ = event.pointer_id;
last_pointer_down_or_up_event_ = event.event_time;
state_ = State::kSecondTouchDown;
}
}
void GestureDetector::FromSecondTouchDown(
fuchsia::ui::input::PointerEvent event) {
FXL_DCHECK(state_ == State::kSecondTouchDown);
// TODO(SCN-882): Use a11y actions vs. simulated input to support taps,
// drags, and long presses?
if (event.phase == fuchsia::ui::input::PointerEventPhase::UP) {
FXL_VLOG(2) << "SecondTouchDown to Idle";
fuchsia::ui::viewsv1::ViewTreeToken clone_token_;
token_.Clone(&clone_token_);
listener_->DoubleTap(std::move(clone_token_), std::move(event));
state_ = State::kIdle;
}
}
void GestureDetector::FromTwoFingersDown(
fuchsia::ui::input::PointerEvent event) {
FXL_DCHECK(state_ == State::kTwoFingersDown);
if (event.pointer_id == finger1_pointer_id_) {
// Send simulated move events when finger #1 moves.
if (event.phase == fuchsia::ui::input::PointerEventPhase::MOVE) {
event.Clone(&finger1_pointer_event_);
// TODO(SCN-1124): forward pointer events through scenic
// touch_dispatcher_->SendSimulatedPointerEvent(std::move(event));
finger1_pointer_event_ = event;
}
// When finger #1 lifts up, finger #2 is tracked as the new finger #1.
// The simulated touch event also ends as a finger is lifted.
else if (event.phase == fuchsia::ui::input::PointerEventPhase::UP) {
SimulateTouchUp();
state_ = State::kFirstTouchDown;
finger1_pointer_id_ = finger2_pointer_id_;
finger2_pointer_event_.Clone(&finger1_pointer_event_);
}
} else if (event.pointer_id == finger2_pointer_id_) {
// The simulated touch event ends once one finger is lifted.
if (event.phase == fuchsia::ui::input::PointerEventPhase::UP) {
SimulateTouchUp();
state_ = State::kFirstTouchDown;
} else if (event.phase == fuchsia::ui::input::PointerEventPhase::MOVE) {
finger2_pointer_event_ = event;
}
}
}
void GestureDetector::CancelAndIdle() {
if (state_ == State::kTwoFingersDown) {
SimulateCancel();
}
state_ = State::kIdle;
}
void GestureDetector::SimulateTouchDown() {
fuchsia::ui::input::PointerEvent clone_event;
finger1_pointer_event_.Clone(&clone_event);
clone_event.phase = fuchsia::ui::input::PointerEventPhase::ADD;
// TODO(SCN-1124): forward pointer events through scenic
// touch_dispatcher_->SendSimulatedPointerEvent(std::move(clone_event));
finger1_pointer_event_.Clone(&clone_event);
clone_event.phase = fuchsia::ui::input::PointerEventPhase::DOWN;
// TODO(SCN-1124): forward pointer events through scenic
// touch_dispatcher_->SendSimulatedPointerEvent(std::move(clone_event));
}
void GestureDetector::SimulateTouchUp() {
fuchsia::ui::input::PointerEvent clone_event;
finger1_pointer_event_.Clone(&clone_event);
clone_event.phase = fuchsia::ui::input::PointerEventPhase::UP;
// TODO(SCN-1124): forward pointer events through scenic
// touch_dispatcher_->SendSimulatedPointerEvent(std::move(clone_event));
finger1_pointer_event_.Clone(&clone_event);
clone_event.phase = fuchsia::ui::input::PointerEventPhase::REMOVE;
// TODO(SCN-1124): forward pointer events through scenic
// touch_dispatcher_->SendSimulatedPointerEvent(std::move(clone_event));
}
void GestureDetector::SimulateCancel() {
fuchsia::ui::input::PointerEvent clone_event;
finger1_pointer_event_.Clone(&clone_event);
clone_event.phase = fuchsia::ui::input::PointerEventPhase::CANCEL;
// TODO(SCN-1124): forward pointer events through scenic
// touch_dispatcher_->SendSimulatedPointerEvent(std::move(clone_event));
}
} // namespace talkback