/*
* Copyright 2013 Prateek Srivastava (@f2prateek)
* Copyright 2012 Roman Nurik
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.f2prateek.progressbutton;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.widget.CompoundButton;
import com.jerome.lib_song.R;
/**
* A two-state button that indicates whether some related content is pinned (the
* checked state) or unpinned (the unchecked state), and the download progress
* for this content.
* <p/>
* See <a href=
* "http://developer.android.com/design/building-blocks/progress.html#custom-indicators"
* >Android Design: Progress & Activity</a> for more details on this custom
* indicator.
* <p/>
* The example on the website is also the default visual implementation that is
* provided. By default the button is not clickable (by the user). If you want
* the user to be able to control the state, use
* {@link android.view.View#setClickable(boolean)}.
*
* @see android.view.View#setClickable(boolean)
* @see android.view.View#setFocusable(boolean)
*/
public class ProgressButton extends CompoundButton {
/** The maximum progress. Defaults to 100. */
private int mMax;
/** The current progress. Defaults to 0. */
private int mProgress;
/** The drawable used as the shadow. */
private Drawable mShadowDrawable;
/** The drawable displayed when the user unpins an item. */
private Drawable mUnpinnedDrawable;
/** The drawable displayed when the user pins an item. */
private Drawable mPinnedDrawable;
/** The paint for the circle. */
private Paint mCirclePaint;
/**
* The paint tp show the progress.
*
* @see #mProgress
*/
private Paint mProgressPaint;
private Rect mTempRect = new Rect();
private RectF mTempRectF = new RectF();
private int mDrawableSize;
private int mInnerSize;
public ProgressButton(Context context) {
this(context, null);
}
public ProgressButton(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.progressButtonStyle);
}
public ProgressButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs, defStyle);
}
/**
* Initialise the {@link ProgressButton}.
*
* @param context
* the application environment
* @param attrs
* Attribute Set provided
* @param defStyle
* unused.
*/
private void init(Context context, AttributeSet attrs, int defStyle) {
// Attribute initialization
final TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.ProgressButton, R.attr.progressButtonStyle,
R.style.ProgressButton_Pin);
final Resources res = getResources();
mProgress = a.getInteger(R.styleable.ProgressButton_progress, 0);
mMax = a.getInteger(R.styleable.ProgressButton_max, 100);
int circleColor = res.getColor(R.color.progress_default_circle_color);
circleColor = a.getColor(R.styleable.ProgressButton_circleColor,
circleColor);
int progressColor = res
.getColor(R.color.progress_default_progress_color);
progressColor = a.getColor(R.styleable.ProgressButton_progressColor,
progressColor);
int pinnedDrawable = a.getResourceId(
R.styleable.ProgressButton_pinnedDrawable,
R.drawable.pin_progress_pinned);
mPinnedDrawable = res.getDrawable(pinnedDrawable);
mPinnedDrawable.setCallback(this);
int unpinnedDrawable = a.getResourceId(
R.styleable.ProgressButton_unpinnedDrawable,
R.drawable.pin_progress_unpinned);
mUnpinnedDrawable = res.getDrawable(unpinnedDrawable);
mUnpinnedDrawable.setCallback(this);
int shadowDrawable = a.getResourceId(
R.styleable.ProgressButton_pbshadowDrawable,
R.drawable.pin_progress_shadow);
mShadowDrawable = res.getDrawable(shadowDrawable);
mShadowDrawable.setCallback(this);
mInnerSize = a.getDimensionPixelSize(
R.styleable.ProgressButton_innerSize, mInnerSize);
mInnerSize = res.getDimensionPixelSize(R.dimen.progress_inner_size);
setChecked(a.getBoolean(R.styleable.ProgressButton_pinned, false));
setClickable(a.getBoolean(R.styleable.ProgressButton_android_clickable,
false));
setFocusable(a.getBoolean(R.styleable.ProgressButton_android_focusable,
false));
setBackground(a
.getDrawable(R.styleable.ProgressButton_android_selectableItemBackground));
a.recycle();
mDrawableSize = mShadowDrawable.getIntrinsicWidth();
mCirclePaint = new Paint();
mCirclePaint.setColor(circleColor);
mCirclePaint.setAntiAlias(true);
mProgressPaint = new Paint();
mProgressPaint.setColor(progressColor);
mProgressPaint.setAntiAlias(true);
}
/** Returns the maximum progress value. */
public int getMax() {
return mMax;
}
/** Sets the maximum progress value. Defaults to 100. */
public void setMax(int max) {
mMax = max;
invalidate();
}
/** Returns the current progress from 0 to max. */
public int getProgress() {
return mProgress;
}
/**
* Sets the current progress (must be between 0 and max).
*
* @see #setMax(int)
*/
public void setProgress(int progress) {
if (progress > mMax || progress < 0) {
throw new IllegalArgumentException(String.format(
"Progress (%d) must be between %d and %d", progress, 0,
mMax));
}
mProgress = progress;
invalidate();
}
/** Get the color used to display the progress level. */
public int getProgressColor() {
return mProgressPaint.getColor();
}
/** Sets the color used to display the progress level. */
public void setProgressColor(int progressColor) {
mProgressPaint.setColor(progressColor);
invalidate();
}
/** Get the color used to display the progress background. */
public int getCircleColor() {
return mCirclePaint.getColor();
}
/** Sets the color used to display the progress background. */
public void setCircleColor(int circleColor) {
mCirclePaint.setColor(circleColor);
invalidate();
}
/** Get the drawable that is displayed when the item is pinned. */
public Drawable getPinnedDrawable() {
return mPinnedDrawable;
}
/** Set the drawable that is displayed when the item is pinned. */
public void setPinnedDrawable(Drawable pinnedDrawable) {
mPinnedDrawable = pinnedDrawable;
invalidate();
}
/** Get the drawable that is displayed when the item is unpinned. */
public Drawable getUnpinnedDrawable() {
return mUnpinnedDrawable;
}
/** Set the drawable that is displayed when the item is unpinned. */
public void setUnpinnedDrawable(Drawable unpinnedDrawable) {
mUnpinnedDrawable = unpinnedDrawable;
invalidate();
}
/** Get the drawable that is displayed as the shadow. */
public Drawable getShadowDrawable() {
return mShadowDrawable;
}
/** Set the drawable that is displayed as the shadow. */
public void setShadowDrawable(Drawable shadowDrawable) {
mShadowDrawable = shadowDrawable;
mDrawableSize = mShadowDrawable.getIntrinsicWidth();
invalidate();
}
public int getInnerSize() {
return mInnerSize;
}
public void setInnerSize(int innerSize) {
mInnerSize = innerSize;
invalidate();
}
/**
* Get whether the button is pinned or not. Equivalent to
* {@link CompoundButton#isChecked()}
*/
public boolean isPinned() {
return isChecked();
}
/**
* Set whether the button is pinned or not. Equivalent to
* {@link CompoundButton#setChecked(boolean)}
*/
public void setPinned(boolean pinned) {
setChecked(pinned);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(resolveSize(mDrawableSize, widthMeasureSpec),
resolveSize(mDrawableSize, heightMeasureSpec));
}
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
if (mPinnedDrawable.isStateful()) {
mPinnedDrawable.setState(getDrawableState());
}
if (mUnpinnedDrawable.isStateful()) {
mUnpinnedDrawable.setState(getDrawableState());
}
if (mShadowDrawable.isStateful()) {
mShadowDrawable.setState(getDrawableState());
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mTempRect.set(0, 0, mDrawableSize, mDrawableSize);
mTempRect.offset((getWidth() - mDrawableSize) / 2,
(getHeight() - mDrawableSize) / 2);
mTempRectF.set(-0.5f, -0.5f, mInnerSize + 0.5f, mInnerSize + 0.5f);
mTempRectF.offset((getWidth() - mInnerSize) / 2,
(getHeight() - mInnerSize) / 2);
canvas.drawArc(mTempRectF, 0, 360, true, mCirclePaint);
canvas.drawArc(mTempRectF, -90, 360 * mProgress / mMax, true,
mProgressPaint);
Drawable iconDrawable = isChecked() ? mPinnedDrawable
: mUnpinnedDrawable;
iconDrawable.setBounds(mTempRect);
iconDrawable.draw(canvas);
mShadowDrawable.setBounds(mTempRect);
mShadowDrawable.draw(canvas);
}
@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
if (isSaveEnabled()) {
SavedState ss = new SavedState(superState);
ss.mMax = mMax;
ss.mProgress = mProgress;
return ss;
}
return superState;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
mMax = ss.mMax;
mProgress = ss.mProgress;
}
/**
* A {@link android.os.Parcelable} representing the {@link ProgressButton}'s
* state.
*/
public static class SavedState extends BaseSavedState {
public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
@Override
public SavedState createFromParcel(Parcel parcel) {
return new SavedState(parcel);
}
@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
private int mProgress;
private int mMax;
public SavedState(Parcelable superState) {
super(superState);
}
private SavedState(Parcel in) {
super(in);
mProgress = in.readInt();
mMax = in.readInt();
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(mProgress);
out.writeInt(mMax);
}
}
}