#include "chromecast/graphics/gestures/side_swipe_detector.h"
#include <deque>
#include "base/auto_reset.h"
#include "chromecast/base/chromecast_switches.h"
#include "ui/aura/window.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/aura/window_tree_host.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/events/event.h"
#include "ui/events/event_rewriter.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/wm/core/coordinate_conversion.h"
namespace chromecast {
namespace {
constexpr int kDefaultSideGestureStartWidth = 35;
constexpr int kDefaultSideGestureStartHeight = 35;
constexpr base::TimeDelta kGestureMarginEventsTimeLimit =
base::Milliseconds(500);
int BottomGestureStartHeight() {
return GetSwitchValueInt(
switches::kBottomSystemGestureStartHeight,
GetSwitchValueInt(switches::kSystemGestureStartHeight,
kDefaultSideGestureStartHeight));
}
}
SideSwipeDetector::SideSwipeDetector(CastGestureHandler* gesture_handler,
aura::Window* root_window)
: gesture_start_width_(GetSwitchValueInt(switches::kSystemGestureStartWidth,
kDefaultSideGestureStartWidth)),
gesture_start_height_(
GetSwitchValueInt(switches::kSystemGestureStartHeight,
kDefaultSideGestureStartHeight)),
bottom_gesture_start_height_(BottomGestureStartHeight()),
gesture_handler_(gesture_handler),
root_window_(root_window),
current_swipe_(CastSideSwipeOrigin::NONE),
current_pointer_id_(ui::kPointerIdUnknown) {
DCHECK(gesture_handler);
DCHECK(root_window);
root_window_->GetHost()->GetEventSource()->AddEventRewriter(this);
}
SideSwipeDetector::~SideSwipeDetector() {
root_window_->GetHost()->GetEventSource()->RemoveEventRewriter(this);
}
CastSideSwipeOrigin SideSwipeDetector::GetDragPosition(
const gfx::Point& point,
const gfx::Rect& screen_bounds) const {
if (point.y() < (screen_bounds.y() + gesture_start_height_)) {
return CastSideSwipeOrigin::TOP;
}
if (point.x() < (screen_bounds.x() + gesture_start_width_)) {
return CastSideSwipeOrigin::LEFT;
}
if (point.x() >
(screen_bounds.x() + screen_bounds.width() - gesture_start_width_)) {
return CastSideSwipeOrigin::RIGHT;
}
if (point.y() > (screen_bounds.y() + screen_bounds.height() -
bottom_gesture_start_height_)) {
return CastSideSwipeOrigin::BOTTOM;
}
return CastSideSwipeOrigin::NONE;
}
void SideSwipeDetector::StashEvent(const ui::TouchEvent& event) {
if (current_swipe_time_.Elapsed() > kGestureMarginEventsTimeLimit) {
stashed_events_.clear();
return;
}
stashed_events_.push_back(event);
}
ui::EventDispatchDetails SideSwipeDetector::RewriteEvent(
const ui::Event& event,
const Continuation continuation) {
if (!event.IsTouchEvent()) {
return SendEvent(continuation, &event);
}
const ui::TouchEvent* touch_event = event.AsTouchEvent();
gfx::Point touch_location = touch_event->root_location();
root_window_->GetHost()->ConvertPixelsToDIP(&touch_location);
gfx::Rect screen_bounds =
display::Screen::Get()->GetDisplayNearestPoint(touch_location).bounds();
CastSideSwipeOrigin side_swipe_origin =
GetDragPosition(touch_location, screen_bounds);
if (current_swipe_ == CastSideSwipeOrigin::NONE &&
side_swipe_origin != CastSideSwipeOrigin::NONE) {
if (!gesture_handler_->CanHandleSwipe(side_swipe_origin)) {
return SendEvent(continuation, &event);
}
if (touch_event->type() != ui::EventType::kTouchPressed) {
return SendEvent(continuation, &event);
}
current_swipe_ = side_swipe_origin;
current_pointer_id_ = touch_event->pointer_details().id;
gesture_handler_->HandleSideSwipe(CastSideSwipeEvent::BEGIN,
side_swipe_origin, touch_location);
DVLOG(1) << "side swipe gesture begin @ " << touch_location.ToString();
current_swipe_time_ = base::ElapsedTimer();
StashEvent(*touch_event);
root_window_->CleanupGestureState();
return DiscardEvent(continuation);
}
if (current_swipe_ == CastSideSwipeOrigin::NONE) {
return SendEvent(continuation, &event);
}
if (touch_event->pointer_details().id != current_pointer_id_) {
return DiscardEvent(continuation);
}
StashEvent(*touch_event);
if (touch_event->type() == ui::EventType::kTouchReleased) {
DVLOG(1) << "gesture release; time since press: "
<< current_swipe_time_.Elapsed().InMilliseconds() << "ms @ "
<< touch_location.ToString();
gesture_handler_->HandleSideSwipe(CastSideSwipeEvent::END, current_swipe_,
touch_location);
current_swipe_ = CastSideSwipeOrigin::NONE;
current_pointer_id_ = ui::kPointerIdUnknown;
if (side_swipe_origin != CastSideSwipeOrigin::NONE &&
!stashed_events_.empty()) {
ui::EventDispatchDetails details;
for (const auto& it : stashed_events_) {
details = SendEvent(continuation, &it);
if (details.dispatcher_destroyed)
break;
}
stashed_events_.clear();
return details;
}
stashed_events_.clear();
return DiscardEvent(continuation);
}
gesture_handler_->HandleSideSwipe(CastSideSwipeEvent::CONTINUE,
current_swipe_, touch_location);
DVLOG(1) << "gesture continue; time since press: "
<< current_swipe_time_.Elapsed().InMilliseconds() << "ms @ "
<< touch_location.ToString();
return DiscardEvent(continuation);
}
}