package com.android.launcher3; import android.animation.ObjectAnimator; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; 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; class PreloadIconDrawable extends Drawable { private static final float ANIMATION_PROGRESS_STOPPED = -1.0f; private static final float ANIMATION_PROGRESS_STARTED = 0f; private static final float ANIMATION_PROGRESS_COMPLETED = 1.0f; private static final float ICON_SCALE_FACTOR = 0.6f; private static Bitmap sProgressBg, sProgressFill; private final Rect mCanvasClipRect = new Rect(); private final RectF mRect = new RectF(); private final Path mProgressPath = new Path(); private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG); final Drawable mIcon; /** * Indicates the progress of the preloader [0-100]. If it goes above 100, only the icon * is shown with no progress bar. */ private int mProgress = 0; private boolean mPathChanged; private float mAnimationProgress = ANIMATION_PROGRESS_STOPPED; private ObjectAnimator mAnimator; public PreloadIconDrawable(Drawable icon, Resources res) { mIcon = icon; setBounds(icon.getBounds()); mPathChanged = false; if (sProgressBg == null) { sProgressBg = BitmapFactory.decodeResource(res, R.drawable.bg_preloader); } if (sProgressFill == null) { sProgressFill = BitmapFactory.decodeResource(res, R.drawable.bg_preloader_progress); } } @Override public void draw(Canvas canvas) { final Rect r = getBounds(); if (canvas.getClipBounds(mCanvasClipRect) && !Rect.intersects(mCanvasClipRect, r)) { // The draw region has been clipped. return; } final float iconScale; if ((mAnimationProgress >= ANIMATION_PROGRESS_STARTED) && (mAnimationProgress < ANIMATION_PROGRESS_COMPLETED)) { mPaint.setAlpha((int) ((1 - mAnimationProgress) * 255)); canvas.drawBitmap(sProgressBg, null, r, mPaint); canvas.drawBitmap(sProgressFill, null, r, mPaint); iconScale = ICON_SCALE_FACTOR + (1 - ICON_SCALE_FACTOR) * mAnimationProgress; } else if (mAnimationProgress == ANIMATION_PROGRESS_STOPPED) { mPaint.setAlpha(255); iconScale = ICON_SCALE_FACTOR; canvas.drawBitmap(sProgressBg, null, r, mPaint); if (mProgress >= 100) { canvas.drawBitmap(sProgressFill, null, r, mPaint); } else if (mProgress > 0) { if (mPathChanged) { mProgressPath.reset(); mProgressPath.moveTo(r.exactCenterX(), r.centerY()); mRect.set(r); mProgressPath.arcTo(mRect, -90, mProgress * 3.6f); mProgressPath.close(); mPathChanged = false; } canvas.save(); canvas.clipPath(mProgressPath); canvas.drawBitmap(sProgressFill, null, r, mPaint); canvas.restore(); } } else { iconScale = 1; } canvas.save(); canvas.scale(iconScale, iconScale, r.exactCenterX(), r.exactCenterY()); mIcon.draw(canvas); canvas.restore(); } @Override protected void onBoundsChange(Rect bounds) { mIcon.setBounds(bounds); mPathChanged = true; } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } @Override public void setAlpha(int alpha) { mIcon.setAlpha(alpha); } @Override public void setColorFilter(ColorFilter cf) { mIcon.setColorFilter(cf); } @Override protected boolean onLevelChange(int level) { mProgress = level; mPathChanged = true; // Stop Animation if (mAnimator != null) { mAnimator.cancel(); mAnimator = null; } mAnimationProgress = ANIMATION_PROGRESS_STOPPED; invalidateSelf(); return true; } /** * Runs the finish animation if it is has not been run after last level change. */ public void maybePerformFinishedAnimation() { if (mAnimationProgress > ANIMATION_PROGRESS_STOPPED) { return; } if (mAnimator != null) { mAnimator.cancel(); } setAnimationProgress(ANIMATION_PROGRESS_STARTED); mAnimator = ObjectAnimator.ofFloat(this, "animationProgress", ANIMATION_PROGRESS_STARTED, ANIMATION_PROGRESS_COMPLETED); mAnimator.start(); } public void setAnimationProgress(float progress) { if (progress != mAnimationProgress) { mAnimationProgress = progress; invalidateSelf(); } } public float getAnimationProgress() { return mAnimationProgress; } }