Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(53)

Side by Side Diff: content/browser/android/overscroll_controller_android.cc

Issue 679493002: [Android] Add a native pull-to-refresh overscroll effect (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Code review Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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 "content/browser/android/overscroll_controller_android.h"
6
7 #include "base/android/build_info.h"
8 #include "cc/layers/layer.h"
9 #include "cc/output/compositor_frame_metadata.h"
10 #include "content/browser/android/content_view_core_impl.h"
11 #include "content/browser/android/edge_effect.h"
12 #include "content/browser/android/edge_effect_l.h"
13 #include "content/common/input/did_overscroll_params.h"
14 #include "ui/base/android/window_android.h"
15 #include "ui/base/android/window_android_compositor.h"
16
17 namespace content {
18 namespace {
19
20 // Used for conditional creation of EdgeEffect types for the overscroll glow.
21 const int kAndroidLSDKVersion = 21;
22
23 ui::SystemUIResourceManager* GetSystemUIResourceManager(
24 ContentViewCore* content_view_core) {
25 ui::WindowAndroid* window = content_view_core->GetWindowAndroid();
26 DCHECK(window);
27 ui::WindowAndroidCompositor* compositor = window->GetCompositor();
28 DCHECK(compositor);
29 return &compositor->GetSystemUIResourceManager();
30 }
31
32 scoped_ptr<EdgeEffectBase> CreateGlowEdgeEffect(
33 ui::SystemUIResourceManager* resource_manager,
34 float dpi_scale) {
35 DCHECK(resource_manager);
36 static bool use_l_flavoured_effect =
37 base::android::BuildInfo::GetInstance()->sdk_int() >= kAndroidLSDKVersion;
38 if (use_l_flavoured_effect)
39 return scoped_ptr<EdgeEffectBase>(new EdgeEffectL(resource_manager));
40
41 return scoped_ptr<EdgeEffectBase>(
42 new EdgeEffect(resource_manager, dpi_scale));
43 }
44
45 } // namespace
46
47 OverscrollControllerAndroid::OverscrollControllerAndroid(
48 ContentViewCoreImpl* content_view_core)
49 : WebContentsObserver(content_view_core->GetWebContents()),
50 content_view_core_(content_view_core),
51 enabled_(true),
52 glow_effect_(base::Bind(&CreateGlowEdgeEffect,
53 GetSystemUIResourceManager(content_view_core),
54 content_view_core->GetDpiScale())),
55 refresh_effect_(GetSystemUIResourceManager(content_view_core), this),
56 triggered_refresh_active_(false) {
57 }
58
59 OverscrollControllerAndroid::~OverscrollControllerAndroid() {
60 }
61
62 bool OverscrollControllerAndroid::WillHandleGestureEvent(
63 const blink::WebGestureEvent& event) {
64 if (!enabled_)
65 return false;
66
67 bool handled = false;
68 bool maybe_needs_animate = false;
69 switch (event.type) {
70 case blink::WebInputEvent::GestureScrollBegin:
71 refresh_effect_.OnScrollBegin();
72 break;
73
74 case blink::WebInputEvent::GestureScrollUpdate: {
75 gfx::Vector2dF scroll_delta(event.data.scrollUpdate.deltaX,
76 event.data.scrollUpdate.deltaY);
77 scroll_delta.Scale(GetDpiScale());
78 maybe_needs_animate = true;
79 handled = refresh_effect_.WillHandleScrollUpdate(scroll_delta);
80 } break;
81
82 case blink::WebInputEvent::GestureScrollEnd:
83 refresh_effect_.OnScrollEnd(gfx::Vector2dF());
84 maybe_needs_animate = true;
85 break;
86
87 case blink::WebInputEvent::GestureFlingStart: {
88 gfx::Vector2dF scroll_velocity(event.data.flingStart.velocityX,
89 event.data.flingStart.velocityY);
90 scroll_velocity.Scale(GetDpiScale());
91 refresh_effect_.OnScrollEnd(scroll_velocity);
92 if (refresh_effect_.IsActive()) {
93 // TODO(jdduke): Figure out a cleaner way of suppressing a fling.
94 // It's important that the any downstream code sees a scroll-ending
95 // event (in this case GestureFlingStart) if it has seen a scroll begin.
96 // Thus, we cannot simply consume the fling. Changing the event type to
97 // a GestureScrollEnd might work in practice, but could lead to
98 // unexpected results. For now, simply truncate the fling velocity, but
99 // not to zero as downstream code may not expect a zero-velocity fling.
100 blink::WebGestureEvent& modified_event =
101 const_cast<blink::WebGestureEvent&>(event);
102 modified_event.data.flingStart.velocityX = .01f;
103 modified_event.data.flingStart.velocityY = .01f;
104 }
105 maybe_needs_animate = true;
106 } break;
107
108 default:
109 break;
110 }
111
112 if (maybe_needs_animate && refresh_effect_.IsActive())
113 content_view_core_->SetNeedsAnimate();
114
115 return handled;
116 }
117
118 void OverscrollControllerAndroid::OnGestureEventAck(
119 const blink::WebGestureEvent& event,
120 InputEventAckState ack_result) {
121 if (!enabled_)
122 return;
123
124 if (event.type == blink::WebInputEvent::GestureScrollUpdate) {
125 // The effect should only be allowed if both the causal touch events go
126 // unconsumed and the generated scroll events go unconsumed.
127 // TODO(jdduke): Prevent activation if the first touchmove was consumed,
128 // i.e., the first GSU was prevented.
129 bool scroll_consumed = ack_result == INPUT_EVENT_ACK_STATE_CONSUMED;
130 OverscrollRefresh::ActivationAllowance activation_allowance =
131 scroll_consumed ? OverscrollRefresh::PREVENT_ACTIVATION
132 : OverscrollRefresh::ALLOW_ACTIVATION;
133 refresh_effect_.OnScrollUpdateAck(activation_allowance);
134 }
135 }
136
137 void OverscrollControllerAndroid::OnOverscrolled(
138 const DidOverscrollParams& params) {
139 if (!enabled_)
140 return;
141
142 if (refresh_effect_.IsActive() ||
143 refresh_effect_.IsPendingScrollUpdateAck()) {
144 // An active (or potentially active) refresh effect should always pre-empt
145 // the passive glow effect.
146 return;
147 }
148
149 const float dpi_scale = GetDpiScale();
150 if (glow_effect_.OnOverscrolled(
151 base::TimeTicks::Now(),
152 gfx::ScaleVector2d(params.accumulated_overscroll, dpi_scale),
153 gfx::ScaleVector2d(params.latest_overscroll_delta, dpi_scale),
154 gfx::ScaleVector2d(params.current_fling_velocity, dpi_scale),
155 gfx::ScaleVector2d(
156 params.causal_event_viewport_point.OffsetFromOrigin(),
157 dpi_scale))) {
158 content_view_core_->SetNeedsAnimate();
159 }
160 }
161
162 bool OverscrollControllerAndroid::Animate(base::TimeTicks current_time) {
163 if (!enabled_)
164 return false;
165
166 scoped_refptr<cc::Layer> parent = content_view_core_->GetLayer();
167 bool needs_animate = refresh_effect_.Animate(current_time, parent.get());
168 needs_animate |= glow_effect_.Animate(current_time, parent.get());
169 return needs_animate;
170 }
171
172 void OverscrollControllerAndroid::OnFrameMetadataUpdated(
173 const cc::CompositorFrameMetadata& frame_metadata) {
174 const float scale_factor =
175 frame_metadata.page_scale_factor * frame_metadata.device_scale_factor;
176 gfx::SizeF viewport_size =
177 gfx::ScaleSize(frame_metadata.scrollable_viewport_size, scale_factor);
178 gfx::SizeF content_size =
179 gfx::ScaleSize(frame_metadata.root_layer_size, scale_factor);
180 gfx::Vector2dF content_scroll_offset =
181 gfx::ScaleVector2d(frame_metadata.root_scroll_offset, scale_factor);
182
183 refresh_effect_.UpdateDisplay(viewport_size, content_scroll_offset);
184 glow_effect_.UpdateDisplay(viewport_size, content_size,
185 content_scroll_offset);
186 }
187
188 void OverscrollControllerAndroid::Enable() {
189 enabled_ = true;
190 }
191
192 void OverscrollControllerAndroid::Disable() {
193 if (!enabled_)
194 return;
195 enabled_ = false;
196 if (!enabled_) {
197 refresh_effect_.Reset();
198 glow_effect_.Reset();
199 }
200 }
201
202 void OverscrollControllerAndroid::DidNavigateMainFrame(
203 const LoadCommittedDetails& details,
204 const FrameNavigateParams& params) {
205 // Once the main frame has navigated, there's little need to further animate
206 // the reload effect. Note that the effect will naturally time out should the
207 // reload be interruped for any reason.
208 triggered_refresh_active_ = false;
209 }
210
211 void OverscrollControllerAndroid::TriggerRefresh() {
212 triggered_refresh_active_ = false;
213 WebContents* web_contents = content_view_core_->GetWebContents();
214 if (web_contents) {
215 triggered_refresh_active_ = true;
216 web_contents->ReloadFocusedFrame(false);
217 }
218 }
219
220 bool OverscrollControllerAndroid::IsTriggeredRefreshActive() const {
221 return triggered_refresh_active_;
222 }
223
224 float OverscrollControllerAndroid::GetDpiScale() const {
225 return content_view_core_->GetDpiScale();
226 }
227
228 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698