Index: chrome/android/java/src/org/chromium/chrome/browser/widget/ClipDrawableProgressBar.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/ClipDrawableProgressBar.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/ClipDrawableProgressBar.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..eabd8e3fa80c023bace448cfc7261f561d89fa75 |
--- /dev/null |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/ClipDrawableProgressBar.java |
@@ -0,0 +1,248 @@ |
+// Copyright 2015 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.content.Context; |
+import android.content.res.TypedArray; |
+import android.graphics.Color; |
+import android.graphics.Rect; |
+import android.graphics.drawable.ClipDrawable; |
+import android.graphics.drawable.ColorDrawable; |
+import android.support.v4.view.ViewCompat; |
+import android.util.AttributeSet; |
+import android.view.Gravity; |
+import android.widget.ImageView; |
+ |
+import org.chromium.base.ApiCompatibilityUtils; |
+import org.chromium.chrome.R; |
+ |
+/** |
+ * An alternative progress bar implemented using ClipDrawable for simplicity and performance. |
+ */ |
+public class ClipDrawableProgressBar extends ImageView { |
+ |
+ // ClipDrawable's max is a fixed constant 10000. |
+ // http://developer.android.com/reference/android/graphics/drawable/ClipDrawable.html |
+ private static final int CLIP_DRAWABLE_MAX = 10000; |
+ |
+ private final Rect mDirtyBound = new Rect(); |
+ private InvalidationListener mListener; |
+ private int mProgressBarColor = Color.TRANSPARENT; |
+ private int mBackgroundColor = Color.TRANSPARENT; |
+ private boolean mIsLastInvalidationProgressChange; |
+ private float mProgress; |
+ private int mDesiredVisibility; |
+ |
+ /** |
+ * Interface for listening to drawing invalidation. |
+ */ |
+ public interface InvalidationListener { |
+ /** |
+ * Called on drawing invalidation. |
+ * @param dirtyRect Invalidated area. |
+ */ |
+ void onInvalidation(Rect dirtyRect); |
+ } |
+ |
+ /** |
+ * Constructor for inflating from XML. |
+ */ |
+ public ClipDrawableProgressBar(Context context, AttributeSet attrs) { |
+ super(context, attrs); |
+ init(context, attrs, 0); |
+ } |
+ |
+ private void init(Context context, AttributeSet attrs, int defStyle) { |
+ mDesiredVisibility = getVisibility(); |
+ |
+ assert attrs != null; |
+ TypedArray a = context.obtainStyledAttributes( |
+ attrs, R.styleable.ClipDrawableProgressBar, defStyle, 0); |
+ mProgressBarColor = a.getColor( |
+ R.styleable.ClipDrawableProgressBar_progressBarColor, Color.TRANSPARENT); |
+ assert mProgressBarColor != Color.TRANSPARENT; |
+ mBackgroundColor = a.getColor( |
+ R.styleable.ClipDrawableProgressBar_backgroundColor, Color.TRANSPARENT); |
+ a.recycle(); |
+ |
+ setImageDrawable(new ClipDrawable(new ColorDrawable(mProgressBarColor), Gravity.START, |
+ ClipDrawable.HORIZONTAL)); |
+ setBackgroundColor(mBackgroundColor); |
+ } |
+ |
+ /** |
+ * Get the progress bar's current level of progress. |
+ * |
+ * @return The current progress, between 0.0 and 1.0. |
+ */ |
+ public float getProgress() { |
+ return mProgress; |
+ } |
+ |
+ /** |
+ * Set the current progress to the specified value. |
+ * |
+ * @param progress The new progress, between 0.0 and 1.0. |
+ */ |
+ public void setProgress(float progress) { |
+ assert 0.0f <= progress && progress <= 1.0f; |
+ if (mProgress == progress) return; |
+ |
+ float currentProgress = mProgress; |
+ mProgress = progress; |
+ getDrawable().setLevel(Math.round(progress * CLIP_DRAWABLE_MAX)); |
+ |
+ if (getVisibility() == VISIBLE && mListener != null) { |
+ if (mIsLastInvalidationProgressChange) { |
+ float left = Math.min(currentProgress, progress); |
+ float right = Math.max(currentProgress, progress); |
+ |
+ if (ApiCompatibilityUtils.isLayoutRtl(this)) { |
+ float leftTemp = left; |
+ left = 1.0f - right; |
+ right = 1.0f - leftTemp; |
+ } |
+ |
+ mDirtyBound.set((int) (left * getWidth()), 0, |
+ (int) Math.ceil(right * getWidth()), getHeight()); |
+ mListener.onInvalidation(mDirtyBound); |
+ } else { |
+ mListener.onInvalidation(null); |
+ } |
+ |
+ mIsLastInvalidationProgressChange = true; |
+ } |
+ } |
+ |
+ /** |
+ * @return Background color of this progress bar. |
+ */ |
+ public int getProgressBarBackgroundColor() { |
+ return mBackgroundColor; |
+ } |
+ |
+ /** |
+ * Adds an observer to be notified of drawing invalidation. |
+ * |
+ * @param listener The observer to be added. |
+ */ |
+ public void setInvalidationListener(InvalidationListener listener) { |
+ mListener = listener; |
+ mIsLastInvalidationProgressChange = false; |
+ } |
+ |
+ /** |
+ * Get a rect that contains all the non-opaque pixels. |
+ * @param alphaBound Instance used to return the result. |
+ */ |
+ public void getAlphaDrawRegion(Rect alphaBound) { |
+ if (getAlpha() < 1.0f) { |
+ alphaBound.set(0, 0, getWidth(), getHeight()); |
+ return; |
+ } |
+ |
+ alphaBound.setEmpty(); |
+ if (Color.alpha(mBackgroundColor) < 255) { |
+ if (ApiCompatibilityUtils.isLayoutRtl(this)) { |
+ alphaBound.set(0, 0, (int) Math.ceil(mProgress * getWidth()), getHeight()); |
+ } else { |
+ alphaBound.set((int) (mProgress * getWidth()), 0, getWidth(), getHeight()); |
+ } |
+ } |
+ if (Color.alpha(mProgressBarColor) < 255) { |
+ if (ApiCompatibilityUtils.isLayoutRtl(this)) { |
+ alphaBound.union((int) (mProgress * getWidth()), 0, getWidth(), getHeight()); |
+ } else { |
+ alphaBound.union(0, 0, (int) Math.ceil(mProgress * getWidth()), getHeight()); |
+ } |
+ } |
+ |
+ return; |
+ } |
+ |
+ /** |
+ * @return Whether the actual internal visibility is changed. |
+ */ |
+ private boolean updateInternalVisibility() { |
+ int oldVisibility = getVisibility(); |
+ int newVisibility = mDesiredVisibility; |
+ if (getAlpha() == 0 && mDesiredVisibility == VISIBLE) { |
+ newVisibility = INVISIBLE; |
+ } |
+ |
+ if (oldVisibility != newVisibility) { |
+ super.setVisibility(newVisibility); |
+ if (mListener != null) { |
+ mListener.onInvalidation(null); |
+ mIsLastInvalidationProgressChange = false; |
+ } |
+ return true; |
+ } |
+ |
+ return false; |
+ } |
+ |
+ // View implementations. |
+ |
+ /** |
+ * Note that this visibility might not be respected for optimization. For example, if alpha |
+ * is 0, it will remain View#INVISIBLE even if this is called with View#VISIBLE. |
+ */ |
+ @Override |
+ public void setVisibility(int visibility) { |
+ mDesiredVisibility = visibility; |
+ updateInternalVisibility(); |
+ } |
+ |
+ @Override |
+ public void setBackgroundColor(int color) { |
+ if (color == Color.TRANSPARENT) { |
+ setBackground(null); |
+ } else { |
+ super.setBackgroundColor(color); |
+ } |
+ |
+ mBackgroundColor = color; |
+ |
+ if (mListener != null && getVisibility() == VISIBLE) { |
+ mListener.onInvalidation(null); |
+ mIsLastInvalidationProgressChange = false; |
+ } |
+ } |
+ |
+ @Override |
+ protected boolean onSetAlpha(int alpha) { |
+ boolean result = super.onSetAlpha(alpha); |
+ |
+ if (mListener != null && !updateInternalVisibility()) { |
+ mListener.onInvalidation(null); |
+ mIsLastInvalidationProgressChange = false; |
+ } |
+ |
+ return result; |
+ } |
+ |
+ @Override |
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) { |
+ super.onSizeChanged(w, h, oldw, oldh); |
+ |
+ if (mListener != null && getVisibility() == VISIBLE && (w != oldw || h != oldh)) { |
+ mListener.onInvalidation(null); |
+ mIsLastInvalidationProgressChange = false; |
+ } |
+ } |
+ |
+ @Override |
+ public void onRtlPropertiesChanged(int layoutDirection) { |
+ int currentLayoutDirection = ViewCompat.getLayoutDirection(this); |
+ super.onRtlPropertiesChanged(layoutDirection); |
+ |
+ if (mListener != null && getVisibility() == VISIBLE |
+ && currentLayoutDirection != layoutDirection) { |
+ mListener.onInvalidation(null); |
+ mIsLastInvalidationProgressChange = false; |
+ } |
+ } |
+} |