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

Unified 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: Fix findbugs... 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « content/browser/android/overscroll_controller_android.h ('k') | content/browser/android/overscroll_glow.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: content/browser/android/overscroll_controller_android.cc
diff --git a/content/browser/android/overscroll_controller_android.cc b/content/browser/android/overscroll_controller_android.cc
new file mode 100644
index 0000000000000000000000000000000000000000..db35ea49b6584653636c33e8241022b48dcd9c56
--- /dev/null
+++ b/content/browser/android/overscroll_controller_android.cc
@@ -0,0 +1,227 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/android/overscroll_controller_android.h"
+
+#include "base/android/build_info.h"
+#include "base/bind.h"
+#include "cc/layers/layer.h"
+#include "cc/output/compositor_frame_metadata.h"
+#include "content/browser/android/edge_effect.h"
+#include "content/browser/android/edge_effect_l.h"
+#include "content/common/input/did_overscroll_params.h"
+#include "content/public/browser/web_contents.h"
+#include "third_party/WebKit/public/web/WebInputEvent.h"
+#include "ui/base/android/window_android_compositor.h"
+
+namespace content {
+namespace {
+
+// Used for conditional creation of EdgeEffect types for the overscroll glow.
+const int kAndroidLSDKVersion = 21;
+
+scoped_ptr<EdgeEffectBase> CreateGlowEdgeEffect(
+ ui::SystemUIResourceManager* resource_manager,
+ float dpi_scale) {
+ DCHECK(resource_manager);
+ static bool use_l_flavoured_effect =
+ base::android::BuildInfo::GetInstance()->sdk_int() >= kAndroidLSDKVersion;
+ if (use_l_flavoured_effect)
+ return scoped_ptr<EdgeEffectBase>(new EdgeEffectL(resource_manager));
+
+ return scoped_ptr<EdgeEffectBase>(
+ new EdgeEffect(resource_manager, dpi_scale));
+}
+
+} // namespace
+
+OverscrollControllerAndroid::OverscrollControllerAndroid(
+ WebContents* web_contents,
+ ui::WindowAndroidCompositor* compositor,
+ float dpi_scale)
+ : WebContentsObserver(web_contents),
+ compositor_(compositor),
+ dpi_scale_(dpi_scale),
+ enabled_(true),
+ glow_effect_(base::Bind(&CreateGlowEdgeEffect,
+ &compositor->GetSystemUIResourceManager(),
+ dpi_scale_)),
+ refresh_effect_(&compositor->GetSystemUIResourceManager(), this),
+ triggered_refresh_active_(false) {
+ DCHECK(web_contents);
+}
+
+OverscrollControllerAndroid::~OverscrollControllerAndroid() {
+}
+
+bool OverscrollControllerAndroid::WillHandleGestureEvent(
+ const blink::WebGestureEvent& event) {
+ if (!enabled_)
+ return false;
+
+ bool handled = false;
+ bool maybe_needs_animate = false;
+ switch (event.type) {
+ case blink::WebInputEvent::GestureScrollBegin:
+ refresh_effect_.OnScrollBegin();
+ break;
+
+ case blink::WebInputEvent::GestureScrollUpdate: {
+ gfx::Vector2dF scroll_delta(event.data.scrollUpdate.deltaX,
+ event.data.scrollUpdate.deltaY);
+ scroll_delta.Scale(dpi_scale_);
+ maybe_needs_animate = true;
+ handled = refresh_effect_.WillHandleScrollUpdate(scroll_delta);
+ } break;
+
+ case blink::WebInputEvent::GestureScrollEnd:
+ refresh_effect_.OnScrollEnd(gfx::Vector2dF());
+ maybe_needs_animate = true;
+ break;
+
+ case blink::WebInputEvent::GestureFlingStart: {
+ gfx::Vector2dF scroll_velocity(event.data.flingStart.velocityX,
+ event.data.flingStart.velocityY);
+ scroll_velocity.Scale(dpi_scale_);
+ refresh_effect_.OnScrollEnd(scroll_velocity);
+ if (refresh_effect_.IsActive()) {
+ // TODO(jdduke): Figure out a cleaner way of suppressing a fling.
+ // It's important that the any downstream code sees a scroll-ending
+ // event (in this case GestureFlingStart) if it has seen a scroll begin.
+ // Thus, we cannot simply consume the fling. Changing the event type to
+ // a GestureScrollEnd might work in practice, but could lead to
+ // unexpected results. For now, simply truncate the fling velocity, but
+ // not to zero as downstream code may not expect a zero-velocity fling.
+ blink::WebGestureEvent& modified_event =
+ const_cast<blink::WebGestureEvent&>(event);
+ modified_event.data.flingStart.velocityX = .01f;
+ modified_event.data.flingStart.velocityY = .01f;
+ }
+ maybe_needs_animate = true;
+ } break;
+
+ default:
+ break;
+ }
+
+ if (maybe_needs_animate && refresh_effect_.IsActive())
+ SetNeedsAnimate();
+
+ return handled;
+}
+
+void OverscrollControllerAndroid::OnGestureEventAck(
+ const blink::WebGestureEvent& event,
+ InputEventAckState ack_result) {
+ if (!enabled_)
+ return;
+
+ // The overscroll effect requires an explicit release signal that may not be
+ // sent from the renderer compositor.
+ if (event.type == blink::WebInputEvent::GestureScrollEnd ||
+ event.type == blink::WebInputEvent::GestureFlingStart) {
+ OnOverscrolled(DidOverscrollParams());
+ }
+
+ if (event.type == blink::WebInputEvent::GestureScrollUpdate) {
+ // The effect should only be allowed if both the causal touch events go
+ // unconsumed and the generated scroll events go unconsumed.
+ // TODO(jdduke): Prevent activation if the first touchmove was consumed,
+ // i.e., the first GSU was prevented.
+ bool consumed = ack_result == INPUT_EVENT_ACK_STATE_CONSUMED;
+ refresh_effect_.OnScrollUpdateAck(consumed);
+ }
+}
+
+void OverscrollControllerAndroid::OnOverscrolled(
+ const DidOverscrollParams& params) {
+ if (!enabled_)
+ return;
+
+ if (refresh_effect_.IsActive() ||
+ refresh_effect_.IsAwaitingScrollUpdateAck()) {
+ // An active (or potentially active) refresh effect should always pre-empt
+ // the passive glow effect.
+ return;
+ }
+
+ if (glow_effect_.OnOverscrolled(
+ base::TimeTicks::Now(),
+ gfx::ScaleVector2d(params.accumulated_overscroll, dpi_scale_),
+ gfx::ScaleVector2d(params.latest_overscroll_delta, dpi_scale_),
+ gfx::ScaleVector2d(params.current_fling_velocity, dpi_scale_),
+ gfx::ScaleVector2d(params.causal_event_viewport_point.OffsetFromOrigin(),
+ dpi_scale_))) {
+ SetNeedsAnimate();
+ }
+}
+
+bool OverscrollControllerAndroid::Animate(base::TimeTicks current_time,
+ cc::Layer* parent_layer) {
+ DCHECK(parent_layer);
+ if (!enabled_)
+ return false;
+
+ bool needs_animate = refresh_effect_.Animate(current_time, parent_layer);
+ needs_animate |= glow_effect_.Animate(current_time, parent_layer);
+ return needs_animate;
+}
+
+void OverscrollControllerAndroid::OnFrameMetadataUpdated(
+ const cc::CompositorFrameMetadata& frame_metadata) {
+ const float scale_factor =
+ frame_metadata.page_scale_factor * frame_metadata.device_scale_factor;
+ gfx::SizeF viewport_size =
+ gfx::ScaleSize(frame_metadata.scrollable_viewport_size, scale_factor);
+ gfx::SizeF content_size =
+ gfx::ScaleSize(frame_metadata.root_layer_size, scale_factor);
+ gfx::Vector2dF content_scroll_offset =
+ gfx::ScaleVector2d(frame_metadata.root_scroll_offset, scale_factor);
+
+ refresh_effect_.UpdateDisplay(viewport_size, content_scroll_offset);
+ glow_effect_.UpdateDisplay(viewport_size, content_size,
+ content_scroll_offset);
+}
+
+void OverscrollControllerAndroid::Enable() {
+ enabled_ = true;
+}
+
+void OverscrollControllerAndroid::Disable() {
+ if (!enabled_)
+ return;
+ enabled_ = false;
+ if (!enabled_) {
+ refresh_effect_.Reset();
+ glow_effect_.Reset();
+ }
+}
+
+void OverscrollControllerAndroid::DidNavigateMainFrame(
+ const LoadCommittedDetails& details,
+ const FrameNavigateParams& params) {
+ // Once the main frame has navigated, there's little need to further animate
+ // the reload effect. Note that the effect will naturally time out should the
+ // reload be interruped for any reason.
+ triggered_refresh_active_ = false;
+}
+
+void OverscrollControllerAndroid::TriggerRefresh() {
+ triggered_refresh_active_ = false;
+ if (!web_contents())
+ return;
+
+ triggered_refresh_active_ = true;
+ web_contents()->ReloadFocusedFrame(false);
+}
+
+bool OverscrollControllerAndroid::IsStillRefreshing() const {
+ return triggered_refresh_active_;
+}
+
+void OverscrollControllerAndroid::SetNeedsAnimate() {
+ compositor_->SetNeedsAnimate();
+}
+
+} // namespace content
« no previous file with comments | « content/browser/android/overscroll_controller_android.h ('k') | content/browser/android/overscroll_glow.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698