summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher3/PreloadIconDrawable.java
diff options
context:
space:
mode:
authorSunny Goyal <sunnygoyal@google.com>2014-08-04 10:53:22 -0700
committerSunny Goyal <sunnygoyal@google.com>2014-08-11 12:32:04 -0700
commit95abbb330ce9bbaf23594245f0f8a795c8118038 (patch)
tree3d72a2655eb05efd10c9d5c5e29cc40d2b40838b /src/com/android/launcher3/PreloadIconDrawable.java
parent6551b7ef07b4d73c096cbfb22002e080b498cbbb (diff)
downloadandroid_packages_apps_Trebuchet-95abbb330ce9bbaf23594245f0f8a795c8118038.tar.gz
android_packages_apps_Trebuchet-95abbb330ce9bbaf23594245f0f8a795c8118038.tar.bz2
android_packages_apps_Trebuchet-95abbb330ce9bbaf23594245f0f8a795c8118038.zip
Updating the virtual preloader UX.
> No click feedback when in preloader mode > No preloader UI when drawn in drag layer > The preloader consists of a background 9 patch image and a circular progress is drawn in the content region of the background. > The preloader is drawn in a slightly larget area than the actual bounds to make the circular progress more prominent compared to the icon. issue: 15835307 Change-Id: Ifec3d93ecf1fac994d1128b517da3797247e7ed6
Diffstat (limited to 'src/com/android/launcher3/PreloadIconDrawable.java')
-rw-r--r--src/com/android/launcher3/PreloadIconDrawable.java173
1 files changed, 127 insertions, 46 deletions
diff --git a/src/com/android/launcher3/PreloadIconDrawable.java b/src/com/android/launcher3/PreloadIconDrawable.java
index d9365cc1f..2972c4f9b 100644
--- a/src/com/android/launcher3/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/PreloadIconDrawable.java
@@ -1,96 +1,143 @@
package com.android.launcher3;
import android.animation.ObjectAnimator;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
+import android.content.res.Resources.Theme;
+import android.content.res.TypedArray;
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;
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 final float MIN_SATUNATION = 0.2f;
+ private static final float MIN_LIGHTNESS = 0.6f;
+
+ private static final float ICON_SCALE_FACTOR = 0.5f;
+ private static final int DEFAULT_COLOR = 0xFF009688;
- private static Bitmap sProgressBg, sProgressFill;
+ private static final Rect sTempRect = new Rect();
- 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);
+ private final RectF mIndicatorRect = new RectF();
+ private boolean mIndicatorRectDirty;
+ private final Paint mPaint;
final Drawable mIcon;
+ private Drawable mBgDrawable;
+ private int mRingOutset;
+
+ private int mIndicatorColor = 0;
+
/**
* 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) {
+ public PreloadIconDrawable(Drawable icon, Theme theme) {
mIcon = icon;
+ mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mPaint.setStyle(Paint.Style.STROKE);
+ mPaint.setStrokeCap(Paint.Cap.ROUND);
+
setBounds(icon.getBounds());
- mPathChanged = false;
+ applyTheme(theme);
+ onLevelChange(0);
+ }
- 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 applyTheme(Theme t) {
+ TypedArray ta = t.obtainStyledAttributes(R.styleable.PreloadIconDrawable);
+ mBgDrawable = ta.getDrawable(R.styleable.PreloadIconDrawable_background);
+ mBgDrawable.setFilterBitmap(true);
+ mPaint.setStrokeWidth(ta.getDimension(R.styleable.PreloadIconDrawable_indicatorSize, 0));
+ mRingOutset = ta.getDimensionPixelSize(R.styleable.PreloadIconDrawable_ringOutset, 0);
+ ta.recycle();
+ onBoundsChange(getBounds());
+ invalidateSelf();
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ mIcon.setBounds(bounds);
+ if (mBgDrawable != null) {
+ sTempRect.set(bounds);
+ sTempRect.inset(-mRingOutset, -mRingOutset);
+ mBgDrawable.setBounds(sTempRect);
}
+ mIndicatorRectDirty = true;
+ }
+
+ public int getOutset() {
+ return mRingOutset;
+ }
+
+ /**
+ * The size of the indicator is same as the content region of the {@link #mBgDrawable} minus
+ * half the stroke size to accommodate the indicator.
+ */
+ private void initIndicatorRect() {
+ Drawable d = mBgDrawable;
+ Rect bounds = d.getBounds();
+
+ d.getPadding(sTempRect);
+ // Amount by which padding has to be scaled
+ float paddingScaleX = ((float) bounds.width()) / d.getIntrinsicWidth();
+ float paddingScaleY = ((float) bounds.height()) / d.getIntrinsicHeight();
+ mIndicatorRect.set(
+ bounds.left + sTempRect.left * paddingScaleX,
+ bounds.top + sTempRect.top * paddingScaleY,
+ bounds.right - sTempRect.right * paddingScaleX,
+ bounds.bottom - sTempRect.bottom * paddingScaleY);
+
+ float inset = mPaint.getStrokeWidth() / 2;
+ mIndicatorRect.inset(inset, inset);
+ mIndicatorRectDirty = false;
}
@Override
public void draw(Canvas canvas) {
- final Rect r = getBounds();
- if (canvas.getClipBounds(mCanvasClipRect) && !Rect.intersects(mCanvasClipRect, r)) {
+ final Rect r = new Rect(getBounds());
+ if (canvas.getClipBounds(sTempRect) && !Rect.intersects(sTempRect, r)) {
// The draw region has been clipped.
return;
}
+ if (mIndicatorRectDirty) {
+ initIndicatorRect();
+ }
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;
+ mBgDrawable.setAlpha(mPaint.getAlpha());
+ mBgDrawable.draw(canvas);
+ canvas.drawOval(mIndicatorRect, 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);
+ mBgDrawable.setAlpha(255);
+ mBgDrawable.draw(canvas);
if (mProgress >= 100) {
- canvas.drawBitmap(sProgressFill, null, r, mPaint);
+ canvas.drawOval(mIndicatorRect, 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();
+ canvas.drawArc(mIndicatorRect, -90, mProgress * 3.6f, false, mPaint);
}
} else {
iconScale = 1;
@@ -103,12 +150,6 @@ class PreloadIconDrawable extends Drawable {
}
@Override
- protected void onBoundsChange(Rect bounds) {
- mIcon.setBounds(bounds);
- mPathChanged = true;
- }
-
- @Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
@@ -126,7 +167,6 @@ class PreloadIconDrawable extends Drawable {
@Override
protected boolean onLevelChange(int level) {
mProgress = level;
- mPathChanged = true;
// Stop Animation
if (mAnimator != null) {
@@ -134,6 +174,14 @@ class PreloadIconDrawable extends Drawable {
mAnimator = null;
}
mAnimationProgress = ANIMATION_PROGRESS_STOPPED;
+ if (level > 0) {
+ // Set the paint color only when the level changes, so that the dominant color
+ // is only calculated when needed.
+ mPaint.setColor(getIndicatorColor());
+ }
+ if (mIcon instanceof FastBitmapDrawable) {
+ ((FastBitmapDrawable) mIcon).setGhostModeEnabled(level <= 0);
+ }
invalidateSelf();
return true;
@@ -165,4 +213,37 @@ class PreloadIconDrawable extends Drawable {
public float getAnimationProgress() {
return mAnimationProgress;
}
+
+ @Override
+ public int getIntrinsicHeight() {
+ return mIcon.getIntrinsicHeight();
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ return mIcon.getIntrinsicWidth();
+ }
+
+ private int getIndicatorColor() {
+ if (mIndicatorColor != 0) {
+ return mIndicatorColor;
+ }
+ if (!(mIcon instanceof FastBitmapDrawable)) {
+ mIndicatorColor = DEFAULT_COLOR;
+ return mIndicatorColor;
+ }
+ mIndicatorColor = Utilities.findDominantColorByHue(
+ ((FastBitmapDrawable) mIcon).getBitmap(), 20);
+
+ // Make sure that the dominant color has enough saturation to be visible properly.
+ float[] hsv = new float[3];
+ Color.colorToHSV(mIndicatorColor, hsv);
+ if (hsv[1] < MIN_SATUNATION) {
+ mIndicatorColor = DEFAULT_COLOR;
+ return mIndicatorColor;
+ }
+ hsv[2] = Math.max(MIN_LIGHTNESS, hsv[2]);
+ mIndicatorColor = Color.HSVToColor(hsv);
+ return mIndicatorColor;
+ }
}