package com.mercandalli.android.apps.files.common.view; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.os.Build; import android.support.annotation.Nullable; import android.util.Property; import com.mercandalli.android.apps.files.R; /** * Created by Jonathan on 30/05/2015. */ public class PlayPauseDrawable extends Drawable { private final Path mLeftPauseBar = new Path(); private final Path mRightPauseBar = new Path(); private final Paint mPaint = new Paint(); private final RectF mBounds = new RectF(); private final float mPauseBarWidth; private final float mPauseBarHeight; private final float mPauseBarDistance; private float mWidth; private float mHeight; private float mProgress; private boolean mIsPlay; public PlayPauseDrawable(Context context) { final Resources res = context.getResources(); mPaint.setAntiAlias(true); mPaint.setStyle(Paint.Style.FILL); mPaint.setColor(Color.WHITE); mPauseBarWidth = res.getDimensionPixelSize(R.dimen.pause_bar_width); mPauseBarHeight = res.getDimensionPixelSize(R.dimen.pause_bar_height); mPauseBarDistance = res.getDimensionPixelSize(R.dimen.pause_bar_distance); } @Override protected void onBoundsChange(Rect bounds) { super.onBoundsChange(bounds); mBounds.set(bounds); mWidth = mBounds.width(); mHeight = mBounds.height(); } @Override public void draw(Canvas canvas) { mLeftPauseBar.rewind(); mRightPauseBar.rewind(); // The current distance between the two pause bars. final float barDist = lerp(mPauseBarDistance, 0, mProgress); // The current width of each pause bar. final float barWidth = lerp(mPauseBarWidth, mPauseBarHeight / 2f, mProgress); // The current position of the left pause bar's top left coordinate. final float firstBarTopLeft = lerp(0, barWidth, mProgress); // The current position of the right pause bar's top right coordinate. final float secondBarTopRight = lerp(2 * barWidth + barDist, barWidth + barDist, mProgress); // Draw the left pause bar. The left pause bar transforms into the // top half of the play button triangle by animating the position of the // rectangle's top left coordinate and expanding its bottom width. mLeftPauseBar.moveTo(0, 0); mLeftPauseBar.lineTo(firstBarTopLeft, -mPauseBarHeight); mLeftPauseBar.lineTo(barWidth, -mPauseBarHeight); mLeftPauseBar.lineTo(barWidth, 0); mLeftPauseBar.close(); // Draw the right pause bar. The right pause bar transforms into the // bottom half of the play button triangle by animating the position of the // rectangle's top right coordinate and expanding its bottom width. mRightPauseBar.moveTo(barWidth + barDist, 0); mRightPauseBar.lineTo(barWidth + barDist, -mPauseBarHeight); mRightPauseBar.lineTo(secondBarTopRight, -mPauseBarHeight); mRightPauseBar.lineTo(2 * barWidth + barDist, 0); mRightPauseBar.close(); canvas.save(); // Translate the play button a tiny bit to the right so it looks more centered. canvas.translate(lerp(0, mPauseBarHeight / 8f, mProgress), 0); // (1) Pause --> Play: rotate 0 to 90 degrees clockwise. // (2) Play --> Pause: rotate 90 to 180 degrees clockwise. final float rotationProgress = mIsPlay ? 1 - mProgress : mProgress; final float startingRotation = mIsPlay ? 90 : 0; canvas.rotate(lerp(startingRotation, startingRotation + 90, rotationProgress), mWidth / 2f, mHeight / 2f); // Position the pause/play button in the center of the drawable's bounds. canvas.translate(mWidth / 2f - ((2 * barWidth + barDist) / 2f), mHeight / 2f + (mPauseBarHeight / 2f)); // Draw the two bars that form the animated pause/play button. canvas.drawPath(mLeftPauseBar, mPaint); canvas.drawPath(mRightPauseBar, mPaint); canvas.restore(); } @Nullable public Animator getPausePlayAnimator() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { return null; } final Property<PlayPauseDrawable, Float> PROGRESS = new Property<PlayPauseDrawable, Float>(Float.class, "progress") { @Override public Float get(PlayPauseDrawable d) { return d.getProgress(); } @Override public void set(PlayPauseDrawable d, Float value) { d.setProgress(value); } }; final Animator anim = ObjectAnimator.ofFloat(this, PROGRESS, mIsPlay ? 1 : 0, mIsPlay ? 0 : 1); anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { mIsPlay = !mIsPlay; } }); return anim; } public boolean isPlay() { return mIsPlay; } public boolean isAnimationPlaying() { return mProgress > 0 && mProgress < 1; } private void setProgress(float progress) { mProgress = progress; invalidateSelf(); } private float getProgress() { return mProgress; } @Override public void setAlpha(int alpha) { mPaint.setAlpha(alpha); invalidateSelf(); } @Override public void setColorFilter(ColorFilter cf) { mPaint.setColorFilter(cf); invalidateSelf(); } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } /** * Linear interpolate between a and b with parameter t. */ private static float lerp(float a, float b, float t) { return a + (b - a) * t; } }