Commit 55fdb8e0 authored by Ryan Daum's avatar Ryan Daum Committed by Commit Bot

[chromecast] Tune-ups to triple tap detection.

Does not discard the first tap event, but tries to issue a cancel for
it instead.  Had to do this because the replaying of the stashed
events weren't making it through to later event rewriters in the chain
(including side swipe detection, and the forthcoming fullscreen
magnifier).

Has the side-effect that many applications will still receive a touch
press event. However touch latency also improves.

Also adds tap slop detection to avoid accidental triple taps.

Bug: internal b/110532452
Test: manual and unit test
Change-Id: Icc9d3b558940348e98c88eb1c29697cd7d59065a
Reviewed-on: https://chromium-review.googlesource.com/1170588Reviewed-by: default avatarAlex Sakhartchouk <alexst@chromium.org>
Commit-Queue: Ryan Daum <rdaum@chromium.org>
Cr-Commit-Position: refs/heads/master@{#582182}
parent 1d89f109
...@@ -37,34 +37,31 @@ ui::EventRewriteStatus TripleTapDetector::RewriteEvent( ...@@ -37,34 +37,31 @@ ui::EventRewriteStatus TripleTapDetector::RewriteEvent(
} }
const ui::TouchEvent& touch_event = static_cast<const ui::TouchEvent&>(event); const ui::TouchEvent& touch_event = static_cast<const ui::TouchEvent&>(event);
return HandleTripleTapState(touch_event, triple_tap_state_);
}
ui::EventRewriteStatus TripleTapDetector::NextDispatchEvent(
const ui::Event& last_event,
std::unique_ptr<ui::Event>* new_event) {
// Only called if the rewriter has returned EVENT_REWRITE_DISPATCH_ANOTHER,
// and we don't do that.
NOTREACHED();
return ui::EVENT_REWRITE_CONTINUE;
}
ui::EventRewriteStatus TripleTapDetector::HandleTripleTapState(
const ui::TouchEvent& event,
TripleTapState state) {
if (event.type() == ui::ET_TOUCH_PRESSED) { if (event.type() == ui::ET_TOUCH_PRESSED) {
// If a press happened again before the minimum inter-tap interval, cancel // If a press happened again before the minimum inter-tap interval, cancel
// the triple tap. // the triple tap.
if (state == TripleTapState::INTERVAL_WAIT && if (triple_tap_state_ == TripleTapState::INTERVAL_WAIT &&
(event.time_stamp() - stashed_events_.back().time_stamp()) < (event.time_stamp() - stashed_events_.back().time_stamp()) <
gesture_detector_config_.double_tap_min_time) { gesture_detector_config_.double_tap_min_time) {
stashed_events_.clear();
TripleTapReset(); TripleTapReset();
return ui::EVENT_REWRITE_DISCARD; return ui::EVENT_REWRITE_CONTINUE;
}
// If the user moved too far from the last tap position, it's not a triple
// tap.
if (tap_count_) {
float distance = (touch_event.location() - last_tap_location_).Length();
if (distance > gesture_detector_config_.double_tap_slop) {
TripleTapReset();
stashed_events_.clear();
return ui::EVENT_REWRITE_CONTINUE;
}
} }
// Otherwise transition into a touched state. // Otherwise transition into a touched state.
triple_tap_state_ = TripleTapState::TOUCH; triple_tap_state_ = TripleTapState::TOUCH;
last_tap_location_ = event.location(); last_tap_location_ = touch_event.location();
// If this is pressed too long, it should be treated as a long-press, and // If this is pressed too long, it should be treated as a long-press, and
// not part of a triple-tap, so set a timer to detect that. // not part of a triple-tap, so set a timer to detect that.
...@@ -72,64 +69,92 @@ ui::EventRewriteStatus TripleTapDetector::HandleTripleTapState( ...@@ -72,64 +69,92 @@ ui::EventRewriteStatus TripleTapDetector::HandleTripleTapState(
gesture_detector_config_.longpress_timeout, this, gesture_detector_config_.longpress_timeout, this,
&TripleTapDetector::OnLongPressIntervalTimerFired); &TripleTapDetector::OnLongPressIntervalTimerFired);
// Copy the event and then kill the original touch pressed event, we'll // If we've already gotten one tap, discard this event, only the original
// produce a new one when the timer expires if it turns out this wasn't a // tap needs to get through.
// triple-tap. if (tap_count_) {
stashed_events_.push_back(event); return ui::EVENT_REWRITE_DISCARD;
}
return ui::EVENT_REWRITE_DISCARD; // Copy the event so we can issue a cancel for it later if this turns out to
// be a triple-tap.
stashed_events_.push_back(touch_event);
return ui::EVENT_REWRITE_CONTINUE;
} }
// Finger was released while we were waiting for one, count it as a tap. // Finger was released while we were waiting for one, count it as a tap.
if (event.type() == ui::ET_TOUCH_RELEASED && state == TripleTapState::TOUCH) { if (touch_event.type() == ui::ET_TOUCH_RELEASED &&
triple_tap_state_ == TripleTapState::TOUCH) {
triple_tap_state_ = TripleTapState::INTERVAL_WAIT; triple_tap_state_ = TripleTapState::INTERVAL_WAIT;
triple_tap_timer_.Start(FROM_HERE, triple_tap_timer_.Start(FROM_HERE,
gesture_detector_config_.double_tap_timeout, this, gesture_detector_config_.double_tap_timeout, this,
&TripleTapDetector::OnTripleTapIntervalTimerFired); &TripleTapDetector::OnTripleTapIntervalTimerFired);
stashed_events_.push_back(event);
tap_count_++; tap_count_++;
if (tap_count_ == 3) { if (tap_count_ == 3) {
stashed_events_.clear();
TripleTapReset(); TripleTapReset();
delegate_->OnTripleTap(event.location()); delegate_->OnTripleTap(touch_event.location());
// Start issuing cancel events for old presses.
DCHECK(!stashed_events_.empty());
const auto& stashed_press = stashed_events_.front();
std::unique_ptr<ui::TouchEvent> rewritten_touch_event =
std::make_unique<ui::TouchEvent>(ui::ET_TOUCH_CANCELLED, gfx::Point(),
touch_event.time_stamp(),
stashed_press.pointer_details());
rewritten_touch_event->set_location_f(stashed_press.location_f());
rewritten_touch_event->set_root_location_f(
stashed_press.root_location_f());
rewritten_touch_event->set_flags(stashed_press.flags());
*rewritten_event = std::move(rewritten_touch_event);
stashed_events_.pop_front();
if (!stashed_events_.empty())
return ui::EVENT_REWRITE_DISPATCH_ANOTHER;
else
return ui::EVENT_REWRITE_REWRITTEN;
} else if (tap_count_ > 1) {
return ui::EVENT_REWRITE_DISCARD;
} }
return ui::EVENT_REWRITE_DISCARD;
}
if (state != TripleTapState::NONE) {
stashed_events_.push_back(event);
return ui::EVENT_REWRITE_DISCARD;
} }
return ui::EVENT_REWRITE_CONTINUE; return ui::EVENT_REWRITE_CONTINUE;
} }
ui::EventRewriteStatus TripleTapDetector::NextDispatchEvent(
const ui::Event& last_event,
std::unique_ptr<ui::Event>* new_event) {
const auto& stashed_press = stashed_events_.front();
std::unique_ptr<ui::TouchEvent> rewritten_touch_event =
std::make_unique<ui::TouchEvent>(ui::ET_TOUCH_CANCELLED, gfx::Point(),
last_event.time_stamp(),
stashed_press.pointer_details());
rewritten_touch_event->set_location_f(stashed_press.location_f());
rewritten_touch_event->set_root_location_f(stashed_press.root_location_f());
rewritten_touch_event->set_flags(stashed_press.flags());
*new_event = std::move(rewritten_touch_event);
stashed_events_.pop_front();
if (!stashed_events_.empty())
return ui::EVENT_REWRITE_DISPATCH_ANOTHER;
else
return ui::EVENT_REWRITE_DISCARD;
}
void TripleTapDetector::OnTripleTapIntervalTimerFired() { void TripleTapDetector::OnTripleTapIntervalTimerFired() {
TripleTapReset(); TripleTapReset();
stashed_events_.clear();
} }
void TripleTapDetector::OnLongPressIntervalTimerFired() { void TripleTapDetector::OnLongPressIntervalTimerFired() {
TripleTapReset(); TripleTapReset();
stashed_events_.clear();
} }
void TripleTapDetector::TripleTapReset() { void TripleTapDetector::TripleTapReset() {
triple_tap_state_ = TripleTapState::NONE; triple_tap_state_ = TripleTapState::NONE;
tap_count_ = 0; tap_count_ = 0;
triple_tap_timer_.Stop(); triple_tap_timer_.Stop();
for (auto& event : stashed_events_) {
DispatchEvent(&event);
}
stashed_events_.clear();
}
void TripleTapDetector::DispatchEvent(ui::TouchEvent* event) {
// Turn off triple-tap before re-dispatching to avoid infinite recursion into
// this detector.
base::AutoReset<bool> toggle_enable(&enabled_, false);
DCHECK(!root_window_->GetHost()
->dispatcher()
->OnEventFromSource(event)
.dispatcher_destroyed);
} }
} // namespace chromecast } // namespace chromecast
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
#ifndef CHROMECAST_GRAPHICS_GESTURES_TRIPLE_TAP_DETECTOR_H_ #ifndef CHROMECAST_GRAPHICS_GESTURES_TRIPLE_TAP_DETECTOR_H_
#define CHROMECAST_GRAPHICS_GESTURES_TRIPLE_TAP_DETECTOR_H_ #define CHROMECAST_GRAPHICS_GESTURES_TRIPLE_TAP_DETECTOR_H_
#include <deque>
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/timer/timer.h" #include "base/timer/timer.h"
...@@ -60,10 +62,6 @@ class TripleTapDetector : public ui::EventRewriter { ...@@ -60,10 +62,6 @@ class TripleTapDetector : public ui::EventRewriter {
private: private:
friend class TripleTapDetectorTest; friend class TripleTapDetectorTest;
void DispatchEvent(ui::TouchEvent* event);
ui::EventRewriteStatus HandleTripleTapState(const ui::TouchEvent& event,
TripleTapState state);
// Expiration event for maximum time between taps in a triple tap. // Expiration event for maximum time between taps in a triple tap.
void OnTripleTapIntervalTimerFired(); void OnTripleTapIntervalTimerFired();
// Expiration event for a finger that is pressed too long during a triple tap. // Expiration event for a finger that is pressed too long during a triple tap.
...@@ -84,7 +82,7 @@ class TripleTapDetector : public ui::EventRewriter { ...@@ -84,7 +82,7 @@ class TripleTapDetector : public ui::EventRewriter {
int tap_count_; int tap_count_;
gfx::Point last_tap_location_; gfx::Point last_tap_location_;
base::OneShotTimer triple_tap_timer_; base::OneShotTimer triple_tap_timer_;
std::vector<ui::TouchEvent> stashed_events_; std::deque<ui::TouchEvent> stashed_events_;
DISALLOW_COPY_AND_ASSIGN(TripleTapDetector); DISALLOW_COPY_AND_ASSIGN(TripleTapDetector);
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment