diff options
author | jdduke <jdduke@chromium.org> | 2015-04-14 18:55:08 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-04-15 01:55:33 +0000 |
commit | b003775553a0b51cbbd8bd25a5b9c4a27f46550c (patch) | |
tree | 25d8b5b591ecc424e9d9413f732d74c27c723737 /content/browser/android/overscroll_refresh.cc | |
parent | b5ec938f61cb21064185ee7361511e432aa32306 (diff) | |
download | chromium_src-b003775553a0b51cbbd8bd25a5b9c4a27f46550c.zip chromium_src-b003775553a0b51cbbd8bd25a5b9c4a27f46550c.tar.gz chromium_src-b003775553a0b51cbbd8bd25a5b9c4a27f46550c.tar.bz2 |
[Android] Use the platform SwipeRefreshLayout
Adopt the compat library's implementation of the pull-to-refresh effect,
removing the old compositor-based approach.
This depends directly on the SwipeRefreshLayout addition in
https://codereview.chromium.org/897663003/.
BUG=428429,444134
Review URL: https://codereview.chromium.org/894193005
Cr-Commit-Position: refs/heads/master@{#325172}
Diffstat (limited to 'content/browser/android/overscroll_refresh.cc')
-rw-r--r-- | content/browser/android/overscroll_refresh.cc | 448 |
1 files changed, 19 insertions, 429 deletions
diff --git a/content/browser/android/overscroll_refresh.cc b/content/browser/android/overscroll_refresh.cc index 58962f6..c2a1453 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; } |