| Index: content/browser/android/overscroll_refresh.cc
|
| diff --git a/content/browser/android/overscroll_refresh.cc b/content/browser/android/overscroll_refresh.cc
|
| index 58962f6356ecfbe02fcd43984416ae843daffaca..c2a1453bc1750eea5e49f337c65f9601f7fe1667 100644
|
| --- a/content/browser/android/overscroll_refresh.cc
|
| +++ b/content/browser/android/overscroll_refresh.cc
|
| @@ -4,418 +4,23 @@
|
|
|
| #include "content/browser/android/overscroll_refresh.h"
|
|
|
| -#include "cc/animation/timing_function.h"
|
| -#include "cc/layers/ui_resource_layer.h"
|
| -#include "cc/trees/layer_tree_host.h"
|
| -#include "content/browser/android/animation_utils.h"
|
| -#include "ui/android/resources/resource_manager.h"
|
| -#include "ui/android/resources/system_ui_resource_type.h"
|
| -#include "ui/gfx/geometry/size_conversions.h"
|
| -
|
| -using std::abs;
|
| -using std::max;
|
| -using std::min;
|
| +#include "base/logging.h"
|
|
|
| namespace content {
|
| namespace {
|
|
|
| -const ui::SystemUIResourceType kIdleResourceId = ui::OVERSCROLL_REFRESH_IDLE;
|
| -const ui::SystemUIResourceType kActiveResourceId =
|
| - ui::OVERSCROLL_REFRESH_ACTIVE;
|
| -
|
| -// Drag movement multiplier between user input and effect translation.
|
| -const float kDragRate = .5f;
|
| -
|
| -// Animation duration after the effect is released without triggering a refresh.
|
| -const int kRecedeTimeMs = 300;
|
| -
|
| -// Animation duration immediately after the effect is released and activated.
|
| -const int kActivationStartTimeMs = 150;
|
| -
|
| -// Animation duration after the effect is released and triggers a refresh.
|
| -const int kActivationTimeMs = 850;
|
| -
|
| -// Max animation duration after the effect is released and triggers a refresh.
|
| -const int kMaxActivationTimeMs = kActivationTimeMs * 4;
|
| -
|
| -// Animation duration after the refresh activated phase has completed.
|
| -const int kActivationRecedeTimeMs = 250;
|
| -
|
| -// Input threshold required to start glowing.
|
| -const float kGlowActivationThreshold = 0.85f;
|
| -
|
| -// Minimum alpha for the effect layer.
|
| -const float kMinAlpha = 0.3f;
|
| -
|
| // Experimentally determined constant used to allow activation even if touch
|
| // release results in a small upward fling (quite common during a slow scroll).
|
| const float kMinFlingVelocityForActivation = -500.f;
|
|
|
| -const float kEpsilon = 0.005f;
|
| -
|
| -void UpdateLayer(cc::UIResourceLayer* layer,
|
| - cc::Layer* parent,
|
| - cc::UIResourceId res_id,
|
| - const gfx::Size& size,
|
| - const gfx::Vector2dF& offset,
|
| - float opacity,
|
| - float rotation,
|
| - bool mirror) {
|
| - DCHECK(layer);
|
| - DCHECK(parent);
|
| - DCHECK(parent->layer_tree_host());
|
| - if (layer->parent() != parent)
|
| - parent->AddChild(layer);
|
| -
|
| - if (size.IsEmpty()) {
|
| - layer->SetIsDrawable(false);
|
| - return;
|
| - }
|
| -
|
| - if (!res_id) {
|
| - layer->SetIsDrawable(false);
|
| - return;
|
| - }
|
| -
|
| - if (opacity == 0) {
|
| - layer->SetIsDrawable(false);
|
| - layer->SetOpacity(0);
|
| - return;
|
| - }
|
| -
|
| - layer->SetUIResourceId(res_id);
|
| - layer->SetIsDrawable(true);
|
| - layer->SetTransformOrigin(
|
| - gfx::Point3F(size.width() * 0.5f, size.height() * 0.5f, 0));
|
| - layer->SetBounds(size);
|
| - layer->SetContentsOpaque(false);
|
| - layer->SetOpacity(Clamp(opacity, 0.f, 1.f));
|
| -
|
| - float offset_x = offset.x() - size.width() * 0.5f;
|
| - float offset_y = offset.y() - size.height() * 0.5f;
|
| - gfx::Transform transform;
|
| - transform.Translate(offset_x, offset_y);
|
| - if (mirror)
|
| - transform.Scale(-1.f, 1.f);
|
| - transform.Rotate(rotation);
|
| - layer->SetTransform(transform);
|
| -}
|
| -
|
| } // namespace
|
|
|
| -class OverscrollRefresh::Effect {
|
| - public:
|
| - Effect(ui::ResourceManager* resource_manager, float target_drag, bool mirror)
|
| - : resource_manager_(resource_manager),
|
| - idle_layer_(cc::UIResourceLayer::Create()),
|
| - active_layer_(cc::UIResourceLayer::Create()),
|
| - target_drag_(target_drag),
|
| - mirror_(mirror),
|
| - drag_(0),
|
| - idle_alpha_(0),
|
| - active_alpha_(0),
|
| - offset_(0),
|
| - rotation_(0),
|
| - size_scale_(1),
|
| - idle_alpha_start_(0),
|
| - idle_alpha_finish_(0),
|
| - active_alpha_start_(0),
|
| - active_alpha_finish_(0),
|
| - offset_start_(0),
|
| - offset_finish_(0),
|
| - rotation_start_(0),
|
| - rotation_finish_(0),
|
| - size_scale_start_(1),
|
| - size_scale_finish_(1),
|
| - state_(STATE_IDLE),
|
| - ease_out_(cc::EaseOutTimingFunction::Create()),
|
| - ease_in_out_(cc::EaseInOutTimingFunction::Create()) {
|
| - DCHECK(target_drag_);
|
| - idle_layer_->SetIsDrawable(false);
|
| - active_layer_->SetIsDrawable(false);
|
| - }
|
| -
|
| - ~Effect() { Detach(); }
|
| -
|
| - void Pull(float delta) {
|
| - if (state_ != STATE_PULL)
|
| - drag_ = 0;
|
| -
|
| - state_ = STATE_PULL;
|
| -
|
| - delta *= kDragRate;
|
| - float max_delta = target_drag_ / OverscrollRefresh::kMinPullsToActivate;
|
| - delta = Clamp(delta, -max_delta, max_delta);
|
| -
|
| - drag_ += delta;
|
| - drag_ = Clamp(drag_, 0.f, target_drag_ * 3.f);
|
| -
|
| - // The following logic and constants were taken from Android's refresh
|
| - // effect (see SwipeRefreshLayout.java from v4 of the AppCompat library).
|
| - float original_drag_percent = drag_ / target_drag_;
|
| - float drag_percent = min(1.f, abs(original_drag_percent));
|
| - float adjusted_percent = max(drag_percent - .4f, 0.f) * 5.f / 3.f;
|
| - float extra_os = abs(drag_) - target_drag_;
|
| - float slingshot_dist = target_drag_;
|
| - float tension_slingshot_percent =
|
| - max(0.f, min(extra_os, slingshot_dist * 2) / slingshot_dist);
|
| - float tension_percent = ((tension_slingshot_percent / 4) -
|
| - std::pow((tension_slingshot_percent / 4), 2.f)) *
|
| - 2.f;
|
| - float extra_move = slingshot_dist * tension_percent * 2;
|
| -
|
| - offset_ = slingshot_dist * drag_percent + extra_move;
|
| -
|
| - rotation_ =
|
| - 360.f * ((-0.25f + .4f * adjusted_percent + tension_percent * 2) * .5f);
|
| -
|
| - idle_alpha_ =
|
| - kMinAlpha + (1.f - kMinAlpha) * drag_percent / kGlowActivationThreshold;
|
| - active_alpha_ = (drag_percent - kGlowActivationThreshold) /
|
| - (1.f - kGlowActivationThreshold);
|
| - idle_alpha_ = Clamp(idle_alpha_, 0.f, 1.f);
|
| - active_alpha_ = Clamp(active_alpha_, 0.f, 1.f);
|
| -
|
| - size_scale_ = 1;
|
| - }
|
| -
|
| - bool Animate(base::TimeTicks current_time, bool still_refreshing) {
|
| - if (IsFinished())
|
| - return false;
|
| -
|
| - if (state_ == STATE_PULL)
|
| - return true;
|
| -
|
| - const double dt = (current_time - start_time_).InMilliseconds();
|
| - const double t = dt / duration_.InMilliseconds();
|
| - const float interp = ease_out_->GetValue(min(t, 1.));
|
| -
|
| - idle_alpha_ = Lerp(idle_alpha_start_, idle_alpha_finish_, interp);
|
| - active_alpha_ = Lerp(active_alpha_start_, active_alpha_finish_, interp);
|
| - offset_ = Lerp(offset_start_, offset_finish_, interp);
|
| - size_scale_ = Lerp(size_scale_start_, size_scale_finish_, interp);
|
| -
|
| - if (state_ == STATE_ACTIVATED || state_ == STATE_ACTIVATED_RECEDE) {
|
| - float adjusted_interp = ease_in_out_->GetValue(min(t, 1.));
|
| - rotation_ = Lerp(rotation_start_, rotation_finish_, adjusted_interp);
|
| - // Add a small constant rotational velocity during activation.
|
| - rotation_ += dt * 90.f / kActivationTimeMs;
|
| - } else {
|
| - rotation_ = Lerp(rotation_start_, rotation_finish_, interp);
|
| - }
|
| -
|
| - if (t < 1.f - kEpsilon)
|
| - return true;
|
| -
|
| - switch (state_) {
|
| - case STATE_IDLE:
|
| - case STATE_PULL:
|
| - NOTREACHED() << "Invalidate state for animation.";
|
| - break;
|
| - case STATE_ACTIVATED_START:
|
| - // Briefly pause the animation after the rapid initial translation.
|
| - if (t < 1.5f)
|
| - break;
|
| - state_ = STATE_ACTIVATED;
|
| - start_time_ = current_time;
|
| - duration_ = base::TimeDelta::FromMilliseconds(kActivationTimeMs);
|
| - activated_start_time_ = current_time;
|
| - offset_start_ = offset_finish_ = offset_;
|
| - rotation_start_ = rotation_;
|
| - rotation_finish_ = rotation_start_ + 270.f;
|
| - size_scale_start_ = size_scale_finish_ = size_scale_;
|
| - break;
|
| - case STATE_ACTIVATED:
|
| - start_time_ = current_time;
|
| - if (still_refreshing &&
|
| - (current_time - activated_start_time_ <
|
| - base::TimeDelta::FromMilliseconds(kMaxActivationTimeMs))) {
|
| - offset_start_ = offset_finish_ = offset_;
|
| - rotation_start_ = rotation_;
|
| - rotation_finish_ = rotation_start_ + 270.f;
|
| - break;
|
| - }
|
| - state_ = STATE_ACTIVATED_RECEDE;
|
| - duration_ = base::TimeDelta::FromMilliseconds(kActivationRecedeTimeMs);
|
| - rotation_start_ = rotation_finish_ = rotation_;
|
| - offset_start_ = offset_finish_ = offset_;
|
| - size_scale_start_ = size_scale_;
|
| - size_scale_finish_ = 0;
|
| - break;
|
| - case STATE_ACTIVATED_RECEDE:
|
| - Finish();
|
| - break;
|
| - case STATE_RECEDE:
|
| - Finish();
|
| - break;
|
| - };
|
| -
|
| - return !IsFinished();
|
| - }
|
| -
|
| - bool Release(base::TimeTicks current_time, bool allow_activation) {
|
| - switch (state_) {
|
| - case STATE_PULL:
|
| - break;
|
| -
|
| - case STATE_ACTIVATED:
|
| - case STATE_ACTIVATED_START:
|
| - // Avoid redundant activations.
|
| - if (allow_activation)
|
| - return false;
|
| - break;
|
| -
|
| - case STATE_IDLE:
|
| - case STATE_ACTIVATED_RECEDE:
|
| - case STATE_RECEDE:
|
| - // These states have already been "released" in some fashion.
|
| - return false;
|
| - }
|
| -
|
| - start_time_ = current_time;
|
| - idle_alpha_start_ = idle_alpha_;
|
| - active_alpha_start_ = active_alpha_;
|
| - offset_start_ = offset_;
|
| - rotation_start_ = rotation_;
|
| - size_scale_start_ = size_scale_finish_ = size_scale_;
|
| -
|
| - if (drag_ < target_drag_ || !allow_activation) {
|
| - state_ = STATE_RECEDE;
|
| - duration_ = base::TimeDelta::FromMilliseconds(kRecedeTimeMs);
|
| - idle_alpha_finish_ = 0;
|
| - active_alpha_finish_ = 0;
|
| - offset_finish_ = 0;
|
| - rotation_finish_ = rotation_start_ - 180.f;
|
| - return false;
|
| - }
|
| -
|
| - state_ = STATE_ACTIVATED_START;
|
| - duration_ = base::TimeDelta::FromMilliseconds(kActivationStartTimeMs);
|
| - activated_start_time_ = current_time;
|
| - idle_alpha_finish_ = idle_alpha_start_;
|
| - active_alpha_finish_ = active_alpha_start_;
|
| - offset_finish_ = target_drag_;
|
| - rotation_finish_ = rotation_start_;
|
| - return true;
|
| - }
|
| -
|
| - void Finish() {
|
| - Detach();
|
| - idle_layer_->SetIsDrawable(false);
|
| - active_layer_->SetIsDrawable(false);
|
| - offset_ = 0;
|
| - idle_alpha_ = 0;
|
| - active_alpha_ = 0;
|
| - rotation_ = 0;
|
| - size_scale_ = 1;
|
| - state_ = STATE_IDLE;
|
| - }
|
| -
|
| - void ApplyToLayers(const gfx::SizeF& viewport_size, cc::Layer* parent) {
|
| - if (IsFinished())
|
| - return;
|
| -
|
| - if (!parent->layer_tree_host())
|
| - return;
|
| -
|
| - // An empty window size, while meaningless, is also relatively harmless, and
|
| - // will simply prevent any drawing of the layers.
|
| - if (viewport_size.IsEmpty()) {
|
| - idle_layer_->SetIsDrawable(false);
|
| - active_layer_->SetIsDrawable(false);
|
| - return;
|
| - }
|
| -
|
| - cc::UIResourceId idle_resource = resource_manager_->GetUIResourceId(
|
| - ui::ANDROID_RESOURCE_TYPE_SYSTEM, kIdleResourceId);
|
| - cc::UIResourceId active_resource = resource_manager_->GetUIResourceId(
|
| - ui::ANDROID_RESOURCE_TYPE_SYSTEM, kActiveResourceId);
|
| -
|
| - gfx::Size idle_size =
|
| - parent->layer_tree_host()->GetUIResourceSize(idle_resource);
|
| - gfx::Size active_size =
|
| - parent->layer_tree_host()->GetUIResourceSize(active_resource);
|
| - gfx::Size scaled_idle_size =
|
| - gfx::ToRoundedSize(gfx::ScaleSize(idle_size, size_scale_));
|
| - gfx::Size scaled_active_size =
|
| - gfx::ToRoundedSize(gfx::ScaleSize(active_size, size_scale_));
|
| -
|
| - gfx::Vector2dF idle_offset(viewport_size.width() * 0.5f,
|
| - offset_ - idle_size.height() * 0.5f);
|
| - gfx::Vector2dF active_offset(viewport_size.width() * 0.5f,
|
| - offset_ - active_size.height() * 0.5f);
|
| -
|
| - UpdateLayer(idle_layer_.get(), parent, idle_resource, scaled_idle_size,
|
| - idle_offset, idle_alpha_, rotation_, mirror_);
|
| - UpdateLayer(active_layer_.get(), parent, active_resource,
|
| - scaled_active_size, active_offset, active_alpha_, rotation_,
|
| - mirror_);
|
| - }
|
| -
|
| - bool IsFinished() const { return state_ == STATE_IDLE; }
|
| -
|
| - private:
|
| - enum State {
|
| - STATE_IDLE = 0,
|
| - STATE_PULL,
|
| - STATE_ACTIVATED_START,
|
| - STATE_ACTIVATED,
|
| - STATE_ACTIVATED_RECEDE,
|
| - STATE_RECEDE
|
| - };
|
| -
|
| - void Detach() {
|
| - idle_layer_->RemoveFromParent();
|
| - active_layer_->RemoveFromParent();
|
| - }
|
| -
|
| - ui::ResourceManager* const resource_manager_;
|
| -
|
| - scoped_refptr<cc::UIResourceLayer> idle_layer_;
|
| - scoped_refptr<cc::UIResourceLayer> active_layer_;
|
| -
|
| - const float target_drag_;
|
| - const bool mirror_;
|
| - float drag_;
|
| - float idle_alpha_;
|
| - float active_alpha_;
|
| - float offset_;
|
| - float rotation_;
|
| - float size_scale_;
|
| -
|
| - float idle_alpha_start_;
|
| - float idle_alpha_finish_;
|
| - float active_alpha_start_;
|
| - float active_alpha_finish_;
|
| - float offset_start_;
|
| - float offset_finish_;
|
| - float rotation_start_;
|
| - float rotation_finish_;
|
| - float size_scale_start_;
|
| - float size_scale_finish_;
|
| -
|
| - base::TimeTicks start_time_;
|
| - base::TimeTicks activated_start_time_;
|
| - base::TimeDelta duration_;
|
| -
|
| - State state_;
|
| -
|
| - scoped_ptr<cc::TimingFunction> ease_out_;
|
| - scoped_ptr<cc::TimingFunction> ease_in_out_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(Effect);
|
| -};
|
| -
|
| -OverscrollRefresh::OverscrollRefresh(ui::ResourceManager* resource_manager,
|
| - OverscrollRefreshClient* client,
|
| - float target_drag_offset_pixels,
|
| - bool mirror)
|
| - : client_(client),
|
| - scrolled_to_top_(true),
|
| +OverscrollRefresh::OverscrollRefresh(OverscrollRefreshHandler* handler)
|
| + : scrolled_to_top_(true),
|
| overflow_y_hidden_(false),
|
| scroll_consumption_state_(DISABLED),
|
| - effect_(new Effect(resource_manager, target_drag_offset_pixels, mirror)) {
|
| - DCHECK(client);
|
| + handler_(handler) {
|
| + DCHECK(handler);
|
| }
|
|
|
| OverscrollRefresh::~OverscrollRefresh() {
|
| @@ -423,7 +28,7 @@ OverscrollRefresh::~OverscrollRefresh() {
|
|
|
| void OverscrollRefresh::Reset() {
|
| scroll_consumption_state_ = DISABLED;
|
| - effect_->Finish();
|
| + handler_->PullReset();
|
| }
|
|
|
| void OverscrollRefresh::OnScrollBegin() {
|
| @@ -441,14 +46,16 @@ void OverscrollRefresh::OnScrollUpdateAck(bool was_consumed) {
|
| if (scroll_consumption_state_ != AWAITING_SCROLL_UPDATE_ACK)
|
| return;
|
|
|
| - scroll_consumption_state_ = was_consumed ? DISABLED : ENABLED;
|
| + if (was_consumed) {
|
| + scroll_consumption_state_ = DISABLED;
|
| + return;
|
| + }
|
| +
|
| + scroll_consumption_state_ = handler_->PullStart() ? ENABLED : DISABLED;
|
| }
|
|
|
| bool OverscrollRefresh::WillHandleScrollUpdate(
|
| const gfx::Vector2dF& scroll_delta) {
|
| - if (viewport_size_.IsEmpty())
|
| - return false;
|
| -
|
| switch (scroll_consumption_state_) {
|
| case DISABLED:
|
| return false;
|
| @@ -459,10 +66,9 @@ bool OverscrollRefresh::WillHandleScrollUpdate(
|
| scroll_consumption_state_ = DISABLED;
|
| return false;
|
|
|
| - case ENABLED: {
|
| - effect_->Pull(scroll_delta.y());
|
| + case ENABLED:
|
| + handler_->PullUpdate(scroll_delta.y());
|
| return true;
|
| - }
|
| }
|
|
|
| NOTREACHED() << "Invalid overscroll state: " << scroll_consumption_state_;
|
| @@ -474,40 +80,24 @@ void OverscrollRefresh::ReleaseWithoutActivation() {
|
| Release(allow_activation);
|
| }
|
|
|
| -bool OverscrollRefresh::Animate(base::TimeTicks current_time,
|
| - cc::Layer* parent_layer) {
|
| - DCHECK(parent_layer);
|
| - if (effect_->IsFinished())
|
| - return false;
|
| -
|
| - if (effect_->Animate(current_time, client_->IsStillRefreshing()))
|
| - effect_->ApplyToLayers(viewport_size_, parent_layer);
|
| -
|
| - return !effect_->IsFinished();
|
| -}
|
| -
|
| bool OverscrollRefresh::IsActive() const {
|
| - return scroll_consumption_state_ == ENABLED || !effect_->IsFinished();
|
| + return scroll_consumption_state_ == ENABLED;
|
| }
|
|
|
| bool OverscrollRefresh::IsAwaitingScrollUpdateAck() const {
|
| return scroll_consumption_state_ == AWAITING_SCROLL_UPDATE_ACK;
|
| }
|
|
|
| -void OverscrollRefresh::UpdateDisplay(
|
| - const gfx::SizeF& viewport_size,
|
| +void OverscrollRefresh::OnFrameUpdated(
|
| const gfx::Vector2dF& content_scroll_offset,
|
| bool root_overflow_y_hidden) {
|
| - viewport_size_ = viewport_size;
|
| scrolled_to_top_ = content_scroll_offset.y() == 0;
|
| overflow_y_hidden_ = root_overflow_y_hidden;
|
| }
|
|
|
| -void OverscrollRefresh::Release(bool allow_activation) {
|
| - if (scroll_consumption_state_ == ENABLED) {
|
| - if (effect_->Release(base::TimeTicks::Now(), allow_activation))
|
| - client_->TriggerRefresh();
|
| - }
|
| +void OverscrollRefresh::Release(bool allow_refresh) {
|
| + if (scroll_consumption_state_ == ENABLED)
|
| + handler_->PullRelease(allow_refresh);
|
| scroll_consumption_state_ = DISABLED;
|
| }
|
|
|
|
|