Commit 64356c22 authored by Ryan Daum's avatar Ryan Daum Committed by Commit Bot

[chromecast] Detect screen enter/exit and corner holds.

  - Adds detection and dispatch of screen entry/exit events.
  - Adds detection of a finger press and hold in the corner.
  - Better handling of multiple-finger events on the screen.

Bug: internal b/112073644
Bug: internal b/112314035
Test: unit test and manual
Change-Id: Id2155f8e432428ee63f3457274f1507ce1edee53
Reviewed-on: https://chromium-review.googlesource.com/1165795Reviewed-by: default avatarAlex Sakhartchouk <alexst@chromium.org>
Reviewed-by: default avatarKevin Schoedel <kpschoedel@chromium.org>
Commit-Queue: Ryan Daum <rdaum@chromium.org>
Cr-Commit-Position: refs/heads/master@{#582196}
parent f2da3d75
...@@ -15,6 +15,7 @@ cast_source_set("graphics") { ...@@ -15,6 +15,7 @@ cast_source_set("graphics") {
deps = [ deps = [
"//base", "//base",
"//ui/events:events",
"//ui/gfx", "//ui/gfx",
] ]
......
...@@ -10,4 +10,8 @@ bool CastGestureHandler::CanHandleSwipe(CastSideSwipeOrigin swipe_origin) { ...@@ -10,4 +10,8 @@ bool CastGestureHandler::CanHandleSwipe(CastSideSwipeOrigin swipe_origin) {
return false; return false;
} }
CastGestureHandler::Corner CastGestureHandler::HandledCornerHolds() const {
return NO_CORNERS;
}
} // namespace chromecast } // namespace chromecast
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
#ifndef CHROMECAST_GRAPHICS_CAST_GESTURE_HANDLER_H_ #ifndef CHROMECAST_GRAPHICS_CAST_GESTURE_HANDLER_H_
#define CHROMECAST_GRAPHICS_CAST_GESTURE_HANDLER_H_ #define CHROMECAST_GRAPHICS_CAST_GESTURE_HANDLER_H_
#include "ui/events/event.h"
// TODO(rdaum): Move into chromecast/graphics/gestures, which will require some // TODO(rdaum): Move into chromecast/graphics/gestures, which will require some
// cross-repo maneuvers. // cross-repo maneuvers.
#include "base/macros.h" #include "base/macros.h"
...@@ -47,6 +49,37 @@ class CastGestureHandler { ...@@ -47,6 +49,37 @@ class CastGestureHandler {
// followed by a release, within the tap timeout window // followed by a release, within the tap timeout window
virtual void HandleTapGesture(const gfx::Point& touch_location) {} virtual void HandleTapGesture(const gfx::Point& touch_location) {}
// Triggered when the finger enters a side margin from inside the screen.
// That is, the finger is leaving the screen.
virtual void HandleScreenExit(CastSideSwipeOrigin side,
const gfx::Point& touch_location) {}
// Triggered when the finger enters a side margin from outside the screen.
// That is, the finger is entering the screen.
virtual void HandleScreenEnter(CastSideSwipeOrigin side,
const gfx::Point& touch_location) {}
enum Corner {
NO_CORNERS = 0,
TOP_LEFT_CORNER = 1 << 0,
BOTTOM_LEFT_CORNER = 1 << 1,
TOP_RIGHT_CORNER = 1 << 2,
BOTTOM_RIGHT_CORNER = 1 << 3,
};
// Return a bitmask of the corners that this handler is interested in, if any.
virtual Corner HandledCornerHolds() const;
// Triggered when the finger has been held inside the corner for longer
// than the corner hold threshold time.
virtual void HandleCornerHold(Corner corner_origin,
const ui::TouchEvent& touch_event) {}
// Triggered when a corner hold is ended because the finger has left the
// corner or has been released.
virtual void HandleCornerHoldEnd(Corner corner_origin,
const ui::TouchEvent& touch_event) {}
private: private:
DISALLOW_COPY_AND_ASSIGN(CastGestureHandler); DISALLOW_COPY_AND_ASSIGN(CastGestureHandler);
}; };
......
...@@ -76,4 +76,47 @@ void CastSystemGestureDispatcher::HandleTapGesture( ...@@ -76,4 +76,47 @@ void CastSystemGestureDispatcher::HandleTapGesture(
} }
} }
} // namespace chromecast void CastSystemGestureDispatcher::HandleScreenExit(
\ No newline at end of file CastSideSwipeOrigin side,
const gfx::Point& touch_location) {
for (auto* gesture_handler : gesture_handlers_) {
gesture_handler->HandleScreenExit(side, touch_location);
}
}
void CastSystemGestureDispatcher::HandleScreenEnter(
CastSideSwipeOrigin side,
const gfx::Point& touch_location) {
for (auto* gesture_handler : gesture_handlers_) {
gesture_handler->HandleScreenEnter(side, touch_location);
}
}
CastGestureHandler::Corner CastSystemGestureDispatcher::HandledCornerHolds()
const {
int corner_hold_bitmask = CastGestureHandler::NO_CORNERS;
for (auto* gesture_handler : gesture_handlers_) {
corner_hold_bitmask |= gesture_handler->HandledCornerHolds();
}
return static_cast<CastGestureHandler::Corner>(corner_hold_bitmask);
}
void CastSystemGestureDispatcher::HandleCornerHold(
CastGestureHandler::Corner corner_origin,
const ui::TouchEvent& touch_event) {
for (auto* gesture_handler : gesture_handlers_) {
if (gesture_handler->HandledCornerHolds() & corner_origin) {
gesture_handler->HandleCornerHold(corner_origin, touch_event);
}
}
}
void CastSystemGestureDispatcher::HandleCornerHoldEnd(
CastGestureHandler::Corner corner_origin,
const ui::TouchEvent& touch_event) {
for (auto* gesture_handler : gesture_handlers_) {
if (gesture_handler->HandledCornerHolds() & corner_origin) {
gesture_handler->HandleCornerHoldEnd(corner_origin, touch_event);
}
}
}
} // namespace chromecast
...@@ -35,6 +35,15 @@ class CastSystemGestureDispatcher : public CastGestureHandler { ...@@ -35,6 +35,15 @@ class CastSystemGestureDispatcher : public CastGestureHandler {
const gfx::Point& touch_location) override; const gfx::Point& touch_location) override;
void HandleTapDownGesture(const gfx::Point& touch_location) override; void HandleTapDownGesture(const gfx::Point& touch_location) override;
void HandleTapGesture(const gfx::Point& touch_location) override; void HandleTapGesture(const gfx::Point& touch_location) override;
void HandleScreenExit(CastSideSwipeOrigin side,
const gfx::Point& touch_location) override;
void HandleScreenEnter(CastSideSwipeOrigin side,
const gfx::Point& touch_location) override;
Corner HandledCornerHolds() const override;
void HandleCornerHold(Corner corner_origin,
const ui::TouchEvent& touch_event) override;
void HandleCornerHoldEnd(Corner corner_origin,
const ui::TouchEvent& touch_event) override;
private: private:
base::flat_set<CastGestureHandler*> gesture_handlers_; base::flat_set<CastGestureHandler*> gesture_handlers_;
......
...@@ -13,7 +13,6 @@ ...@@ -13,7 +13,6 @@
#include "ui/aura/window_tree_host.h" #include "ui/aura/window_tree_host.h"
#include "ui/display/display.h" #include "ui/display/display.h"
#include "ui/display/screen.h" #include "ui/display/screen.h"
#include "ui/events/event.h"
#include "ui/events/event_rewriter.h" #include "ui/events/event_rewriter.h"
#include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect.h"
...@@ -57,7 +56,11 @@ SideSwipeDetector::SideSwipeDetector(CastGestureHandler* gesture_handler, ...@@ -57,7 +56,11 @@ SideSwipeDetector::SideSwipeDetector(CastGestureHandler* gesture_handler,
bottom_gesture_start_height_(BottomGestureStartHeight()), bottom_gesture_start_height_(BottomGestureStartHeight()),
gesture_handler_(gesture_handler), gesture_handler_(gesture_handler),
root_window_(root_window), root_window_(root_window),
current_swipe_(CastSideSwipeOrigin::NONE) { current_swipe_origin_(CastSideSwipeOrigin::NONE),
current_pointer_id_(ui::PointerDetails::kUnknownPointerId),
corner_hold_timer_(new base::OneShotTimer),
in_corner_hold_(false),
current_corner_origin_(CastGestureHandler::NO_CORNERS) {
DCHECK(gesture_handler); DCHECK(gesture_handler);
DCHECK(root_window); DCHECK(root_window);
root_window_->GetHost()->GetEventSource()->AddEventRewriter(this); root_window_->GetHost()->GetEventSource()->AddEventRewriter(this);
...@@ -67,30 +70,74 @@ SideSwipeDetector::~SideSwipeDetector() { ...@@ -67,30 +70,74 @@ SideSwipeDetector::~SideSwipeDetector() {
root_window_->GetHost()->GetEventSource()->RemoveEventRewriter(this); root_window_->GetHost()->GetEventSource()->RemoveEventRewriter(this);
} }
CastSideSwipeOrigin SideSwipeDetector::GetDragPosition( bool SideSwipeDetector::GetMarginPosition(
const gfx::Point& point, const gfx::Point& point,
const gfx::Rect& screen_bounds) const { const gfx::Rect& screen_bounds,
if (point.y() < (screen_bounds.y() + gesture_start_height_)) { CastSideSwipeOrigin* side,
return CastSideSwipeOrigin::TOP; CastGestureHandler::Corner* corner) const {
DCHECK(corner);
DCHECK(side);
*corner = CastGestureHandler::NO_CORNERS;
*side = CastSideSwipeOrigin::NONE;
const int top_margin_limit = screen_bounds.y() + gesture_start_height_;
const int left_margin_limit = screen_bounds.x() + gesture_start_width_;
const int right_margin_limit =
screen_bounds.x() + screen_bounds.width() - gesture_start_width_;
const int bottom_margin_limit =
screen_bounds.y() + screen_bounds.height() - bottom_gesture_start_height_;
// Find out if anybody is interested in corner events (hold or swipe). If not,
// we won't bother trying to check them, so that we can be sure that swipe
// events that happen in corners are treated as a side.
CastGestureHandler::Corner corners = gesture_handler_->HandledCornerHolds();
// Look for corners first if needed.
if (corners != CastGestureHandler::NO_CORNERS) {
if (corners && point.y() < top_margin_limit &&
point.x() < left_margin_limit) {
*corner = CastGestureHandler::TOP_LEFT_CORNER;
return true;
}
if (point.y() < top_margin_limit && point.x() > right_margin_limit) {
*corner = CastGestureHandler::TOP_RIGHT_CORNER;
return true;
}
if (point.x() < left_margin_limit && point.y() > bottom_margin_limit) {
*corner = CastGestureHandler::BOTTOM_LEFT_CORNER;
return true;
}
if (point.x() > right_margin_limit && point.y() > bottom_margin_limit) {
*corner = CastGestureHandler::BOTTOM_RIGHT_CORNER;
return true;
}
} }
if (point.x() < (screen_bounds.x() + gesture_start_width_)) {
return CastSideSwipeOrigin::LEFT; // Then sides.
if (point.y() < top_margin_limit) {
*side = CastSideSwipeOrigin::TOP;
return true;
}
if (point.x() < left_margin_limit) {
*side = CastSideSwipeOrigin::LEFT;
return true;
} }
if (point.x() > if (point.x() > right_margin_limit) {
(screen_bounds.x() + screen_bounds.width() - gesture_start_width_)) { *side = CastSideSwipeOrigin::RIGHT;
return CastSideSwipeOrigin::RIGHT; return true;
} }
if (point.y() > (screen_bounds.y() + screen_bounds.height() - if (point.y() > bottom_margin_limit) {
bottom_gesture_start_height_)) { *side = CastSideSwipeOrigin::BOTTOM;
return CastSideSwipeOrigin::BOTTOM; return true;
} }
return CastSideSwipeOrigin::NONE; *side = CastSideSwipeOrigin::NONE;
return false;
} }
void SideSwipeDetector::StashEvent(const ui::TouchEvent& event) { void SideSwipeDetector::StashEvent(const ui::TouchEvent& event) {
// If the time since the gesture start is longer than our threshold, do not // If the time since the gesture start is longer than our threshold, do not
// stash the event (and clear the stashed events). // stash the event (and clear the stashed events).
if (current_swipe_time_.Elapsed() > kGestureMarginEventsTimeLimit) { if (gesture_elapsed_timer_.Elapsed() > kGestureMarginEventsTimeLimit) {
stashed_events_.clear(); stashed_events_.clear();
return; return;
} }
...@@ -104,7 +151,6 @@ ui::EventRewriteStatus SideSwipeDetector::RewriteEvent( ...@@ -104,7 +151,6 @@ ui::EventRewriteStatus SideSwipeDetector::RewriteEvent(
if (!event.IsTouchEvent()) { if (!event.IsTouchEvent()) {
return ui::EVENT_REWRITE_CONTINUE; return ui::EVENT_REWRITE_CONTINUE;
} }
const ui::TouchEvent* touch_event = event.AsTouchEvent(); const ui::TouchEvent* touch_event = event.AsTouchEvent();
// Touch events come through in screen pixels, but untransformed. This is the // Touch events come through in screen pixels, but untransformed. This is the
...@@ -116,69 +162,121 @@ ui::EventRewriteStatus SideSwipeDetector::RewriteEvent( ...@@ -116,69 +162,121 @@ ui::EventRewriteStatus SideSwipeDetector::RewriteEvent(
gfx::Rect screen_bounds = display::Screen::GetScreen() gfx::Rect screen_bounds = display::Screen::GetScreen()
->GetDisplayNearestPoint(touch_location) ->GetDisplayNearestPoint(touch_location)
.bounds(); .bounds();
CastSideSwipeOrigin side_swipe_origin =
GetDragPosition(touch_location, screen_bounds);
// A located event has occurred inside the margin. It might be the start of
// our gesture, or a touch that we need to squash.
if (current_swipe_ == CastSideSwipeOrigin::NONE &&
side_swipe_origin != CastSideSwipeOrigin::NONE) {
// Check to see if we have any potential consumers of events on this side.
// If not, we can continue on without consuming it.
if (!gesture_handler_->CanHandleSwipe(side_swipe_origin)) {
return ui::EVENT_REWRITE_CONTINUE;
}
// Detect the beginning of a system gesture swipe. CastSideSwipeOrigin detected_side;
if (touch_event->type() != ui::ET_TOUCH_PRESSED) { CastGestureHandler::Corner detected_corner;
bool is_margin_event = GetMarginPosition(touch_location, screen_bounds,
&detected_side, &detected_corner);
// Gesture initiation is a press event inside a margin for a new pointer id.
if (touch_event->type() == ui::ET_TOUCH_PRESSED) {
if (!is_margin_event) {
return ui::EVENT_REWRITE_CONTINUE; return ui::EVENT_REWRITE_CONTINUE;
} }
current_swipe_ = side_swipe_origin; // We're entering the screen, so notify the dispatcher of that before
// looking for a swipe.
gesture_handler_->HandleScreenEnter(detected_side, touch_event->location());
// Let the subscribers know about the gesture begin. // There's already a finger down, so don't process any swipes or corners
gesture_handler_->HandleSideSwipeBegin(side_swipe_origin, touch_location); // with the new one.
if (current_pointer_id_ != ui::PointerDetails::kUnknownPointerId) {
return ui::EVENT_REWRITE_CONTINUE;
}
VLOG(1) << "side swipe gesture begin @ " << touch_location.ToString(); // Remember the finger.
current_swipe_time_ = base::ElapsedTimer(); current_pointer_id_ = touch_event->pointer_details().id;
// If we're in a corner (which means we're looking for that kind of thing),
// start the corner hold timer and move on.
in_corner_hold_ = false;
current_corner_origin_ = CastGestureHandler::NO_CORNERS;
if (detected_corner != CastGestureHandler::NO_CORNERS) {
current_corner_origin_ = detected_corner;
corner_hold_start_event_ = ui::Event::Clone(*touch_event);
corner_hold_timer_->Start(
FROM_HERE, gesture_detector_config_.longpress_timeout, this,
&SideSwipeDetector::OnCornerHoldTimerFired);
} else if (detected_side != CastSideSwipeOrigin::NONE &&
gesture_handler_->CanHandleSwipe(detected_side)) {
current_swipe_origin_ = detected_side;
// Let the subscribers know about the gesture begin.
gesture_handler_->HandleSideSwipeBegin(detected_side, touch_location);
VLOG(1) << "side swipe gesture begin @ " << touch_location.ToString();
} else {
return ui::EVENT_REWRITE_CONTINUE;
}
gesture_elapsed_timer_ = base::ElapsedTimer();
// Stash a copy of the event should we decide to reconstitute it later if we // Stash a copy of the event should we decide to reconstitute it later if
// decide that this isn't in fact a side swipe. // we decide that this isn't in fact a side swipe or corner hold.
StashEvent(*touch_event); StashEvent(*touch_event);
// Avoid corrupt gesture state caused by a missing kGestureScrollEnd event // Avoid corrupt gesture state caused by a missing kGestureScrollEnd event
// as we potentially transition between web views. // as we potentially transition between web views.
root_window_->CleanupGestureState(); root_window_->CleanupGestureState();
// And then stop the original event from propagating.
return ui::EVENT_REWRITE_DISCARD; return ui::EVENT_REWRITE_DISCARD;
} }
if (current_swipe_ == CastSideSwipeOrigin::NONE) { // If we're releasing on our way out of the screen, let the world know.
if (touch_event->type() == ui::ET_TOUCH_RELEASED &&
detected_side != CastSideSwipeOrigin::NONE) {
gesture_handler_->HandleScreenExit(detected_side, touch_event->location());
}
// If we're not in a gesture of any kind, or the the non-press events we're
// getting are not for the same pointer id, we're not interested in them.
if ((current_corner_origin_ == CastGestureHandler::NO_CORNERS &&
current_swipe_origin_ == CastSideSwipeOrigin::NONE) ||
touch_event->pointer_details().id != current_pointer_id_) {
return ui::EVENT_REWRITE_CONTINUE; return ui::EVENT_REWRITE_CONTINUE;
} }
// A swipe is in progress, or has completed, so stop propagation of underlying // While we're in a swipe, stash events for later playback.
// gesture/touch events, after stashing a copy of the original event. if (in_corner_hold_ || current_swipe_origin_ != CastSideSwipeOrigin::NONE) {
StashEvent(*touch_event); StashEvent(*touch_event);
}
// The finger has lifted, which means the end of the gesture, or if the finger
// hasn't travelled far enough, replay the original events.
if (touch_event->type() == ui::ET_TOUCH_RELEASED) { if (touch_event->type() == ui::ET_TOUCH_RELEASED) {
VLOG(1) << "gesture release; time since press: " current_pointer_id_ = ui::PointerDetails::kUnknownPointerId;
<< current_swipe_time_.Elapsed().InMilliseconds() << "ms @ "
// If a corner event was in progress, we will need to either finish a hold
// or cancel it.
if (current_corner_origin_ != CastGestureHandler::NO_CORNERS) {
// If the corner was being held, notify consumers that it's over,
// clear events, and discard.
if (in_corner_hold_) {
EndCornerHold(*touch_event);
stashed_events_.clear();
return ui::EVENT_REWRITE_DISCARD;
}
// Otherwise cancel and replay held events.
CancelCornerHoldCheck();
return StartNextDispatchStashedEvents(new_event);
}
// If there was no side swipe in progress, we can just move on.
if (current_swipe_origin_ == CastSideSwipeOrigin::NONE) {
return ui::EVENT_REWRITE_CONTINUE;
}
VLOG(1) << "gesture swipe release; time since press: "
<< gesture_elapsed_timer_.Elapsed().InMilliseconds() << "ms @ "
<< touch_location.ToString(); << touch_location.ToString();
gesture_handler_->HandleSideSwipeEnd(current_swipe_, touch_location); gesture_handler_->HandleSideSwipeEnd(current_swipe_origin_, touch_location);
current_swipe_ = CastSideSwipeOrigin::NONE; current_swipe_origin_ = CastSideSwipeOrigin::NONE;
// If the finger is still inside the touch margin at release, this is not // If the finger is still inside the touch margin at release, this is not
// really a side swipe. Start streaming out events we stashed for later // really a side swipe. Start streaming out events we stashed for later
// retrieval. // retrieval.
if (side_swipe_origin != CastSideSwipeOrigin::NONE && if (detected_side != CastSideSwipeOrigin::NONE) {
!stashed_events_.empty()) { return StartNextDispatchStashedEvents(new_event);
*new_event = ui::Event::Clone(stashed_events_.front());
stashed_events_.pop_front();
return ui::EVENT_REWRITE_DISPATCH_ANOTHER;
} }
// Otherwise, clear them. // Otherwise, clear them.
...@@ -187,15 +285,49 @@ ui::EventRewriteStatus SideSwipeDetector::RewriteEvent( ...@@ -187,15 +285,49 @@ ui::EventRewriteStatus SideSwipeDetector::RewriteEvent(
return ui::EVENT_REWRITE_DISCARD; return ui::EVENT_REWRITE_DISCARD;
} }
// The system gesture is ongoing... // If we're looking for corner hold and the timer has not expired and we've
gesture_handler_->HandleSideSwipeContinue(current_swipe_, touch_location); // left the corner, cancel the hold check now and replay the stashed events.
VLOG(1) << "gesture continue; time since press: " if (current_corner_origin_ != CastGestureHandler::NO_CORNERS &&
<< current_swipe_time_.Elapsed().InMilliseconds() << "ms @ " detected_corner != current_corner_origin_ &&
<< touch_location.ToString(); corner_hold_timer_->IsRunning()) {
CancelCornerHoldCheck();
return StartNextDispatchStashedEvents(new_event);
}
// If a swipe is ongoing, let consumers know about it.
if (current_swipe_origin_ != CastSideSwipeOrigin::NONE) {
gesture_handler_->HandleSideSwipeContinue(current_swipe_origin_,
touch_location);
VLOG(1) << "gesture continue; time since press: "
<< gesture_elapsed_timer_.Elapsed().InMilliseconds() << "ms @ "
<< touch_location.ToString();
}
// If we're in corner hold and we've left the corner, that's an end.
if (in_corner_hold_ && detected_corner != current_corner_origin_) {
EndCornerHold(*touch_event);
stashed_events_.clear();
}
return ui::EVENT_REWRITE_DISCARD; return ui::EVENT_REWRITE_DISCARD;
} }
void SideSwipeDetector::CancelCornerHoldCheck() {
corner_hold_timer_->Stop();
corner_hold_start_event_.reset();
in_corner_hold_ = false;
current_corner_origin_ = CastGestureHandler::NO_CORNERS;
}
void SideSwipeDetector::EndCornerHold(const ui::TouchEvent& event) {
gesture_handler_->HandleCornerHoldEnd(current_corner_origin_, event);
in_corner_hold_ = false;
current_corner_origin_ = CastGestureHandler::NO_CORNERS;
corner_hold_start_event_.reset();
}
ui::EventRewriteStatus SideSwipeDetector::NextDispatchEvent( ui::EventRewriteStatus SideSwipeDetector::NextDispatchEvent(
const ui::Event& last_event, const ui::Event& last_event,
std::unique_ptr<ui::Event>* new_event) { std::unique_ptr<ui::Event>* new_event) {
...@@ -209,4 +341,30 @@ ui::EventRewriteStatus SideSwipeDetector::NextDispatchEvent( ...@@ -209,4 +341,30 @@ ui::EventRewriteStatus SideSwipeDetector::NextDispatchEvent(
return ui::EVENT_REWRITE_DISPATCH_ANOTHER; return ui::EVENT_REWRITE_DISPATCH_ANOTHER;
} }
void SideSwipeDetector::OnCornerHoldTimerFired() {
DCHECK(corner_hold_start_event_);
DCHECK(current_corner_origin_ != CastGestureHandler::NO_CORNERS);
DCHECK(!in_corner_hold_);
in_corner_hold_ = true;
gesture_handler_->HandleCornerHold(current_corner_origin_,
*corner_hold_start_event_->AsTouchEvent());
corner_hold_start_event_.reset();
}
void SideSwipeDetector::SetTimerForTesting(
std::unique_ptr<base::OneShotTimer> timer) {
corner_hold_timer_ = std::move(timer);
}
ui::EventRewriteStatus SideSwipeDetector::StartNextDispatchStashedEvents(
std::unique_ptr<ui::Event>* new_event) {
if (!stashed_events_.empty()) {
*new_event = ui::Event::Clone(stashed_events_.front());
stashed_events_.pop_front();
return ui::EVENT_REWRITE_DISPATCH_ANOTHER;
}
stashed_events_.clear();
return ui::EVENT_REWRITE_DISCARD;
}
} // namespace chromecast } // namespace chromecast
...@@ -6,8 +6,10 @@ ...@@ -6,8 +6,10 @@
#define CHROMECAST_GRAPHICS_GESTURES_SIDE_SWIPE_DETECTOR_H_ #define CHROMECAST_GRAPHICS_GESTURES_SIDE_SWIPE_DETECTOR_H_
#include "base/timer/elapsed_timer.h" #include "base/timer/elapsed_timer.h"
#include "base/timer/timer.h"
#include "chromecast/graphics/cast_gesture_handler.h" #include "chromecast/graphics/cast_gesture_handler.h"
#include "ui/events/event_rewriter.h" #include "ui/events/event_rewriter.h"
#include "ui/events/gesture_detection/gesture_detector.h"
namespace aura { namespace aura {
class Window; class Window;
...@@ -15,6 +17,10 @@ class Window; ...@@ -15,6 +17,10 @@ class Window;
namespace chromecast { namespace chromecast {
namespace test {
class SideSwipeDetectorTest;
} // namespace test
// An event rewriter for detecting system-wide gestures performed on the margins // An event rewriter for detecting system-wide gestures performed on the margins
// of the root window. // of the root window.
// Recognizes swipe gestures that originate from the top, left, bottom, and // Recognizes swipe gestures that originate from the top, left, bottom, and
...@@ -28,8 +34,10 @@ class SideSwipeDetector : public ui::EventRewriter { ...@@ -28,8 +34,10 @@ class SideSwipeDetector : public ui::EventRewriter {
~SideSwipeDetector() override; ~SideSwipeDetector() override;
CastSideSwipeOrigin GetDragPosition(const gfx::Point& point, bool GetMarginPosition(const gfx::Point& point,
const gfx::Rect& screen_bounds) const; const gfx::Rect& screen_bounds,
CastSideSwipeOrigin* side,
CastGestureHandler::Corner* corner) const;
// Overridden from ui::EventRewriter // Overridden from ui::EventRewriter
ui::EventRewriteStatus RewriteEvent( ui::EventRewriteStatus RewriteEvent(
...@@ -40,7 +48,16 @@ class SideSwipeDetector : public ui::EventRewriter { ...@@ -40,7 +48,16 @@ class SideSwipeDetector : public ui::EventRewriter {
std::unique_ptr<ui::Event>* new_event) override; std::unique_ptr<ui::Event>* new_event) override;
private: private:
friend class test::SideSwipeDetectorTest;
void SetTimerForTesting(std::unique_ptr<base::OneShotTimer> mock_timer);
void StashEvent(const ui::TouchEvent& event); void StashEvent(const ui::TouchEvent& event);
void OnCornerHoldTimerFired();
void EndCornerHold(const ui::TouchEvent& event);
void CancelCornerHoldCheck();
ui::EventRewriteStatus StartNextDispatchStashedEvents(
std::unique_ptr<ui::Event>* new_event);
const int gesture_start_width_; const int gesture_start_width_;
const int gesture_start_height_; const int gesture_start_height_;
...@@ -48,14 +65,22 @@ class SideSwipeDetector : public ui::EventRewriter { ...@@ -48,14 +65,22 @@ class SideSwipeDetector : public ui::EventRewriter {
CastGestureHandler* gesture_handler_; CastGestureHandler* gesture_handler_;
aura::Window* root_window_; aura::Window* root_window_;
CastSideSwipeOrigin current_swipe_; CastSideSwipeOrigin current_swipe_origin_;
base::ElapsedTimer current_swipe_time_; int current_pointer_id_;
base::ElapsedTimer gesture_elapsed_timer_;
std::deque<ui::TouchEvent> stashed_events_; std::deque<ui::TouchEvent> stashed_events_;
// A default gesture detector config, so we can share the same
// longpress timeout for corner hold.
ui::GestureDetector::Config gesture_detector_config_;
std::unique_ptr<base::OneShotTimer> corner_hold_timer_;
std::unique_ptr<ui::Event> corner_hold_start_event_;
bool in_corner_hold_;
CastGestureHandler::Corner current_corner_origin_;
DISALLOW_COPY_AND_ASSIGN(SideSwipeDetector); DISALLOW_COPY_AND_ASSIGN(SideSwipeDetector);
}; };
} // namespace chromecast } // namespace chromecast
#endif // CHROMECAST_GRAPHICS_GESTURES_SIDE_SWIPE_DETECTOR_H_ #endif // CHROMECAST_GRAPHICS_GESTURES_SIDE_SWIPE_DETECTOR_H_
\ No newline at end of file
...@@ -6,14 +6,25 @@ ...@@ -6,14 +6,25 @@
#include <memory> #include <memory>
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/test/simple_test_tick_clock.h"
#include "base/test/test_mock_time_task_runner.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/aura/client/screen_position_client.h" #include "ui/aura/client/screen_position_client.h"
#include "ui/aura/test/aura_test_base.h" #include "ui/aura/test/aura_test_base.h"
#include "ui/aura/test/event_generator_delegate_aura.h" #include "ui/aura/test/event_generator_delegate_aura.h"
#include "ui/aura/window.h" #include "ui/aura/window.h"
#include "ui/events/event_utils.h"
#include "ui/events/test/event_generator.h" #include "ui/events/test/event_generator.h"
#include "ui/wm/core/default_screen_position_client.h" #include "ui/wm/core/default_screen_position_client.h"
// Gmock matchers and actions that we use below.
using testing::_;
using testing::AnyOf;
using testing::Eq;
using testing::Return;
namespace chromecast { namespace chromecast {
namespace test { namespace test {
...@@ -23,6 +34,9 @@ constexpr base::TimeDelta kTimeDelay = base::TimeDelta::FromMilliseconds(100); ...@@ -23,6 +34,9 @@ constexpr base::TimeDelta kTimeDelay = base::TimeDelta::FromMilliseconds(100);
constexpr int kSwipeDistance = 50; constexpr int kSwipeDistance = 50;
constexpr int kNumSteps = 5; constexpr int kNumSteps = 5;
constexpr gfx::Point kZeroPoint{0, 0}; constexpr gfx::Point kZeroPoint{0, 0};
constexpr base::TimeDelta kHoldCornerDelay =
base::TimeDelta::FromMilliseconds(3500);
constexpr gfx::Point kNWCorner{5, 5};
} // namespace } // namespace
...@@ -49,11 +63,9 @@ class TestEventGeneratorDelegate ...@@ -49,11 +63,9 @@ class TestEventGeneratorDelegate
DISALLOW_COPY_AND_ASSIGN(TestEventGeneratorDelegate); DISALLOW_COPY_AND_ASSIGN(TestEventGeneratorDelegate);
}; };
// TODO(rdaum): Make this use gmock for all events instead of just for corners.
class TestSideSwipeGestureHandler : public CastGestureHandler { class TestSideSwipeGestureHandler : public CastGestureHandler {
public: public:
TestSideSwipeGestureHandler()
: begin_swipe_point_(kZeroPoint), end_swipe_point_(kZeroPoint) {}
~TestSideSwipeGestureHandler() override = default; ~TestSideSwipeGestureHandler() override = default;
bool CanHandleSwipe(CastSideSwipeOrigin swipe_origin) override { bool CanHandleSwipe(CastSideSwipeOrigin swipe_origin) override {
...@@ -82,6 +94,21 @@ class TestSideSwipeGestureHandler : public CastGestureHandler { ...@@ -82,6 +94,21 @@ class TestSideSwipeGestureHandler : public CastGestureHandler {
CastSideSwipeOrigin end_swipe_origin() const { return end_swipe_origin_; } CastSideSwipeOrigin end_swipe_origin() const { return end_swipe_origin_; }
gfx::Point end_swipe_point() const { return end_swipe_point_; } gfx::Point end_swipe_point() const { return end_swipe_point_; }
// Mocks.
MOCK_METHOD2(HandleScreenExit,
void(CastSideSwipeOrigin side,
const gfx::Point& touch_location));
MOCK_METHOD2(HandleScreenEnter,
void(CastSideSwipeOrigin side,
const gfx::Point& touch_location));
MOCK_CONST_METHOD0(HandledCornerHolds, CastGestureHandler::Corner());
MOCK_METHOD2(HandleCornerHold,
void(CastGestureHandler::Corner corner_origin,
const ui::TouchEvent& touch_event));
MOCK_METHOD2(HandleCornerHoldEnd,
void(CastGestureHandler::Corner corner_origin,
const ui::TouchEvent& touch_event));
private: private:
bool handle_swipe_ = true; bool handle_swipe_ = true;
...@@ -124,6 +151,17 @@ class SideSwipeDetectorTest : public aura::test::AuraTestBase { ...@@ -124,6 +151,17 @@ class SideSwipeDetectorTest : public aura::test::AuraTestBase {
gesture_handler_.get(), root_window()); gesture_handler_.get(), root_window());
test_event_handler_ = std::make_unique<TestEventHandler>(); test_event_handler_ = std::make_unique<TestEventHandler>();
root_window()->AddPostTargetHandler(test_event_handler_.get()); root_window()->AddPostTargetHandler(test_event_handler_.get());
mock_task_runner_ = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
base::Time::Now(), base::TimeTicks::Now());
auto mock_timer = std::make_unique<base::OneShotTimer>(
mock_task_runner_->GetMockTickClock());
mock_timer->SetTaskRunner(mock_task_runner_);
ui::SetEventTickClockForTesting(mock_task_runner_->GetMockTickClock());
side_swipe_detector_->SetTimerForTesting(std::move(mock_timer));
EXPECT_CALL(test_gesture_handler(), HandledCornerHolds())
.WillRepeatedly(Return(CastGestureHandler::NO_CORNERS));
} }
void TearDown() override { void TearDown() override {
...@@ -133,6 +171,52 @@ class SideSwipeDetectorTest : public aura::test::AuraTestBase { ...@@ -133,6 +171,52 @@ class SideSwipeDetectorTest : public aura::test::AuraTestBase {
aura::test::AuraTestBase::TearDown(); aura::test::AuraTestBase::TearDown();
} }
// Simulate a tap-hold event in one place.
void Hold(const gfx::Point& tap_point, const base::TimeDelta& delta) {
ui::TouchEvent press(
ui::ET_TOUCH_PRESSED, tap_point, mock_clock()->NowTicks(),
ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH,
ui::PointerDetails::kUnknownPointerId));
GetEventGenerator().Dispatch(&press);
mock_task_runner()->AdvanceMockTickClock(delta);
mock_task_runner()->FastForwardBy(delta);
ui::TouchEvent release(
ui::ET_TOUCH_RELEASED, tap_point, mock_clock()->NowTicks(),
ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH,
ui::PointerDetails::kUnknownPointerId));
GetEventGenerator().Dispatch(&release);
}
void Drag(const gfx::Point& start_point,
const base::TimeDelta& start_hold_time,
const base::TimeDelta& drag_time,
const gfx::Point& end_point,
bool end_release = true) {
ui::TouchEvent press(
ui::ET_TOUCH_PRESSED, start_point, mock_clock()->NowTicks(),
ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH,
ui::PointerDetails::kUnknownPointerId));
GetEventGenerator().Dispatch(&press);
mock_task_runner()->AdvanceMockTickClock(start_hold_time);
mock_task_runner()->FastForwardBy(start_hold_time);
ui::TouchEvent move(
ui::ET_TOUCH_MOVED, end_point, mock_clock()->NowTicks(),
ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH,
ui::PointerDetails::kUnknownPointerId));
GetEventGenerator().Dispatch(&move);
mock_task_runner()->AdvanceMockTickClock(drag_time);
mock_task_runner()->FastForwardBy(drag_time);
if (end_release) {
ui::TouchEvent release(
ui::ET_TOUCH_RELEASED, end_point, mock_clock()->NowTicks(),
ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH,
ui::PointerDetails::kUnknownPointerId));
GetEventGenerator().Dispatch(&release);
}
}
ui::test::EventGenerator& GetEventGenerator() { ui::test::EventGenerator& GetEventGenerator() {
if (!event_generator_) { if (!event_generator_) {
event_generator_.reset(new ui::test::EventGenerator( event_generator_.reset(new ui::test::EventGenerator(
...@@ -147,6 +231,14 @@ class SideSwipeDetectorTest : public aura::test::AuraTestBase { ...@@ -147,6 +231,14 @@ class SideSwipeDetectorTest : public aura::test::AuraTestBase {
TestEventHandler& test_event_handler() { return *test_event_handler_; } TestEventHandler& test_event_handler() { return *test_event_handler_; }
base::TestMockTimeTaskRunner* mock_task_runner() const {
return mock_task_runner_.get();
}
const base::TickClock* mock_clock() const {
return mock_task_runner_->GetMockTickClock();
}
private: private:
std::unique_ptr<aura::client::ScreenPositionClient> screen_position_client_; std::unique_ptr<aura::client::ScreenPositionClient> screen_position_client_;
std::unique_ptr<ui::test::EventGenerator> event_generator_; std::unique_ptr<ui::test::EventGenerator> event_generator_;
...@@ -154,6 +246,7 @@ class SideSwipeDetectorTest : public aura::test::AuraTestBase { ...@@ -154,6 +246,7 @@ class SideSwipeDetectorTest : public aura::test::AuraTestBase {
std::unique_ptr<SideSwipeDetector> side_swipe_detector_; std::unique_ptr<SideSwipeDetector> side_swipe_detector_;
std::unique_ptr<TestEventHandler> test_event_handler_; std::unique_ptr<TestEventHandler> test_event_handler_;
std::unique_ptr<TestSideSwipeGestureHandler> gesture_handler_; std::unique_ptr<TestSideSwipeGestureHandler> gesture_handler_;
scoped_refptr<base::TestMockTimeTaskRunner> mock_task_runner_;
}; };
// Test that initialization works and initial state is clean. // Test that initialization works and initial state is clean.
...@@ -175,8 +268,8 @@ TEST_F(SideSwipeDetectorTest, SwipeWithNoSystemGesture) { ...@@ -175,8 +268,8 @@ TEST_F(SideSwipeDetectorTest, SwipeWithNoSystemGesture) {
generator.GestureScrollSequence(drag_point, generator.GestureScrollSequence(drag_point,
drag_point - gfx::Vector2d(0, kSwipeDistance), drag_point - gfx::Vector2d(0, kSwipeDistance),
kTimeDelay, kNumSteps); kTimeDelay, kNumSteps);
base::RunLoop run_loop;
run_loop.RunUntilIdle(); base::RunLoop().RunUntilIdle();
EXPECT_EQ(CastSideSwipeOrigin::NONE, EXPECT_EQ(CastSideSwipeOrigin::NONE,
test_gesture_handler().begin_swipe_origin()); test_gesture_handler().begin_swipe_origin());
...@@ -193,8 +286,8 @@ TEST_F(SideSwipeDetectorTest, SwipeFromLeft) { ...@@ -193,8 +286,8 @@ TEST_F(SideSwipeDetectorTest, SwipeFromLeft) {
generator.GestureScrollSequence(drag_point, generator.GestureScrollSequence(drag_point,
drag_point + gfx::Vector2d(kSwipeDistance, 0), drag_point + gfx::Vector2d(kSwipeDistance, 0),
kTimeDelay, kNumSteps); kTimeDelay, kNumSteps);
base::RunLoop run_loop;
run_loop.RunUntilIdle(); base::RunLoop().RunUntilIdle();
EXPECT_EQ(CastSideSwipeOrigin::LEFT, EXPECT_EQ(CastSideSwipeOrigin::LEFT,
test_gesture_handler().begin_swipe_origin()); test_gesture_handler().begin_swipe_origin());
...@@ -212,8 +305,8 @@ TEST_F(SideSwipeDetectorTest, SwipeFromRight) { ...@@ -212,8 +305,8 @@ TEST_F(SideSwipeDetectorTest, SwipeFromRight) {
generator.GestureScrollSequence(drag_point, generator.GestureScrollSequence(drag_point,
drag_point - gfx::Vector2d(kSwipeDistance, 0), drag_point - gfx::Vector2d(kSwipeDistance, 0),
kTimeDelay, kNumSteps); kTimeDelay, kNumSteps);
base::RunLoop run_loop;
run_loop.RunUntilIdle(); base::RunLoop().RunUntilIdle();
EXPECT_EQ(CastSideSwipeOrigin::RIGHT, EXPECT_EQ(CastSideSwipeOrigin::RIGHT,
test_gesture_handler().begin_swipe_origin()); test_gesture_handler().begin_swipe_origin());
...@@ -230,8 +323,8 @@ TEST_F(SideSwipeDetectorTest, SwipeFromTop) { ...@@ -230,8 +323,8 @@ TEST_F(SideSwipeDetectorTest, SwipeFromTop) {
generator.GestureScrollSequence(drag_point, generator.GestureScrollSequence(drag_point,
drag_point + gfx::Vector2d(0, kSwipeDistance), drag_point + gfx::Vector2d(0, kSwipeDistance),
kTimeDelay, kNumSteps); kTimeDelay, kNumSteps);
base::RunLoop run_loop;
run_loop.RunUntilIdle(); base::RunLoop().RunUntilIdle();
EXPECT_EQ(CastSideSwipeOrigin::TOP, EXPECT_EQ(CastSideSwipeOrigin::TOP,
test_gesture_handler().begin_swipe_origin()); test_gesture_handler().begin_swipe_origin());
...@@ -249,8 +342,8 @@ TEST_F(SideSwipeDetectorTest, SwipeFromBottom) { ...@@ -249,8 +342,8 @@ TEST_F(SideSwipeDetectorTest, SwipeFromBottom) {
generator.GestureScrollSequence(drag_point, generator.GestureScrollSequence(drag_point,
drag_point - gfx::Vector2d(0, kSwipeDistance), drag_point - gfx::Vector2d(0, kSwipeDistance),
kTimeDelay, kNumSteps); kTimeDelay, kNumSteps);
base::RunLoop run_loop;
run_loop.RunUntilIdle(); base::RunLoop().RunUntilIdle();
EXPECT_EQ(CastSideSwipeOrigin::BOTTOM, EXPECT_EQ(CastSideSwipeOrigin::BOTTOM,
test_gesture_handler().begin_swipe_origin()); test_gesture_handler().begin_swipe_origin());
...@@ -272,8 +365,8 @@ TEST_F(SideSwipeDetectorTest, SwipeUnhandledIgnored) { ...@@ -272,8 +365,8 @@ TEST_F(SideSwipeDetectorTest, SwipeUnhandledIgnored) {
generator.GestureScrollSequence(drag_point, generator.GestureScrollSequence(drag_point,
drag_point - gfx::Vector2d(0, kSwipeDistance), drag_point - gfx::Vector2d(0, kSwipeDistance),
kTimeDelay, kNumSteps); kTimeDelay, kNumSteps);
base::RunLoop run_loop;
run_loop.RunUntilIdle(); base::RunLoop().RunUntilIdle();
EXPECT_EQ(CastSideSwipeOrigin::NONE, EXPECT_EQ(CastSideSwipeOrigin::NONE,
test_gesture_handler().begin_swipe_origin()); test_gesture_handler().begin_swipe_origin());
...@@ -284,5 +377,106 @@ TEST_F(SideSwipeDetectorTest, SwipeUnhandledIgnored) { ...@@ -284,5 +377,106 @@ TEST_F(SideSwipeDetectorTest, SwipeUnhandledIgnored) {
EXPECT_NE(0, test_event_handler().NumTouchEventsReceived()); EXPECT_NE(0, test_event_handler().NumTouchEventsReceived());
} }
TEST_F(SideSwipeDetectorTest, ScreenEnter) {
EXPECT_CALL(test_gesture_handler(),
HandleScreenEnter(Eq(CastSideSwipeOrigin::TOP), _))
.Times(1);
EXPECT_CALL(test_gesture_handler(),
HandleScreenExit(Eq(CastSideSwipeOrigin::TOP), _))
.Times(0);
test_gesture_handler().SetHandleSwipe(false);
gfx::Point drag_point(root_window()->bounds().width() / 2, 0);
ui::test::EventGenerator& generator = GetEventGenerator();
generator.GestureScrollSequence(drag_point,
drag_point + gfx::Vector2d(0, kSwipeDistance),
kTimeDelay, kNumSteps);
base::RunLoop().RunUntilIdle();
}
TEST_F(SideSwipeDetectorTest, ScreenExit) {
EXPECT_CALL(test_gesture_handler(),
HandleScreenExit(Eq(CastSideSwipeOrigin::TOP), _))
.Times(1);
EXPECT_CALL(test_gesture_handler(),
HandleScreenEnter(Eq(CastSideSwipeOrigin::TOP), _))
.Times(0);
test_gesture_handler().SetHandleSwipe(false);
gfx::Point drag_point(root_window()->bounds().width() / 2, 0);
ui::test::EventGenerator& generator = GetEventGenerator();
generator.GestureScrollSequence(drag_point + gfx::Vector2d(0, kSwipeDistance),
drag_point, kTimeDelay, kNumSteps);
base::RunLoop().RunUntilIdle();
}
// Test corner hold.
TEST_F(SideSwipeDetectorTest, CornerHoldTrigger) {
EXPECT_CALL(test_gesture_handler(), HandledCornerHolds())
.WillRepeatedly(Return(CastGestureHandler::TOP_LEFT_CORNER));
EXPECT_CALL(test_gesture_handler(),
HandleCornerHold(Eq(CastGestureHandler::TOP_LEFT_CORNER), _))
.Times(1);
EXPECT_CALL(test_gesture_handler(),
HandleCornerHoldEnd(Eq(CastGestureHandler::TOP_LEFT_CORNER), _))
.Times(1);
Hold(kNWCorner, kHoldCornerDelay);
base::RunLoop().RunUntilIdle();
}
// Test that it's a corner hold if we hold long enough in corner then drag out.
TEST_F(SideSwipeDetectorTest, CornerHoldDragOutOfCorner) {
EXPECT_CALL(test_gesture_handler(), HandledCornerHolds())
.WillRepeatedly(Return(CastGestureHandler::TOP_LEFT_CORNER));
EXPECT_CALL(test_gesture_handler(), HandleCornerHold(_, _)).Times(1);
EXPECT_CALL(test_gesture_handler(), HandleCornerHoldEnd(_, _)).Times(1);
Drag(kNWCorner, kHoldCornerDelay, kHoldCornerDelay,
kNWCorner + gfx::Vector2d(100, 100));
base::RunLoop().RunUntilIdle();
}
// Test that it's not a corner hold if it doesn't sit in the corner long enough.
TEST_F(SideSwipeDetectorTest, CornerHoldCancelNotLongEnough) {
EXPECT_CALL(test_gesture_handler(), HandledCornerHolds())
.WillRepeatedly(Return(CastGestureHandler::TOP_LEFT_CORNER));
EXPECT_CALL(test_gesture_handler(), HandleCornerHold(_, _)).Times(0);
EXPECT_CALL(test_gesture_handler(), HandleCornerHoldEnd(_, _)).Times(0);
Hold(kNWCorner, base::TimeDelta::FromMilliseconds(5));
base::RunLoop().RunUntilIdle();
}
// Test that it's not a corner hold if it drags out of the corner before the
// hold time.
TEST_F(SideSwipeDetectorTest, CornerHoldCancelDragOutOfCorner) {
EXPECT_CALL(test_gesture_handler(), HandledCornerHolds())
.WillRepeatedly(Return(CastGestureHandler::TOP_LEFT_CORNER));
EXPECT_CALL(test_gesture_handler(), HandleCornerHold(_, _)).Times(0);
EXPECT_CALL(test_gesture_handler(), HandleCornerHoldEnd(_, _)).Times(0);
Drag(kNWCorner, base::TimeDelta::FromMilliseconds(5), kHoldCornerDelay,
kNWCorner + gfx::Vector2d(100, 100));
base::RunLoop().RunUntilIdle();
}
// Test that it's not a corner hold if it drags out of the corner before the
// hold time, even with the finger held.
TEST_F(SideSwipeDetectorTest, CornerHoldCancelDragOutOfCornerButHeld) {
EXPECT_CALL(test_gesture_handler(), HandledCornerHolds())
.WillRepeatedly(Return(CastGestureHandler::TOP_LEFT_CORNER));
EXPECT_CALL(test_gesture_handler(), HandleCornerHold(_, _)).Times(0);
EXPECT_CALL(test_gesture_handler(), HandleCornerHoldEnd(_, _)).Times(0);
Drag(kNWCorner, base::TimeDelta::FromMilliseconds(5), kHoldCornerDelay,
kNWCorner + gfx::Vector2d(100, 100), false /* end_release */);
base::RunLoop().RunUntilIdle();
}
} // namespace test } // namespace test
} // namespace chromecast } // namespace chromecast
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