1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "athena/home/home_card_gesture_manager.h"
6
7#include "athena/home/home_card_constants.h"
8#include "ui/events/event.h"
9
10namespace athena {
11
12namespace {
13
14// The maximum height, in pixels, of a home card with final state
15// VISIBLE_MINIMIZED.
16const int kMinimizedFinalStateMaxHeight = 50 + kHomeCardMinimizedHeight;
17
18// The maximum height, in pixels, of an initially centered home card with final
19// state VISIBLE_MINIMIZED.
20const int kMinimizedFinalStateMaxHeightInitiallyCentered =
21    90 + kHomeCardMinimizedHeight;
22
23// The minimum height, as a percentage of the screen height, of a home card with
24// final state VISIBLE_CENTERED.
25const float kCenteredFinalStateMinScreenRatio = 0.5f;
26
27// The minimum height, as a percentage of the screen height, of an initially
28// minimized home card with final state VISIBLE_CENTERED.
29const float kCenteredFinalStateMinScreenRatioInitiallyMinimized = 0.3f;
30
31}
32
33HomeCardGestureManager::HomeCardGestureManager(Delegate* delegate,
34                                               const gfx::Rect& screen_bounds)
35    : delegate_(delegate),
36      original_state_(HomeCard::HIDDEN),
37      y_offset_(0),
38      last_estimated_height_(0),
39      screen_bounds_(screen_bounds) {}
40
41HomeCardGestureManager::~HomeCardGestureManager() {}
42
43void HomeCardGestureManager::ProcessGestureEvent(ui::GestureEvent* event) {
44  switch (event->type()) {
45    case ui::ET_GESTURE_SCROLL_BEGIN:
46      y_offset_ = event->location().y();
47      original_state_ = HomeCard::Get()->GetState();
48      DCHECK_NE(HomeCard::HIDDEN, original_state_);
49      event->SetHandled();
50      break;
51    case ui::ET_GESTURE_SCROLL_END:
52      event->SetHandled();
53      delegate_->OnGestureEnded(GetFinalState(), false);
54      break;
55    case ui::ET_GESTURE_SCROLL_UPDATE:
56      UpdateScrollState(*event);
57      break;
58    case ui::ET_SCROLL_FLING_START: {
59      const ui::GestureEventDetails& details = event->details();
60      const float kFlingCompletionVelocity = 100.0f;
61      HomeCard::State final_state = GetFinalState();
62
63      // When the user does not drag far enough to switch the final state, but
64      // a fling happens at the end of the gesture, the state should change
65      // based on the direction of the fling.
66      // Checking |final_state| == |original_state| may cause unexpected results
67      // for gestures where the user flings in the opposite direction that they
68      // moved the home card (e.g. drag home card up from minimized state and
69      // then fling down)
70      // TODO(mukai): Consider this case once reported.
71      bool is_fling = ::fabs(details.velocity_y()) > kFlingCompletionVelocity;
72      if (final_state == original_state_ && is_fling) {
73        if (details.velocity_y() > 0) {
74          final_state = std::min(HomeCard::VISIBLE_MINIMIZED,
75                                 static_cast<HomeCard::State>(final_state + 1));
76        } else {
77          final_state = std::max(HomeCard::VISIBLE_CENTERED,
78                                 static_cast<HomeCard::State>(final_state - 1));
79        }
80      }
81      delegate_->OnGestureEnded(final_state, is_fling);
82      break;
83    }
84    default:
85      // do nothing.
86      break;
87  }
88}
89
90HomeCard::State HomeCardGestureManager::GetFinalState() const {
91  int max_height = (original_state_ == HomeCard::VISIBLE_CENTERED)
92      ? kMinimizedFinalStateMaxHeightInitiallyCentered
93      : kMinimizedFinalStateMaxHeight;
94  if (last_estimated_height_ < max_height)
95    return HomeCard::VISIBLE_MINIMIZED;
96
97  float ratio = (original_state_ == HomeCard::VISIBLE_MINIMIZED)
98      ? kCenteredFinalStateMinScreenRatioInitiallyMinimized
99      : kCenteredFinalStateMinScreenRatio;
100  if (last_estimated_height_ < screen_bounds_.height() * ratio)
101    return HomeCard::VISIBLE_BOTTOM;
102
103  return HomeCard::VISIBLE_CENTERED;
104}
105
106void HomeCardGestureManager::UpdateScrollState(const ui::GestureEvent& event) {
107  last_estimated_height_ =
108      screen_bounds_.height() - event.root_location().y() + y_offset_;
109
110  if (last_estimated_height_ <= kHomeCardMinimizedHeight) {
111    delegate_->OnGestureProgressed(
112        HomeCard::VISIBLE_BOTTOM, HomeCard::VISIBLE_MINIMIZED, 1.0f);
113    return;
114  }
115
116  HomeCard::State bigger_state = HomeCard::VISIBLE_BOTTOM;
117  float smaller_height = kHomeCardMinimizedHeight;
118  float bigger_height = kHomeCardHeight;
119  if (last_estimated_height_ > kHomeCardHeight) {
120    bigger_state = HomeCard::VISIBLE_CENTERED;
121    smaller_height = kHomeCardHeight;
122    bigger_height = screen_bounds_.height();
123  }
124
125  // The finger is between two states.
126  float progress = (last_estimated_height_ - smaller_height) /
127      (bigger_height - smaller_height);
128  progress = std::min(1.0f, std::max(0.0f, progress));
129
130  delegate_->OnGestureProgressed(
131      static_cast<HomeCard::State>(bigger_state + 1),
132      bigger_state,
133      progress);
134}
135
136}  // namespace athena
137