package external.circularprocessbutton;
import com.aiyou.AiYouApplication;
import com.aiyou.R;
import com.aiyou.utils.SwitchManager;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.widget.Button;
public class CircularProgressButton extends Button {
public static final int IDLE_STATE_PROGRESS = 0;
public static final int ERROR_STATE_PROGRESS = -1;
private StrokeGradientDrawable background;
private CircularAnimatedDrawable mAnimatedDrawable;
private CircularProgressDrawable mProgressDrawable;
private StateManager mStateManager;
private State mState;
private String mIdleText;
private String mCompleteText;
private String mErrorText;
private int mColorDisabled;
private int mColorIdle;
private int mColorError;
private int mColorProgress;
private int mColorComplete;
private int mColorIndicator;
private int mColorIndicatorBackground;
private int mIconComplete;
private int mIconError;
private int mStrokeWidth;
private int mPaddingProgress;
private float mCornerRadius;
private boolean mIndeterminateProgressMode;
private boolean mConfigurationChanged;
private enum State {
PROGRESS, IDLE, COMPLETE, ERROR
}
private int mMaxProgress;
private int mProgress;
private boolean mMorphingInProgress;
public CircularProgressButton(Context context) {
super(context);
init(context, null);
}
public CircularProgressButton(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public CircularProgressButton(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
private void init(Context context, AttributeSet attributeSet) {
mStrokeWidth = (int) getContext().getResources().getDimension(
R.dimen.stroke_width);
initAttributes(context, attributeSet);
mMaxProgress = 100;
mState = State.IDLE;
mStateManager = new StateManager(this);
setText(mIdleText);
GradientDrawable gradientDrawable = (GradientDrawable) context
.getResources()
.getDrawable(R.drawable.background_button_processbar).mutate();
gradientDrawable.setColor(mColorIdle);
gradientDrawable.setCornerRadius(mCornerRadius);
background = new StrokeGradientDrawable(gradientDrawable);
background.setStrokeColor(mColorIdle);
background.setStrokeWidth(mStrokeWidth);
setBackgroundCompat(gradientDrawable);
}
private void initAttributes(Context context, AttributeSet attributeSet) {
TypedArray attr = getTypedArray(context, attributeSet,
R.styleable.CircularProgressButton);
if (attr == null) {
return;
}
try {
mIdleText = attr
.getString(R.styleable.CircularProgressButton_cpb_textIdle);
mCompleteText = attr
.getString(R.styleable.CircularProgressButton_cpb_textComplete);
mErrorText = attr
.getString(R.styleable.CircularProgressButton_cpb_textError);
mIconComplete = attr.getResourceId(
R.styleable.CircularProgressButton_cpb_iconComplete, 0);
mIconError = attr.getResourceId(
R.styleable.CircularProgressButton_cpb_iconError, 0);
mCornerRadius = attr.getDimension(
R.styleable.CircularProgressButton_cpb_cornerRadius, 0);
mPaddingProgress = attr.getDimensionPixelSize(
R.styleable.CircularProgressButton_cpb_paddingProgress, 0);
int blue = getColor(R.color.blue);
int red = getColor(R.color.red);
int green = getColor(R.color.green);
// int white = getColor(R.color.white);
int grey = getColor(R.color.grey);
int modeColor = 0xffffffff;
if (SwitchManager.getInstance(AiYouApplication.getInstance()).isNightModeEnabled()) {
modeColor = 0xff222222;
}
mColorDisabled = attr.getColor(
R.styleable.CircularProgressButton_cpb_colorDisabled, grey);
mColorIdle = attr.getColor(
R.styleable.CircularProgressButton_cpb_colorIdle, blue);
mColorError = attr.getColor(
R.styleable.CircularProgressButton_cpb_colorError, red);
mColorComplete = attr
.getColor(
R.styleable.CircularProgressButton_cpb_colorComplete,
green);
mColorProgress = attr.getColor(
R.styleable.CircularProgressButton_cpb_colorProgress,
modeColor);
mColorIndicator = attr
.getColor(
R.styleable.CircularProgressButton_cpb_colorIndicator,
blue);
mColorIndicatorBackground = attr
.getColor(
R.styleable.CircularProgressButton_cpb_colorIndicatorBackground,
modeColor);
} finally {
attr.recycle();
}
}
protected int getColor(int id) {
return getResources().getColor(id);
}
protected TypedArray getTypedArray(Context context,
AttributeSet attributeSet, int[] attr) {
return context.obtainStyledAttributes(attributeSet, attr, 0, 0);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mProgress > 0 && mState == State.PROGRESS && !mMorphingInProgress) {
if (mIndeterminateProgressMode) {
drawIndeterminateProgress(canvas);
} else {
drawProgress(canvas);
}
}
}
private void drawIndeterminateProgress(Canvas canvas) {
if (mAnimatedDrawable == null) {
int offset = (getWidth() - getHeight()) / 2;
mAnimatedDrawable = new CircularAnimatedDrawable(mColorIndicator,
mStrokeWidth);
int left = offset + mPaddingProgress;
int right = getWidth() - offset - mPaddingProgress;
int bottom = getHeight() - mPaddingProgress;
int top = mPaddingProgress;
mAnimatedDrawable.setBounds(left, top, right, bottom);
mAnimatedDrawable.setCallback(this);
mAnimatedDrawable.start();
} else {
mAnimatedDrawable.draw(canvas);
}
}
private void drawProgress(Canvas canvas) {
if (mProgressDrawable == null) {
int offset = (getWidth() - getHeight()) / 2;
int size = getHeight() - mPaddingProgress * 2;
mProgressDrawable = new CircularProgressDrawable(size,
mStrokeWidth, mColorIndicator);
int left = offset + mPaddingProgress;
mProgressDrawable.setBounds(left, mPaddingProgress, left,
mPaddingProgress);
}
float sweepAngle = (360f / mMaxProgress) * mProgress;
mProgressDrawable.setSweepAngle(sweepAngle);
mProgressDrawable.draw(canvas);
}
public boolean isIndeterminateProgressMode() {
return mIndeterminateProgressMode;
}
public void setIndeterminateProgressMode(boolean indeterminateProgressMode) {
this.mIndeterminateProgressMode = indeterminateProgressMode;
}
@Override
protected boolean verifyDrawable(Drawable who) {
return who == mAnimatedDrawable || super.verifyDrawable(who);
}
private MorphingAnimation createMorphing() {
mMorphingInProgress = true;
MorphingAnimation animation = new MorphingAnimation(this, background);
animation.setFromCornerRadius(mCornerRadius);
animation.setToCornerRadius(mCornerRadius);
animation.setFromWidth(getWidth());
animation.setToWidth(getWidth());
if (mConfigurationChanged) {
animation.setDuration(MorphingAnimation.DURATION_INSTANT);
} else {
animation.setDuration(MorphingAnimation.DURATION_NORMAL);
}
mConfigurationChanged = false;
return animation;
}
private MorphingAnimation createProgressMorphing(float fromCorner,
float toCorner, int fromWidth, int toWidth) {
mMorphingInProgress = true;
MorphingAnimation animation = new MorphingAnimation(this, background);
animation.setFromCornerRadius(fromCorner);
animation.setToCornerRadius(toCorner);
animation.setPadding(mPaddingProgress);
animation.setFromWidth(fromWidth);
animation.setToWidth(toWidth);
if (mConfigurationChanged) {
animation.setDuration(MorphingAnimation.DURATION_INSTANT);
} else {
animation.setDuration(MorphingAnimation.DURATION_NORMAL);
}
mConfigurationChanged = false;
return animation;
}
private void morphToProgress() {
setWidth(getWidth());
setText(null);
MorphingAnimation animation = createProgressMorphing(mCornerRadius,
getHeight(), getWidth(), getHeight());
animation.setFromColor(mColorIdle);
animation.setToColor(mColorProgress);
animation.setFromStrokeColor(mColorIdle);
animation.setToStrokeColor(mColorIndicatorBackground);
animation.setListener(mProgressStateListener);
animation.start();
}
private OnAnimationEndListener mProgressStateListener = new OnAnimationEndListener() {
@Override
public void onAnimationEnd() {
mMorphingInProgress = false;
mState = State.PROGRESS;
mStateManager.checkState(CircularProgressButton.this);
}
};
private void morphProgressToComplete() {
MorphingAnimation animation = createProgressMorphing(getHeight(),
mCornerRadius, getHeight(), getWidth());
animation.setFromColor(mColorProgress);
animation.setToColor(mColorComplete);
animation.setFromStrokeColor(mColorIndicator);
animation.setToStrokeColor(mColorComplete);
animation.setListener(mCompleteStateListener);
animation.start();
}
private void morphIdleToComplete() {
MorphingAnimation animation = createMorphing();
animation.setFromColor(mColorIdle);
animation.setToColor(mColorComplete);
animation.setFromStrokeColor(mColorIdle);
animation.setToStrokeColor(mColorComplete);
animation.setListener(mCompleteStateListener);
animation.start();
}
private OnAnimationEndListener mCompleteStateListener = new OnAnimationEndListener() {
@Override
public void onAnimationEnd() {
if (mIconComplete != 0) {
setText(null);
setIcon(mIconComplete);
} else {
setText(mCompleteText);
}
mMorphingInProgress = false;
mState = State.COMPLETE;
mStateManager.checkState(CircularProgressButton.this);
}
};
private void morphCompleteToIdle() {
MorphingAnimation animation = createMorphing();
animation.setFromColor(mColorComplete);
animation.setToColor(mColorIdle);
animation.setFromStrokeColor(mColorComplete);
animation.setToStrokeColor(mColorIdle);
animation.setListener(mIdleStateListener);
animation.start();
}
private void morphErrorToIdle() {
MorphingAnimation animation = createMorphing();
animation.setFromColor(mColorError);
animation.setToColor(mColorIdle);
animation.setFromStrokeColor(mColorError);
animation.setToStrokeColor(mColorIdle);
animation.setListener(mIdleStateListener);
animation.start();
}
private OnAnimationEndListener mIdleStateListener = new OnAnimationEndListener() {
@Override
public void onAnimationEnd() {
removeIcon();
setText(mIdleText);
mMorphingInProgress = false;
mState = State.IDLE;
mStateManager.checkState(CircularProgressButton.this);
}
};
private void morphIdleToError() {
MorphingAnimation animation = createMorphing();
animation.setFromColor(mColorIdle);
animation.setToColor(mColorError);
animation.setFromStrokeColor(mColorIdle);
animation.setToStrokeColor(mColorError);
animation.setListener(mErrorStateListener);
animation.start();
}
private void morphProgressToError() {
MorphingAnimation animation = createProgressMorphing(getHeight(),
mCornerRadius, getHeight(), getWidth());
animation.setFromColor(mColorProgress);
animation.setToColor(mColorError);
animation.setFromStrokeColor(mColorIndicator);
animation.setToStrokeColor(mColorError);
animation.setListener(mErrorStateListener);
animation.start();
}
private OnAnimationEndListener mErrorStateListener = new OnAnimationEndListener() {
@Override
public void onAnimationEnd() {
if (mIconComplete != 0) {
setText(null);
setIcon(mIconError);
} else {
setText(mErrorText);
}
mMorphingInProgress = false;
mState = State.ERROR;
mStateManager.checkState(CircularProgressButton.this);
}
};
private void morphProgressToIdle() {
MorphingAnimation animation = createProgressMorphing(getHeight(),
mCornerRadius, getHeight(), getWidth());
animation.setFromColor(mColorProgress);
animation.setToColor(mColorIdle);
animation.setFromStrokeColor(mColorIndicator);
animation.setToStrokeColor(mColorIdle);
animation.setListener(new OnAnimationEndListener() {
@Override
public void onAnimationEnd() {
removeIcon();
setText(mIdleText);
mMorphingInProgress = false;
mState = State.IDLE;
mStateManager.checkState(CircularProgressButton.this);
}
});
animation.start();
}
private void setIcon(int icon) {
Drawable drawable = getResources().getDrawable(icon);
if (drawable != null) {
int padding = (getWidth() / 2) - (drawable.getIntrinsicWidth() / 2);
setCompoundDrawablesWithIntrinsicBounds(icon, 0, 0, 0);
setPadding(padding, 0, 0, 0);
}
}
protected void removeIcon() {
setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
setPadding(0, 0, 0, 0);
}
/**
* Set the View's background. Masks the API changes made in Jelly Bean.
*/
@SuppressWarnings("deprecation")
@SuppressLint("NewApi")
public void setBackgroundCompat(Drawable drawable) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
setBackground(drawable);
} else {
setBackgroundDrawable(drawable);
}
}
public void setProgress(int progress) {
mProgress = progress;
if (mMorphingInProgress || getWidth() == 0) {
return;
}
mStateManager.saveProgress(this);
if (mProgress >= mMaxProgress) {
if (mState == State.PROGRESS) {
morphProgressToComplete();
} else if (mState == State.IDLE) {
morphIdleToComplete();
}
} else if (mProgress > IDLE_STATE_PROGRESS) {
if (mState == State.IDLE) {
morphToProgress();
} else if (mState == State.PROGRESS) {
invalidate();
} else if (mState == State.ERROR) {
morphErrorToIdle();
morphToProgress();
}
} else if (mProgress == ERROR_STATE_PROGRESS) {
if (mState == State.PROGRESS) {
morphProgressToError();
} else if (mState == State.IDLE) {
morphIdleToError();
}
} else if (mProgress == IDLE_STATE_PROGRESS) {
if (mState == State.COMPLETE) {
morphCompleteToIdle();
} else if (mState == State.PROGRESS) {
morphProgressToIdle();
} else if (mState == State.ERROR) {
morphErrorToIdle();
}
}
}
public int getProgress() {
return mProgress;
}
public void setBackgroundColor(int color) {
background.getGradientDrawable().setColor(color);
}
public void setStrokeColor(int color) {
background.setStrokeColor(color);
}
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
if (enabled) {
setStrokeColor(mColorIdle);
setBackgroundColor(mColorIdle);
} else {
setStrokeColor(mColorDisabled);
setBackgroundColor(mColorDisabled);
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (changed) {
setProgress(mProgress);
}
}
@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState savedState = new SavedState(superState);
savedState.mProgress = mProgress;
savedState.mIndeterminateProgressMode = mIndeterminateProgressMode;
savedState.mConfigurationChanged = true;
return savedState;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
if (state instanceof SavedState) {
SavedState savedState = (SavedState) state;
mProgress = savedState.mProgress;
mIndeterminateProgressMode = savedState.mIndeterminateProgressMode;
mConfigurationChanged = savedState.mConfigurationChanged;
super.onRestoreInstanceState(savedState.getSuperState());
setProgress(mProgress);
} else {
super.onRestoreInstanceState(state);
}
}
static class SavedState extends BaseSavedState {
private boolean mIndeterminateProgressMode;
private boolean mConfigurationChanged;
private int mProgress;
public SavedState(Parcelable parcel) {
super(parcel);
}
private SavedState(Parcel in) {
super(in);
mProgress = in.readInt();
mIndeterminateProgressMode = in.readInt() == 1;
mConfigurationChanged = in.readInt() == 1;
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(mProgress);
out.writeInt(mIndeterminateProgressMode ? 1 : 0);
out.writeInt(mConfigurationChanged ? 1 : 0);
}
public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
@Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}