Index: chrome/android/java/src/org/chromium/chrome/browser/widget/MaterialProgressBar.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/MaterialProgressBar.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/MaterialProgressBar.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ba11dcd72cce27b22087a872ef2de5cebb88cdf5 |
--- /dev/null |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/MaterialProgressBar.java |
@@ -0,0 +1,256 @@ |
+// Copyright 2016 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. |
+ |
+package org.chromium.chrome.browser.widget; |
+ |
+import android.animation.ValueAnimator; |
+import android.animation.ValueAnimator.AnimatorUpdateListener; |
+import android.content.Context; |
+import android.content.res.Resources; |
+import android.content.res.TypedArray; |
+import android.graphics.Canvas; |
+import android.graphics.Paint; |
+import android.support.v4.view.ViewCompat; |
+import android.util.AttributeSet; |
+import android.view.View; |
+ |
+import org.chromium.base.ApiCompatibilityUtils; |
+import org.chromium.chrome.R; |
+ |
+/** |
+ * Material-styled horizontal progress bar |
+ * |
+ * This class is to be used in place of the support library's progress bar, which does not support |
+ * indeterminate progress bar styling on JB or KK. |
+ * |
+ * ------------------------------------------------------------------------------------------------- |
+ * DESIGN SPEC |
+ * |
+ * https://material.io/guidelines/components/progress-activity.html |
+ * |
+ * Secondary progress is represented by a second bar that is drawn on top of the primary progress, |
+ * and is completely optional. |
+ * |
+ * ------------------------------------------------------------------------------------------------- |
+ * DEFINING THE CONTROL STYLING IN AN XML LAYOUT |
+ * |
+ * Add the "chrome" namespace as an attribute to the main tag of the layout file: |
+ * xmlns:chrome="http://schemas.android.com/apk/res-auto |
+ * |
+ * These attributes control styling of the bar: |
+ * chrome:colorBackground Background color of a determinate progress bar. |
+ * chrome:colorProgress Represents progress along a determinate progress bar. |
+ * Also used as the background color for indeterminate progress. |
+ * chrome:colorSecondaryProgress Represents secondary progress on top of the regular progress. |
+ * chrome:colorThrobber Color of the pulses fired along the indeterminate progress bar. |
+ */ |
+public class MaterialProgressBar extends View implements AnimatorUpdateListener { |
+ private static final long INDETERMINATE_ANIMATION_DURATION_MS = 2500; |
+ |
+ private final ValueAnimator mIndeterminateAnimator = ValueAnimator.ofFloat(0.0f, 3.0f); |
+ private final Paint mBackgroundPaint = new Paint(); |
+ private final Paint mProgressPaint = new Paint(); |
+ private final Paint mSecondaryProgressPaint = new Paint(); |
+ private final Paint mThrobberPaint = new Paint(); |
+ |
+ private boolean mIsIndeterminate; |
+ private int mProgress; |
+ private int mSecondaryProgress; |
+ |
+ public MaterialProgressBar(Context context) { |
+ super(context); |
+ initialize(context, null, 0); |
+ } |
+ |
+ public MaterialProgressBar(Context context, AttributeSet attrs) { |
+ super(context, attrs); |
+ initialize(context, attrs, 0); |
+ } |
+ |
+ public MaterialProgressBar(Context context, AttributeSet attrs, int defStyle) { |
+ super(context, attrs, defStyle); |
+ initialize(context, attrs, defStyle); |
+ } |
+ |
+ /** Sets the background color, corresponding to "chrome:colorBackground". */ |
+ @Override |
+ public void setBackgroundColor(int color) { |
+ mBackgroundPaint.setColor(color); |
+ postInvalidateOnAnimation(); |
+ } |
+ |
+ /** Sets the progress color, corresponding to "chrome:colorProgress". */ |
+ public void setProgressColor(int color) { |
+ mProgressPaint.setColor(color); |
+ postInvalidateOnAnimation(); |
+ } |
+ |
+ /** Sets the secondary color, corresponding to "chrome:colorSecondaryProgress". */ |
+ public void setSecondaryProgressColor(int color) { |
+ mSecondaryProgressPaint.setColor(color); |
+ postInvalidateOnAnimation(); |
+ } |
+ |
+ /** Sets the indeterminate throbber color, corresponding to "chrome:colorThrobber". */ |
+ public void setThrobberColor(int color) { |
+ mThrobberPaint.setColor(color); |
+ postInvalidateOnAnimation(); |
+ } |
+ |
+ /** |
+ * Sets the progress value being displayed. |
+ * @param progress Progress value, ranging from 0 to 100. Will be clamped into the range. |
+ */ |
+ public void setProgress(int progress) { |
+ mProgress = Math.max(0, Math.min(100, progress)); |
+ postInvalidateOnAnimation(); |
+ } |
+ |
+ /** |
+ * Sets the secondary progress value being displayed. |
+ * @param progress Progress value, ranging from 0 to 100. Will be clamped into the range. |
+ */ |
+ public void setSecondaryProgress(int progress) { |
+ mSecondaryProgress = Math.max(0, Math.min(100, progress)); |
+ postInvalidateOnAnimation(); |
+ } |
+ |
+ /** Sets whether the progress bar is indeterminate or not. */ |
+ public void setIndeterminate(boolean indeterminate) { |
+ if (mIsIndeterminate == indeterminate) return; |
+ mIsIndeterminate = indeterminate; |
+ |
+ if (mIsIndeterminate) startIndeterminateAnimation(); |
+ postInvalidateOnAnimation(); |
+ } |
+ |
+ @Override |
+ public void setVisibility(int visibility) { |
+ super.setVisibility(visibility); |
+ if (visibility == View.VISIBLE) { |
+ startIndeterminateAnimation(); |
+ } else { |
+ stopIndeterminateAnimation(); |
+ } |
+ } |
+ |
+ @Override |
+ public void onAnimationUpdate(ValueAnimator animation) { |
+ postInvalidateOnAnimation(); |
+ } |
+ |
+ @Override |
+ public void onDraw(Canvas canvas) { |
+ if (mIsIndeterminate) { |
+ drawIndeterminateBar(canvas); |
+ } else { |
+ drawDeterminateBar(canvas); |
Ian Wen
2016/12/20 23:17:58
Not sure if we need to support determinate bar in
gone
2016/12/21 00:54:23
We could, but I'm going for an all encompassing so
|
+ } |
+ } |
+ |
+ @Override |
+ public void onAttachedToWindow() { |
+ super.onAttachedToWindow(); |
+ startIndeterminateAnimation(); |
+ } |
+ |
+ @Override |
+ public void onDetachedFromWindow() { |
+ super.onDetachedFromWindow(); |
+ stopIndeterminateAnimation(); |
+ } |
+ |
+ private void initialize(Context context, AttributeSet attrs, int defStyle) { |
+ Resources resources = context.getResources(); |
+ int backgroundColor = |
+ ApiCompatibilityUtils.getColor(resources, R.color.progress_bar_background); |
+ int progressColor = |
+ ApiCompatibilityUtils.getColor(resources, R.color.progress_bar_foreground); |
+ int secondaryProgressColor = |
+ ApiCompatibilityUtils.getColor(resources, R.color.progress_bar_secondary); |
+ int throbberColor = |
+ ApiCompatibilityUtils.getColor(resources, R.color.progress_bar_default_throbber); |
+ |
+ if (attrs != null) { |
+ TypedArray a = context.obtainStyledAttributes( |
+ attrs, R.styleable.MaterialProgressBar, defStyle, 0); |
+ backgroundColor = |
+ a.getColor(R.styleable.MaterialProgressBar_colorBackground, backgroundColor); |
+ progressColor = |
+ a.getColor(R.styleable.MaterialProgressBar_colorProgress, progressColor); |
+ secondaryProgressColor = a.getColor( |
+ R.styleable.MaterialProgressBar_colorSecondaryProgress, secondaryProgressColor); |
+ throbberColor = |
+ a.getColor(R.styleable.MaterialProgressBar_colorThrobber, throbberColor); |
+ a.recycle(); |
+ } |
+ |
+ setBackgroundColor(backgroundColor); |
+ setProgressColor(progressColor); |
+ setSecondaryProgressColor(secondaryProgressColor); |
+ setThrobberColor(throbberColor); |
+ |
+ mIndeterminateAnimator.setRepeatCount(ValueAnimator.INFINITE); |
+ mIndeterminateAnimator.setDuration(INDETERMINATE_ANIMATION_DURATION_MS); |
+ mIndeterminateAnimator.addUpdateListener(this); |
+ } |
+ |
+ private void startIndeterminateAnimation() { |
+ if (!mIsIndeterminate || mIndeterminateAnimator.isRunning()) return; |
+ if (!ViewCompat.isAttachedToWindow(this) || getVisibility() != View.VISIBLE) return; |
+ mIndeterminateAnimator.start(); |
+ } |
+ |
+ private void stopIndeterminateAnimation() { |
+ if (!mIndeterminateAnimator.isRunning()) return; |
+ mIndeterminateAnimator.cancel(); |
+ } |
+ |
+ private void drawIndeterminateBar(Canvas canvas) { |
+ int width = canvas.getWidth(); |
+ drawRect(canvas, mProgressPaint, 0, width); |
+ |
+ // The first pulse fires off at the beginning of the animation. |
+ float value = (Float) mIndeterminateAnimator.getAnimatedValue(); |
+ float left = width * (float) (Math.pow(value, 1.5f) - 0.5f); |
+ float right = width * value; |
+ drawRect(canvas, mThrobberPaint, left, right); |
+ |
+ // The second pulse fires off at some point after the first pulse has been fired. |
+ final float secondPulseStart = 1.1f; |
+ final float secondPulseLength = 1.0f; |
+ if (value >= secondPulseStart) { |
+ float percentage = (value - secondPulseStart) / secondPulseLength; |
+ left = width * (float) (Math.pow(percentage, 3.0f) - 0.1f); |
+ right = width * percentage; |
+ drawRect(canvas, mThrobberPaint, left, right); |
+ } |
+ } |
+ |
+ private void drawDeterminateBar(Canvas canvas) { |
+ int width = canvas.getWidth(); |
+ drawRect(canvas, mBackgroundPaint, 0, width); |
+ |
+ if (mProgress > 0) { |
+ float percentage = mProgress / 100.0f; |
+ drawRect(canvas, mProgressPaint, 0, width * percentage); |
+ } |
+ |
+ if (mSecondaryProgress > 0) { |
+ float percentage = mSecondaryProgress / 100.0f; |
+ drawRect(canvas, mSecondaryProgressPaint, 0, width * percentage); |
+ } |
+ } |
+ |
+ private void drawRect(Canvas canvas, Paint paint, float start, float end) { |
+ if (ViewCompat.getLayoutDirection(this) == View.LAYOUT_DIRECTION_RTL) { |
+ int width = canvas.getWidth(); |
+ float rtlStart = width - end; |
+ float rtlEnd = width - start; |
+ canvas.drawRect(rtlStart, 0, rtlEnd, canvas.getHeight(), paint); |
+ } else { |
+ canvas.drawRect(start, 0, end, canvas.getHeight(), paint); |
+ } |
+ } |
+} |