package com.marshalchen.common.uimodule.googleprogressbar;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Interpolator;
/**
* Dices 'I'm feeling lucky' progress of Google Play Music app.
*
* @author Oleksandr Melnykov
*/
public class GoogleMusicDicesDrawable extends Drawable implements Drawable.Callback {
private static final int DICE_SIDE_COLOR = Color.parseColor("#FFDBDBDB");
private static final int DICE_SIDE_SHADOW_COLOR = Color.parseColor("#FFB8B8B9");
private static final int ANIMATION_DURATION = 350;
private static final int ANIMATION_START_DELAY = 150;
private static final Interpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator();
private Paint mPaint;
private Paint mPaintShadow;
private Paint mPaintCircle;
private int mSize;
private float mScale;
private DiceRotation mDiceRotation;
private DiceState[] mDiceStates;
private int mDiceState;
private enum DiceSide {
ONE,
TWO,
THREE,
FOUR,
FIVE,
SIX
}
private enum DiceRotation {
LEFT,
DOWN;
DiceRotation invert() {
return this == LEFT ? DOWN : LEFT;
}
}
private class DiceState {
private DiceSide side1;
private DiceSide side2;
DiceState(DiceSide side1, DiceSide side2) {
this.side1 = side1;
this.side2 = side2;
}
}
public GoogleMusicDicesDrawable() {
init();
}
private void init() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(DICE_SIDE_COLOR);
mPaintShadow = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaintShadow.setColor(DICE_SIDE_SHADOW_COLOR);
mPaintCircle = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaintCircle.setColor(Color.WHITE);
mDiceStates = new DiceState[] {
new DiceState(DiceSide.ONE, DiceSide.THREE),
new DiceState(DiceSide.TWO, DiceSide.THREE),
new DiceState(DiceSide.TWO, DiceSide.SIX),
new DiceState(DiceSide.FOUR, DiceSide.SIX),
new DiceState(DiceSide.FOUR, DiceSide.FIVE),
new DiceState(DiceSide.ONE, DiceSide.FIVE)
};
mDiceRotation = DiceRotation.LEFT;
initObjectAnimator();
}
private void initObjectAnimator() {
final ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(this, "scale", 0, 1);
objectAnimator.setInterpolator(ACCELERATE_INTERPOLATOR);
objectAnimator.setDuration(ANIMATION_DURATION);
objectAnimator.setStartDelay(ANIMATION_START_DELAY);
objectAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mScale = 0;
mDiceState++;
if (mDiceState == mDiceStates.length) {
mDiceState = 0;
}
mDiceRotation = mDiceRotation.invert();
objectAnimator.start();
}
});
objectAnimator.start();
}
@Override
public void draw(Canvas canvas) {
if (mDiceRotation != null) {
switch (mDiceRotation) {
case DOWN:
drawScaleY(canvas);
break;
case LEFT:
drawScaleX(canvas);
break;
}
}
}
@Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
mPaintShadow.setAlpha(alpha);
mPaintCircle.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
mPaintShadow.setColorFilter(cf);
mPaintCircle.setColorFilter(cf);
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
mSize = bounds.width();
}
@Override
public void invalidateDrawable(Drawable who) {
final Callback callback = getCallback();
if (callback != null) {
callback.invalidateDrawable(this);
}
}
@Override
public void scheduleDrawable(Drawable who, Runnable what, long when) {
final Callback callback = getCallback();
if (callback != null) {
callback.scheduleDrawable(this, what, when);
}
}
@Override
public void unscheduleDrawable(Drawable who, Runnable what) {
final Callback callback = getCallback();
if (callback != null) {
callback.unscheduleDrawable(this, what);
}
}
private void drawScaleX(Canvas canvas) {
canvas.save();
Matrix matrix = new Matrix();
matrix.preScale(1 - mScale, 1, 0, mSize / 2);
canvas.concat(matrix);
drawDiceSide(canvas, mDiceStates[mDiceState].side1, mScale > 0.1f);
canvas.restore();
canvas.save();
matrix = new Matrix();
matrix.preScale(mScale, 1, mSize, mSize / 2);
canvas.concat(matrix);
drawDiceSide(canvas, mDiceStates[mDiceState].side2, false);
canvas.restore();
}
private void drawScaleY(Canvas canvas) {
canvas.save();
Matrix matrix = new Matrix();
matrix.preScale(1, mScale, mSize / 2, 0);
canvas.concat(matrix);
drawDiceSide(canvas, mDiceStates[mDiceState].side1, false);
canvas.restore();
canvas.save();
matrix = new Matrix();
matrix.preScale(1, 1 - mScale, mSize / 2, mSize);
canvas.concat(matrix);
drawDiceSide(canvas, mDiceStates[mDiceState].side2, mScale > 0.1f);
canvas.restore();
}
private void drawDiceSide(Canvas canvas, DiceSide side, boolean shadow) {
int circleRadius = mSize / 10;
canvas.drawRect(0, 0, mSize, mSize, shadow ? mPaintShadow : mPaint);
switch (side) {
case ONE:
canvas.drawCircle(mSize / 2, mSize / 2, circleRadius, mPaintCircle);
break;
case TWO:
canvas.drawCircle(mSize / 4, mSize - mSize / 4, circleRadius, mPaintCircle);
canvas.drawCircle(mSize - mSize / 4, mSize / 4, circleRadius, mPaintCircle);
break;
case THREE:
canvas.drawCircle(mSize / 2, mSize / 2, circleRadius, mPaintCircle);
canvas.drawCircle(mSize / 4, mSize / 4, circleRadius, mPaintCircle);
canvas.drawCircle(mSize - mSize / 4, mSize - mSize / 4, mSize / 10, mPaintCircle);
break;
case FOUR:
canvas.drawCircle(mSize / 4, mSize / 4, circleRadius, mPaintCircle);
canvas.drawCircle(mSize / 4, mSize - mSize / 4, circleRadius, mPaintCircle);
canvas.drawCircle(mSize - mSize / 4, mSize - mSize / 4, circleRadius, mPaintCircle);
canvas.drawCircle(mSize - mSize / 4, mSize / 4, circleRadius, mPaintCircle);
break;
case FIVE:
canvas.drawCircle(mSize / 2, mSize / 2, circleRadius, mPaintCircle);
canvas.drawCircle(mSize / 4, mSize / 4, circleRadius, mPaintCircle);
canvas.drawCircle(mSize / 4, mSize - mSize / 4, circleRadius, mPaintCircle);
canvas.drawCircle(mSize - mSize / 4, mSize - mSize / 4, circleRadius, mPaintCircle);
canvas.drawCircle(mSize - mSize / 4, mSize / 4, circleRadius, mPaintCircle);
break;
case SIX:
canvas.drawCircle(mSize / 4, mSize / 4, circleRadius, mPaintCircle);
canvas.drawCircle(mSize / 4, mSize / 2, circleRadius, mPaintCircle);
canvas.drawCircle(mSize / 4, mSize - mSize / 4, circleRadius, mPaintCircle);
canvas.drawCircle(mSize - mSize / 4, mSize / 4, circleRadius, mPaintCircle);
canvas.drawCircle(mSize - mSize / 4, mSize / 2, circleRadius, mPaintCircle);
canvas.drawCircle(mSize - mSize / 4, mSize - mSize / 4, circleRadius, mPaintCircle);
break;
}
}
float getScale() {
return mScale;
}
void setScale(float scale) {
this.mScale = scale;
}
public static class Builder {
public Drawable build() {
return new GoogleMusicDicesDrawable();
}
}
}