[Mac] Implement Ambient Light API
[chromium-blink-merge.git] / content / browser / android / overscroll_controller_android.cc
blob76a6c473a84eea520a5213eb7b1a85e433665948
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.
5 #include "content/browser/android/overscroll_controller_android.h"
7 #include "base/android/build_info.h"
8 #include "base/command_line.h"
9 #include "cc/layers/layer.h"
10 #include "cc/output/compositor_frame_metadata.h"
11 #include "content/browser/android/edge_effect.h"
12 #include "content/browser/android/edge_effect_l.h"
13 #include "content/browser/web_contents/web_contents_impl.h"
14 #include "content/common/input/did_overscroll_params.h"
15 #include "content/public/browser/navigation_controller.h"
16 #include "content/public/browser/user_metrics.h"
17 #include "content/public/common/content_switches.h"
18 #include "third_party/WebKit/public/web/WebInputEvent.h"
19 #include "ui/android/resources/resource_manager.h"
20 #include "ui/base/android/window_android_compositor.h"
21 #include "ui/base/l10n/l10n_util_android.h"
23 namespace content {
24 namespace {
26 // Used for conditional creation of EdgeEffect types for the overscroll glow.
27 const int kAndroidLSDKVersion = 21;
29 // Default offset in dips from the top of the view beyond which the refresh
30 // action will be activated.
31 const int kDefaultRefreshDragTargetDips = 64;
33 scoped_ptr<EdgeEffectBase> CreateGlowEdgeEffect(
34 ui::ResourceManager* resource_manager,
35 float dpi_scale) {
36 DCHECK(resource_manager);
37 static bool use_l_flavoured_effect =
38 base::android::BuildInfo::GetInstance()->sdk_int() >= kAndroidLSDKVersion;
39 if (use_l_flavoured_effect)
40 return scoped_ptr<EdgeEffectBase>(new EdgeEffectL(resource_manager));
42 return scoped_ptr<EdgeEffectBase>(
43 new EdgeEffect(resource_manager, dpi_scale));
46 scoped_ptr<OverscrollGlow> CreateGlowEffect(OverscrollGlowClient* client,
47 float dpi_scale) {
48 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
49 switches::kDisableOverscrollEdgeEffect)) {
50 return nullptr;
53 return make_scoped_ptr(new OverscrollGlow(client));
56 scoped_ptr<OverscrollRefresh> CreateRefreshEffect(
57 ui::WindowAndroidCompositor* compositor,
58 OverscrollRefreshClient* client,
59 float dpi_scale) {
60 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
61 switches::kDisablePullToRefreshEffect)) {
62 return nullptr;
65 return make_scoped_ptr(new OverscrollRefresh(
66 &compositor->GetResourceManager(), client,
67 kDefaultRefreshDragTargetDips * dpi_scale, l10n_util::IsLayoutRtl()));
70 } // namespace
72 OverscrollControllerAndroid::OverscrollControllerAndroid(
73 WebContents* web_contents,
74 ui::WindowAndroidCompositor* compositor,
75 float dpi_scale)
76 : compositor_(compositor),
77 dpi_scale_(dpi_scale),
78 enabled_(true),
79 glow_effect_(CreateGlowEffect(this, dpi_scale)),
80 refresh_effect_(CreateRefreshEffect(compositor, this, dpi_scale)),
81 triggered_refresh_active_(false),
82 is_fullscreen_(static_cast<WebContentsImpl*>(web_contents)
83 ->IsFullscreenForCurrentTab()) {
84 DCHECK(web_contents);
85 DCHECK(compositor);
86 if (refresh_effect_)
87 Observe(web_contents);
90 OverscrollControllerAndroid::~OverscrollControllerAndroid() {
93 bool OverscrollControllerAndroid::WillHandleGestureEvent(
94 const blink::WebGestureEvent& event) {
95 if (!enabled_)
96 return false;
98 // Suppress refresh detection for fullscreen web apps.
99 if (!refresh_effect_ || is_fullscreen_)
100 return false;
102 bool handled = false;
103 bool maybe_needs_animate = false;
104 switch (event.type) {
105 case blink::WebInputEvent::GestureScrollBegin:
106 refresh_effect_->OnScrollBegin();
107 break;
109 case blink::WebInputEvent::GestureScrollUpdate: {
110 gfx::Vector2dF scroll_delta(event.data.scrollUpdate.deltaX,
111 event.data.scrollUpdate.deltaY);
112 scroll_delta.Scale(dpi_scale_);
113 maybe_needs_animate = true;
114 handled = refresh_effect_->WillHandleScrollUpdate(scroll_delta);
115 } break;
117 case blink::WebInputEvent::GestureScrollEnd:
118 refresh_effect_->OnScrollEnd(gfx::Vector2dF());
119 maybe_needs_animate = true;
120 break;
122 case blink::WebInputEvent::GestureFlingStart: {
123 gfx::Vector2dF scroll_velocity(event.data.flingStart.velocityX,
124 event.data.flingStart.velocityY);
125 scroll_velocity.Scale(dpi_scale_);
126 refresh_effect_->OnScrollEnd(scroll_velocity);
127 if (refresh_effect_->IsActive()) {
128 // TODO(jdduke): Figure out a cleaner way of suppressing a fling.
129 // It's important that the any downstream code sees a scroll-ending
130 // event (in this case GestureFlingStart) if it has seen a scroll begin.
131 // Thus, we cannot simply consume the fling. Changing the event type to
132 // a GestureScrollEnd might work in practice, but could lead to
133 // unexpected results. For now, simply truncate the fling velocity, but
134 // not to zero as downstream code may not expect a zero-velocity fling.
135 blink::WebGestureEvent& modified_event =
136 const_cast<blink::WebGestureEvent&>(event);
137 modified_event.data.flingStart.velocityX = .01f;
138 modified_event.data.flingStart.velocityY = .01f;
140 maybe_needs_animate = true;
141 } break;
143 case blink::WebInputEvent::GesturePinchBegin:
144 refresh_effect_->ReleaseWithoutActivation();
145 break;
147 default:
148 break;
151 if (maybe_needs_animate && refresh_effect_->IsActive())
152 SetNeedsAnimate();
154 return handled;
157 void OverscrollControllerAndroid::OnGestureEventAck(
158 const blink::WebGestureEvent& event,
159 InputEventAckState ack_result) {
160 if (!enabled_)
161 return;
163 // The overscroll effect requires an explicit release signal that may not be
164 // sent from the renderer compositor.
165 if (event.type == blink::WebInputEvent::GestureScrollEnd ||
166 event.type == blink::WebInputEvent::GestureFlingStart) {
167 OnOverscrolled(DidOverscrollParams());
170 if (event.type == blink::WebInputEvent::GestureScrollUpdate &&
171 refresh_effect_) {
172 // The effect should only be allowed if both the causal touch events go
173 // unconsumed and the generated scroll events go unconsumed.
174 bool consumed = ack_result == INPUT_EVENT_ACK_STATE_CONSUMED ||
175 event.data.scrollUpdate.previousUpdateInSequencePrevented;
176 refresh_effect_->OnScrollUpdateAck(consumed);
180 void OverscrollControllerAndroid::OnOverscrolled(
181 const DidOverscrollParams& params) {
182 if (!enabled_)
183 return;
185 if (refresh_effect_ && (refresh_effect_->IsActive() ||
186 refresh_effect_->IsAwaitingScrollUpdateAck())) {
187 // An active (or potentially active) refresh effect should always pre-empt
188 // the passive glow effect.
189 return;
192 if (glow_effect_ &&
193 glow_effect_->OnOverscrolled(
194 base::TimeTicks::Now(),
195 gfx::ScaleVector2d(params.accumulated_overscroll, dpi_scale_),
196 gfx::ScaleVector2d(params.latest_overscroll_delta, dpi_scale_),
197 gfx::ScaleVector2d(params.current_fling_velocity, dpi_scale_),
198 gfx::ScaleVector2d(
199 params.causal_event_viewport_point.OffsetFromOrigin(),
200 dpi_scale_))) {
201 SetNeedsAnimate();
205 bool OverscrollControllerAndroid::Animate(base::TimeTicks current_time,
206 cc::Layer* parent_layer) {
207 DCHECK(parent_layer);
208 if (!enabled_)
209 return false;
211 bool needs_animate = false;
212 if (refresh_effect_)
213 needs_animate |= refresh_effect_->Animate(current_time, parent_layer);
214 if (glow_effect_)
215 needs_animate |= glow_effect_->Animate(current_time, parent_layer);
216 return needs_animate;
219 void OverscrollControllerAndroid::OnFrameMetadataUpdated(
220 const cc::CompositorFrameMetadata& frame_metadata) {
221 if (!refresh_effect_ && !glow_effect_)
222 return;
224 const float scale_factor =
225 frame_metadata.page_scale_factor * frame_metadata.device_scale_factor;
226 gfx::SizeF viewport_size =
227 gfx::ScaleSize(frame_metadata.scrollable_viewport_size, scale_factor);
228 gfx::SizeF content_size =
229 gfx::ScaleSize(frame_metadata.root_layer_size, scale_factor);
230 gfx::Vector2dF content_scroll_offset =
231 gfx::ScaleVector2d(frame_metadata.root_scroll_offset, scale_factor);
233 if (refresh_effect_)
234 refresh_effect_->UpdateDisplay(viewport_size, content_scroll_offset);
236 if (glow_effect_) {
237 glow_effect_->UpdateDisplay(viewport_size, content_size,
238 content_scroll_offset);
242 void OverscrollControllerAndroid::Enable() {
243 enabled_ = true;
246 void OverscrollControllerAndroid::Disable() {
247 if (!enabled_)
248 return;
249 enabled_ = false;
250 if (!enabled_) {
251 if (refresh_effect_)
252 refresh_effect_->Reset();
253 if (glow_effect_)
254 glow_effect_->Reset();
258 void OverscrollControllerAndroid::DidNavigateMainFrame(
259 const LoadCommittedDetails& details,
260 const FrameNavigateParams& params) {
261 // Once the main frame has navigated, there's little need to further animate
262 // the reload effect. Note that the effect will naturally time out should the
263 // reload be interruped for any reason.
264 triggered_refresh_active_ = false;
267 void OverscrollControllerAndroid::DidToggleFullscreenModeForTab(
268 bool entered_fullscreen) {
269 if (is_fullscreen_ == entered_fullscreen)
270 return;
271 is_fullscreen_ = entered_fullscreen;
272 if (is_fullscreen_)
273 refresh_effect_->ReleaseWithoutActivation();
276 void OverscrollControllerAndroid::TriggerRefresh() {
277 triggered_refresh_active_ = false;
278 if (!web_contents())
279 return;
281 triggered_refresh_active_ = true;
282 RecordAction(base::UserMetricsAction("MobilePullGestureReload"));
283 web_contents()->GetController().Reload(true);
286 bool OverscrollControllerAndroid::IsStillRefreshing() const {
287 return triggered_refresh_active_;
290 scoped_ptr<EdgeEffectBase> OverscrollControllerAndroid::CreateEdgeEffect() {
291 return CreateGlowEdgeEffect(&compositor_->GetResourceManager(), dpi_scale_);
294 void OverscrollControllerAndroid::SetNeedsAnimate() {
295 compositor_->SetNeedsAnimate();
298 } // namespace content