package carbon.widget;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.View;
import carbon.Carbon;
import carbon.R;
import carbon.animation.AnimatedColorStateList;
import carbon.animation.AnimatedView;
import carbon.drawable.CircularProgressDrawable;
import carbon.drawable.DefaultPrimaryColorStateList;
import carbon.drawable.ProgressBarDrawable;
import carbon.drawable.ProgressDrawable;
public class ProgressBar extends View implements AnimatedView, TintedView, VisibleView {
private ProgressDrawable drawable;
public enum Style {
BarDeterminate, BarIndeterminate, BarQuery, CircularDeterminate, CircularIndeterminate
}
public ProgressBar(Context context) {
super(context);
initProgressBar(null, android.R.attr.progressBarStyle);
}
public ProgressBar(Context context, AttributeSet attrs) {
super(Carbon.getThemedContext(context, attrs, R.styleable.ProgressBar, android.R.attr.progressBarStyle, R.styleable.ProgressBar_carbon_theme), attrs);
initProgressBar(attrs, android.R.attr.progressBarStyle);
}
public ProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(Carbon.getThemedContext(context, attrs, R.styleable.ProgressBar, defStyleAttr, R.styleable.ProgressBar_carbon_theme), attrs, defStyleAttr);
initProgressBar(attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public ProgressBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(Carbon.getThemedContext(context, attrs, R.styleable.ProgressBar, defStyleAttr, R.styleable.ProgressBar_carbon_theme), attrs, defStyleAttr, defStyleRes);
initProgressBar(attrs, defStyleAttr);
}
private static int[] animationIds = new int[]{
R.styleable.ProgressBar_carbon_inAnimation,
R.styleable.ProgressBar_carbon_outAnimation
};
private static int[] tintIds = new int[]{
R.styleable.ProgressBar_carbon_tint,
R.styleable.ProgressBar_carbon_tintMode,
R.styleable.ProgressBar_carbon_backgroundTint,
R.styleable.ProgressBar_carbon_backgroundTintMode,
R.styleable.ProgressBar_carbon_animateColorChanges
};
private void initProgressBar(AttributeSet attrs, int defStyleAttr) {
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ProgressBar, defStyleAttr, R.style.carbon_ProgressBar);
Style style = Style.values()[a.getInt(R.styleable.ProgressBar_carbon_progressStyle, 0)];
if (style == Style.BarDeterminate || style == Style.BarIndeterminate || style == Style.BarQuery) {
setDrawable(new ProgressBarDrawable());
} else {
setDrawable(new CircularProgressDrawable());
}
drawable.setStyle(style);
drawable.setBarWidth(a.getDimension(R.styleable.ProgressBar_carbon_barWidth, 5));
Carbon.initTint(this, a, tintIds);
Carbon.initAnimations(this, a, animationIds);
a.recycle();
if (getVisibility() == VISIBLE) {
setBarWidth(getBarWidth() + getBarPadding());
setBarPadding(0);
} else {
setBarPadding(getBarWidth() + getBarPadding());
setBarWidth(0);
}
}
public void setProgress(float progress) {
drawable.setProgress(progress);
}
public float getProgress() {
return drawable.getProgress();
}
public float getBarWidth() {
return drawable.getBarWidth();
}
public void setBarWidth(float arcWidth) {
drawable.setBarWidth(arcWidth);
}
public void setBarPadding(float padding) {
drawable.setBarPadding(padding);
}
public float getBarPadding() {
return drawable.getBarPadding();
}
public void setDrawable(ProgressDrawable newDrawable) {
this.drawable = newDrawable;
if (drawable != null)
drawable.setCallback(null);
if (newDrawable != null)
newDrawable.setCallback(this);
}
public ProgressDrawable getDrawable() {
return drawable;
}
@Override
protected boolean verifyDrawable(@NonNull Drawable who) {
return super.verifyDrawable(who) || who == drawable;
}
@Override
public void draw(@NonNull Canvas canvas) {
super.draw(canvas);
if (drawable != null)
drawable.draw(canvas);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (!changed)
return;
if (getWidth() == 0 || getHeight() == 0)
return;
if (drawable != null)
drawable.setBounds(0, 0, getWidth(), getHeight());
}
// -------------------------------
// animations
// -------------------------------
private Animator inAnim = null, outAnim = null;
private Animator animator;
public Animator animateVisibility(final int visibility) {
if (visibility == View.VISIBLE && (getVisibility() != View.VISIBLE || animator != null)) {
if (animator != null)
animator.cancel();
if (inAnim != null) {
animator = inAnim;
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator a) {
animator.removeListener(this);
animator = null;
}
@Override
public void onAnimationCancel(Animator animation) {
animator.removeListener(this);
animator = null;
}
});
animator.start();
}
setVisibility(visibility);
} else if (visibility != View.VISIBLE && (getVisibility() == View.VISIBLE || animator != null)) {
if (animator != null)
animator.cancel();
if (outAnim == null) {
setVisibility(visibility);
return null;
}
animator = outAnim;
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator a) {
if (((ValueAnimator) a).getAnimatedFraction() == 1)
setVisibility(visibility);
animator.removeListener(this);
animator = null;
}
@Override
public void onAnimationCancel(Animator animation) {
animator.removeListener(this);
animator = null;
}
});
animator.start();
}
return animator;
}
public Animator getAnimator() {
return animator;
}
public Animator getOutAnimator() {
return outAnim;
}
public void setOutAnimator(Animator outAnim) {
if (this.outAnim != null)
this.outAnim.setTarget(null);
this.outAnim = outAnim;
if (outAnim != null)
outAnim.setTarget(this);
}
public Animator getInAnimator() {
return inAnim;
}
public void setInAnimator(Animator inAnim) {
if (this.inAnim != null)
this.inAnim.setTarget(null);
this.inAnim = inAnim;
if (inAnim != null)
inAnim.setTarget(this);
}
// -------------------------------
// tint
// -------------------------------
ColorStateList tint;
PorterDuff.Mode tintMode;
ColorStateList backgroundTint;
PorterDuff.Mode backgroundTintMode;
boolean animateColorChanges;
ValueAnimator.AnimatorUpdateListener tintAnimatorListener = new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
postInvalidate();
ViewCompat.postInvalidateOnAnimation(ProgressBar.this);
}
};
ValueAnimator.AnimatorUpdateListener backgroundTintAnimatorListener = new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
postInvalidate();
ViewCompat.postInvalidateOnAnimation(ProgressBar.this);
}
};
@Override
public void setTint(ColorStateList list) {
this.tint = animateColorChanges && !(list instanceof AnimatedColorStateList) ? AnimatedColorStateList.fromList(list, tintAnimatorListener) : list;
updateTint();
}
@Override
public void setTint(int color) {
if (color == 0) {
setTint(new DefaultPrimaryColorStateList(getContext()));
} else {
setTint(ColorStateList.valueOf(color));
}
}
@Override
public ColorStateList getTint() {
return tint;
}
private void updateTint() {
if (tint != null && tintMode != null) {
int color = tint.getColorForState(getDrawableState(), tint.getDefaultColor());
if (drawable != null) {
drawable.setTint(color);
drawable.setTintMode(tintMode);
}
} else {
if (drawable != null) {
drawable.setTint(null);
}
}
}
@Override
public void setTintMode(@NonNull PorterDuff.Mode mode) {
this.tintMode = mode;
updateTint();
}
@Override
public PorterDuff.Mode getTintMode() {
return tintMode;
}
@Override
public void setBackgroundTint(ColorStateList list) {
this.backgroundTint = animateColorChanges && !(list instanceof AnimatedColorStateList) ? AnimatedColorStateList.fromList(list, backgroundTintAnimatorListener) : list;
updateBackgroundTint();
}
@Override
public void setBackgroundTint(int color) {
if (color == 0) {
setBackgroundTint(new DefaultPrimaryColorStateList(getContext()));
} else {
setBackgroundTint(ColorStateList.valueOf(color));
}
}
@Override
public ColorStateList getBackgroundTint() {
return backgroundTint;
}
private void updateBackgroundTint() {
if (getBackground() == null)
return;
if (backgroundTint != null && backgroundTintMode != null) {
int color = backgroundTint.getColorForState(getDrawableState(), backgroundTint.getDefaultColor());
getBackground().setColorFilter(new PorterDuffColorFilter(color, tintMode));
} else {
getBackground().setColorFilter(null);
}
}
@Override
public void setBackgroundTintMode(PorterDuff.Mode mode) {
this.backgroundTintMode = mode;
updateBackgroundTint();
}
@Override
public PorterDuff.Mode getBackgroundTintMode() {
return backgroundTintMode;
}
public boolean isAnimateColorChangesEnabled() {
return animateColorChanges;
}
public void setAnimateColorChangesEnabled(boolean animateColorChanges) {
this.animateColorChanges = animateColorChanges;
if (tint != null && !(tint instanceof AnimatedColorStateList))
setTint(AnimatedColorStateList.fromList(tint, tintAnimatorListener));
if (backgroundTint != null && !(backgroundTint instanceof AnimatedColorStateList))
setBackgroundTint(AnimatedColorStateList.fromList(backgroundTint, backgroundTintAnimatorListener));
}
}