OLD | NEW |
---|---|
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package org.chromium.chrome.browser.widget; | 5 package org.chromium.chrome.browser.widget; |
6 | 6 |
7 import android.animation.Animator; | 7 import android.animation.TimeAnimator; |
8 import android.animation.AnimatorListenerAdapter; | 8 import android.animation.TimeAnimator.TimeListener; |
9 import android.animation.ObjectAnimator; | |
10 import android.content.Context; | 9 import android.content.Context; |
11 import android.graphics.drawable.Drawable; | |
12 import android.graphics.drawable.LayerDrawable; | |
13 import android.util.AttributeSet; | 10 import android.util.AttributeSet; |
14 import android.view.View; | |
15 | 11 |
16 import org.chromium.base.VisibleForTesting; | 12 import org.chromium.base.VisibleForTesting; |
17 import org.chromium.ui.interpolators.BakedBezierInterpolator; | 13 import org.chromium.chrome.browser.util.MathUtils; |
18 | 14 |
19 /** | 15 /** |
20 * Progress bar for use in the Toolbar view. | 16 * Progress bar for use in the Toolbar view. |
21 */ | 17 */ |
22 public class ToolbarProgressBar extends SmoothProgressBar { | 18 public class ToolbarProgressBar extends ClipDrawableProgressBar { |
23 private static final long PROGRESS_CLEARING_DELAY_MS = 200; | 19 private static final float ALPHA_CHANGE_SPEED_PER_SECOND = 8.0f; |
24 private static final int SHOW_HIDE_DURATION_MS = 100; | 20 private static final long HIDING_DELAY_MS = 100; |
25 | 21 |
26 private final Runnable mClearLoadProgressRunnable; | 22 // Public duration constants are for testing. |
27 private int mDesiredVisibility; | 23 public static final long SHOWING_DURATION_MS = |
28 private Animator mShowAnimator; | 24 (long) Math.ceil(1.0f / ALPHA_CHANGE_SPEED_PER_SECOND * 1000); |
29 private Animator mHideAnimator; | 25 public static final long HIDING_DURATION_MS = HIDING_DELAY_MS |
26 + (long) Math.ceil(1.0f / ALPHA_CHANGE_SPEED_PER_SECOND * 1000); | |
27 | |
28 private boolean mIsStarted; | |
29 private float mTargetAlpha; | |
30 private boolean mIsHideRunnablePosted; | |
31 private TimeAnimator mAlphaAnimator = new TimeAnimator(); | |
32 private final Runnable mHideRunnable = new Runnable() { | |
33 @Override | |
34 public void run() { | |
35 animateAlphaTo(0.0f); | |
36 mIsHideRunnablePosted = false; | |
37 } | |
38 }; | |
30 | 39 |
31 /** | 40 /** |
32 * Creates a toolbar progress bar. | 41 * Creates a toolbar progress bar. |
42 * | |
33 * @param context the application environment. | 43 * @param context the application environment. |
34 * @param attrs the xml attributes that should be used to initialize this vi ew. | 44 * @param attrs the xml attributes that should be used to initialize this vi ew. |
35 */ | 45 */ |
36 public ToolbarProgressBar(Context context, AttributeSet attrs) { | 46 public ToolbarProgressBar(Context context, AttributeSet attrs) { |
37 super(context, attrs); | 47 super(context, attrs); |
38 // The base constructor will trigger a progress change and alter the exp ected | |
39 // visibility, so force a visibility change to reset the state. | |
40 setVisibility(VISIBLE); | |
41 | 48 |
42 mClearLoadProgressRunnable = new Runnable() { | 49 setAlpha(mTargetAlpha); |
50 | |
51 mAlphaAnimator.setTimeListener(new TimeListener() { | |
Ted C
2015/07/15 02:41:12
What is the benefit of the alpha animator?
Doesn'
Kibeom Kim (inactive)
2015/07/15 11:22:46
I like TimeAnimator in general, I don't know why I
| |
43 @Override | 52 @Override |
44 public void run() { | 53 public void onTimeUpdate(TimeAnimator animation, long totalTimeMs, l ong deltaTimeMs) { |
45 setProgress(0); | 54 float alpha = getAlpha(); |
46 } | |
47 }; | |
48 | 55 |
49 // Hide the background portion of the system progress bar. | 56 if (alpha == mTargetAlpha) { |
50 Drawable progressDrawable = getProgressDrawable(); | 57 mAlphaAnimator.end(); |
51 if (progressDrawable instanceof LayerDrawable) { | 58 return; |
52 Drawable progressBackgroundDrawable = | 59 } |
53 ((LayerDrawable) progressDrawable) | |
54 .findDrawableByLayerId(android.R.id.background); | |
55 if (progressBackgroundDrawable != null) { | |
56 progressBackgroundDrawable.setVisible(false, false); | |
57 progressBackgroundDrawable.setAlpha(0); | |
58 } | |
59 } | |
60 } | |
61 | 60 |
62 @Override | 61 float maxAlphaChange = ALPHA_CHANGE_SPEED_PER_SECOND * deltaTime Ms / 1000.0f; |
63 public void setSecondaryProgress(int secondaryProgress) { | 62 setAlpha(alpha |
64 super.setSecondaryProgress(secondaryProgress); | 63 + MathUtils.clamp(mTargetAlpha - alpha, -maxAlphaChange, maxAlphaChange)); |
65 setVisibilityForProgress(); | |
66 } | |
67 | |
68 @Override | |
69 protected void setProgressInternal(int progress) { | |
70 super.setProgressInternal(progress); | |
71 | |
72 if (progress == getMax()) { | |
73 postDelayed(mClearLoadProgressRunnable, PROGRESS_CLEARING_DELAY_MS); | |
74 } | |
75 | |
76 setVisibilityForProgress(); | |
77 } | |
78 | |
79 @Override | |
80 public void setVisibility(int v) { | |
81 mDesiredVisibility = v; | |
82 setVisibilityForProgress(); | |
83 } | |
84 | |
85 private void setVisibilityForProgress() { | |
86 if (mDesiredVisibility != VISIBLE) { | |
87 super.setVisibility(mDesiredVisibility); | |
88 return; | |
89 } | |
90 | |
91 int progress = Math.max(getProgress(), getSecondaryProgress()); | |
92 super.setVisibility(progress == 0 ? INVISIBLE : VISIBLE); | |
93 } | |
94 | |
95 @Override | |
96 protected void onSizeChanged(int w, int h, int oldw, int oldh) { | |
97 super.onSizeChanged(w, h, oldw, oldh); | |
98 | |
99 // Some versions of Android have a bug where they don't properly update the drawables with | |
100 // the correct bounds. setProgressDrawable has been overridden to prope rly push the bounds | |
101 // but on rotation they weren't always being set. Forcing a bounds upda te on size changes | |
102 // fixes the problem. | |
103 setProgressDrawable(getProgressDrawable()); | |
104 } | |
105 | |
106 @Override | |
107 protected void onLayout(boolean changed, int left, int top, int right, int b ottom) { | |
108 super.onLayout(changed, left, top, right, bottom); | |
109 buildAnimators(); | |
110 setPivotY(getHeight()); | |
111 } | |
112 | |
113 @Override | |
114 public void setProgressDrawable(Drawable d) { | |
115 Drawable currentDrawable = getProgressDrawable(); | |
116 | |
117 super.setProgressDrawable(d); | |
118 | |
119 if (currentDrawable != null && d instanceof LayerDrawable) { | |
120 LayerDrawable ld = (LayerDrawable) d; | |
121 for (int i = 0; i < ld.getNumberOfLayers(); i++) { | |
122 ld.getDrawable(i).setBounds(currentDrawable.getBounds()); | |
123 } | |
124 } | |
125 } | |
126 | |
127 /** | |
128 * @return Whether or not this progress bar has animations running for showi ng/hiding itself. | |
129 */ | |
130 @VisibleForTesting | |
131 boolean isAnimatingForShowOrHide() { | |
132 return (mShowAnimator != null && mShowAnimator.isStarted()) | |
133 || (mHideAnimator != null && mHideAnimator.isStarted()); | |
134 } | |
135 | |
136 private void buildAnimators() { | |
137 if (mShowAnimator != null && mShowAnimator.isRunning()) mShowAnimator.en d(); | |
138 if (mHideAnimator != null && mHideAnimator.isRunning()) mHideAnimator.en d(); | |
139 | |
140 mShowAnimator = ObjectAnimator.ofFloat(this, View.SCALE_Y, 0.f, 1.f); | |
141 mShowAnimator.setDuration(SHOW_HIDE_DURATION_MS); | |
142 mShowAnimator.setInterpolator(BakedBezierInterpolator.FADE_IN_CURVE); | |
143 mShowAnimator.addListener(new AnimatorListenerAdapter() { | |
144 @Override | |
145 public void onAnimationStart(Animator animation) { | |
146 setSecondaryProgress(getMax()); | |
147 } | |
148 }); | |
149 | |
150 mHideAnimator = ObjectAnimator.ofFloat(this, View.SCALE_Y, 1.f, 0.f); | |
151 mHideAnimator.setDuration(SHOW_HIDE_DURATION_MS); | |
152 mHideAnimator.setInterpolator(BakedBezierInterpolator.FADE_OUT_CURVE); | |
153 mHideAnimator.addListener(new AnimatorListenerAdapter() { | |
154 @Override | |
155 public void onAnimationEnd(Animator animation) { | |
156 setSecondaryProgress(0); | |
157 } | 64 } |
158 }); | 65 }); |
159 } | 66 } |
160 | 67 |
68 /** | |
69 * Start showing progress bar animation. | |
70 */ | |
71 public void start() { | |
72 mIsStarted = true; | |
73 | |
74 removeCallbacks(mHideRunnable); | |
75 mIsHideRunnablePosted = false; | |
76 | |
77 animateAlphaTo(1.0f); | |
78 } | |
79 | |
80 /** | |
81 * Start hiding progress bar animation. | |
82 */ | |
83 public void finish() { | |
84 mIsStarted = false; | |
Ted C
2015/07/15 02:41:12
Is page load finished always going to happen right
Kibeom Kim (inactive)
2015/07/15 11:22:46
I think, regardless of happening right after 100%
Ted C
2015/07/15 17:42:59
Not sure I agree with the "correctness" assertion.
Kibeom Kim (inactive)
2015/07/16 04:45:48
Investigation so far:
In general, progress can be
| |
85 | |
86 removeCallbacks(mHideRunnable); | |
87 postDelayed(mHideRunnable, HIDING_DELAY_MS); | |
88 mIsHideRunnablePosted = true; | |
89 } | |
90 | |
91 /** | |
92 * @return Whether or not this progress bar has animations running or schedu led for | |
93 * showing/hiding itself. | |
94 */ | |
95 @VisibleForTesting | |
96 public boolean isAnimatingForShowOrHide() { | |
97 return mAlphaAnimator.isStarted() || mIsHideRunnablePosted; | |
98 } | |
99 | |
100 private void animateAlphaTo(float targetAlpha) { | |
101 mTargetAlpha = targetAlpha; | |
102 if (getAlpha() != targetAlpha) mAlphaAnimator.start(); | |
103 } | |
104 | |
105 // ClipDrawableProgressBar implementation. | |
106 | |
161 @Override | 107 @Override |
162 public void setProgress(int progress) { | 108 public void setProgress(float progress) { |
163 // If the show animator has started, the progress bar needs to be tracke d as if it is | 109 assert mIsStarted; |
164 // currently showing. This makes sure we trigger the proper hide animat ion and cancel the | |
165 // show animation if we show/hide the bar very fast. See crbug.com/4533 60. | |
166 boolean isShowing = | |
167 getProgress() > 0 || (mShowAnimator != null && mShowAnimator.isS tarted()); | |
168 boolean willShow = progress > 0; | |
169 | |
170 removeCallbacks(mClearLoadProgressRunnable); | |
171 super.setProgress(progress); | 110 super.setProgress(progress); |
172 | |
173 if (isShowing != willShow) { | |
174 if (mShowAnimator == null || mHideAnimator == null) buildAnimators() ; | |
175 | |
176 if (mShowAnimator.isRunning()) mShowAnimator.end(); | |
177 if (mHideAnimator.isRunning()) mHideAnimator.end(); | |
178 | |
179 if (willShow) { | |
180 mShowAnimator.start(); | |
181 } else { | |
182 mHideAnimator.start(); | |
183 } | |
184 } | |
185 } | 111 } |
186 } | 112 } |
OLD | NEW |