// 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.chrome.R;
/**
* An alternative progress bar implemented using ClipDrawable for simplicity and performance.
*/
public class ClipDrawableProgressBar extends ImageView {
/**
* Structure that has complete {@link ClipDrawableProgressBar} drawing information.
*/
public static class DrawingInfo {
public final Rect progressBarRect = new Rect();
public final Rect progressBarBackgroundRect = new Rect();
public int progressBarColor;
public int progressBarBackgroundColor;
}
// 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 ColorDrawable mForegroundDrawable;
private int mBackgroundColor = Color.TRANSPARENT;
private float mProgress;
private int mProgressUpdateCount;
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);
mDesiredVisibility = getVisibility();
assert attrs != null;
TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.ClipDrawableProgressBar, 0, 0);
int foregroundColor = a.getColor(
R.styleable.ClipDrawableProgressBar_progressBarColor, Color.TRANSPARENT);
mBackgroundColor = a.getColor(
R.styleable.ClipDrawableProgressBar_backgroundColor, Color.TRANSPARENT);
assert foregroundColor != Color.TRANSPARENT;
assert Color.alpha(foregroundColor) == 255
: "Currently ClipDrawableProgressBar only supports opaque progress bar color.";
a.recycle();
mForegroundDrawable = new ColorDrawable(foregroundColor);
setImageDrawable(
new ClipDrawable(mForegroundDrawable, 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;
mProgress = progress;
mProgressUpdateCount += 1;
getDrawable().setLevel(Math.round(progress * CLIP_DRAWABLE_MAX));
}
/**
* @return Background color of this progress bar.
*/
public int getProgressBarBackgroundColor() {
return mBackgroundColor;
}
/**
* @return Foreground color of the progress bar.
*/
public int getForegroundColor() {
return mForegroundDrawable.getColor();
}
/**
* Get progress bar drawing information.
* @param drawingInfoOut An instance that the result will be written.
*/
public void getDrawingInfo(DrawingInfo drawingInfoOut) {
int foregroundColor = mForegroundDrawable.getColor();
float effectiveAlpha = getVisibility() == VISIBLE ? getAlpha() : 0.0f;
drawingInfoOut.progressBarColor = applyAlpha(foregroundColor, effectiveAlpha);
drawingInfoOut.progressBarBackgroundColor = applyAlpha(mBackgroundColor, effectiveAlpha);
if (ViewCompat.getLayoutDirection(this) == LAYOUT_DIRECTION_LTR) {
drawingInfoOut.progressBarRect.set(
getLeft(),
getTop(),
getLeft() + Math.round(mProgress * getWidth()),
getBottom());
drawingInfoOut.progressBarBackgroundRect.set(
drawingInfoOut.progressBarRect.right,
getTop(),
getRight(),
getBottom());
} else {
drawingInfoOut.progressBarRect.set(
getRight() - Math.round(mProgress * getWidth()),
getTop(),
getRight(),
getBottom());
drawingInfoOut.progressBarBackgroundRect.set(
getLeft(),
getTop(),
drawingInfoOut.progressBarRect.left,
getBottom());
}
}
/**
* Resets progress update count to 0.
*/
public void resetProgressUpdateCount() {
mProgressUpdateCount = 0;
}
/**
* @return Progress update count since reset.
*/
public int getProgressUpdateCount() {
return mProgressUpdateCount;
}
private void updateInternalVisibility() {
int oldVisibility = getVisibility();
int newVisibility = mDesiredVisibility;
if (getAlpha() == 0 && mDesiredVisibility == VISIBLE) newVisibility = INVISIBLE;
if (oldVisibility != newVisibility) super.setVisibility(newVisibility);
}
private int applyAlpha(int color, float alpha) {
return (Math.round(alpha * (color >>> 24)) << 24) | (0x00ffffff & color);
}
// 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;
}
/**
* Sets the color for the foreground (i.e. the moving part) of the progress bar.
* @param color The new color of the progress bar foreground.
*/
public void setForegroundColor(int color) {
mForegroundDrawable.setColor(color);
}
@Override
protected boolean onSetAlpha(int alpha) {
updateInternalVisibility();
return super.onSetAlpha(alpha);
}
}