summaryrefslogtreecommitdiffstats
path: root/src/com/android
diff options
context:
space:
mode:
authorSunny Goyal <sunnygoyal@google.com>2017-02-02 16:37:21 -0800
committerSunny Goyal <sunnygoyal@google.com>2017-02-06 10:43:27 -0800
commit96ac68a481ce5b794b5227e09ace7c30d6dd5e7b (patch)
tree9ea46cdeccfce02fbe0830d7430f9f95c64557b6 /src/com/android
parente1fa0145d36fbefeb397f952cba2689ce537d786 (diff)
downloadandroid_packages_apps_Trebuchet-96ac68a481ce5b794b5227e09ace7c30d6dd5e7b.tar.gz
android_packages_apps_Trebuchet-96ac68a481ce5b794b5227e09ace7c30d6dd5e7b.tar.bz2
android_packages_apps_Trebuchet-96ac68a481ce5b794b5227e09ace7c30d6dd5e7b.zip
Updating the PreloadIconDrawable
> The drawable gets the path from MaskIconDrawable path, instead of using a circle > The progress changes are animated as well Bug: 34831873 Change-Id: I4e7f0b610f4fd94de8e0cfcf8b179b775cf0b4d8
Diffstat (limited to 'src/com/android')
-rw-r--r--src/com/android/launcher3/BubbleTextView.java26
-rw-r--r--src/com/android/launcher3/FastBitmapDrawable.java2
-rw-r--r--src/com/android/launcher3/PendingAppWidgetHostView.java19
-rw-r--r--src/com/android/launcher3/PreloadIconDrawable.java252
-rw-r--r--src/com/android/launcher3/Workspace.java1
-rw-r--r--src/com/android/launcher3/folder/FolderIcon.java13
-rw-r--r--src/com/android/launcher3/graphics/DragPreviewProvider.java5
-rw-r--r--src/com/android/launcher3/graphics/DrawableFactory.java41
-rw-r--r--src/com/android/launcher3/graphics/FixedScaleDrawable.java41
-rw-r--r--src/com/android/launcher3/graphics/IconPalette.java42
-rw-r--r--src/com/android/launcher3/graphics/LauncherIcons.java20
-rw-r--r--src/com/android/launcher3/graphics/PreloadIconDrawable.java289
12 files changed, 421 insertions, 330 deletions
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 2efe31fa0..8043eacdb 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -19,7 +19,6 @@ package com.android.launcher3;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
-import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -27,7 +26,6 @@ import android.graphics.Paint;
import android.graphics.Region;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
-import android.util.SparseArray;
import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -45,6 +43,7 @@ import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.graphics.HolographicOutlineHelper;
import com.android.launcher3.graphics.IconPalette;
+import com.android.launcher3.graphics.PreloadIconDrawable;
import com.android.launcher3.model.PackageItemInfo;
import java.text.NumberFormat;
@@ -57,8 +56,6 @@ import java.text.NumberFormat;
public class BubbleTextView extends TextView
implements BaseRecyclerViewFastScrollBar.FastScrollFocusableView, ItemInfoUpdateReceiver {
- private static SparseArray<Theme> sPreloaderThemes = new SparseArray<Theme>(2);
-
// Dimensions in DP
private static final float AMBIENT_SHADOW_RADIUS = 2.5f;
private static final float KEY_SHADOW_RADIUS = 1f;
@@ -423,10 +420,6 @@ public class BubbleTextView extends TextView
super.onAttachedToWindow();
if (mBackground != null) mBackground.setCallback(this);
-
- if (mIcon instanceof PreloadIconDrawable) {
- ((PreloadIconDrawable) mIcon).applyPreloaderTheme(getPreloaderTheme());
- }
mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
@@ -495,7 +488,8 @@ public class BubbleTextView extends TextView
if (mIcon instanceof PreloadIconDrawable) {
preloadDrawable = (PreloadIconDrawable) mIcon;
} else {
- preloadDrawable = new PreloadIconDrawable(mIcon, getPreloaderTheme());
+ preloadDrawable = DrawableFactory.get(getContext())
+ .newPendingIcon(info.iconBitmap, getContext());
setIcon(preloadDrawable);
}
@@ -520,20 +514,6 @@ public class BubbleTextView extends TextView
: null;
}
- private Theme getPreloaderTheme() {
- Object tag = getTag();
- int style = ((tag != null) && (tag instanceof ShortcutInfo) &&
- (((ShortcutInfo) tag).container >= 0)) ? R.style.PreloadIcon_Folder
- : R.style.PreloadIcon;
- Theme theme = sPreloaderThemes.get(style);
- if (theme == null) {
- theme = getResources().newTheme();
- theme.applyStyle(style, true);
- sPreloaderThemes.put(style, theme);
- }
- return theme;
- }
-
/**
* Sets the icon for this view based on the layout direction.
*/
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index df195471b..95d2dafce 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -120,7 +120,7 @@ public class FastBitmapDrawable extends Drawable {
public FastBitmapDrawable(Bitmap b) {
mBitmap = b;
- setBounds(0, 0, b.getWidth(), b.getHeight());
+ setFilterBitmap(true);
}
public void applyIconBadge(BadgeInfo badgeInfo, BadgeRenderer badgeRenderer) {
diff --git a/src/com/android/launcher3/PendingAppWidgetHostView.java b/src/com/android/launcher3/PendingAppWidgetHostView.java
index 3256df6fe..354cf2065 100644
--- a/src/com/android/launcher3/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/PendingAppWidgetHostView.java
@@ -17,7 +17,6 @@
package com.android.launcher3;
import android.content.Context;
-import android.content.res.Resources.Theme;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -33,8 +32,8 @@ import android.view.ContextThemeWrapper;
import android.view.View;
import android.view.View.OnClickListener;
-import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.IconCache.ItemInfoUpdateReceiver;
+import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.model.PackageItemInfo;
public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
@@ -42,8 +41,6 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
private static final float SETUP_ICON_SIZE_FACTOR = 2f / 5;
private static final float MIN_SATUNATION = 0.7f;
- private static Theme sPreloaderTheme;
-
private final Rect mRect = new Rect();
private View mDefaultView;
private OnClickListener mClickListener;
@@ -149,13 +146,8 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
updateSettingColor();
} else {
- if (sPreloaderTheme == null) {
- sPreloaderTheme = getResources().newTheme();
- sPreloaderTheme.applyStyle(R.style.PreloadIcon, true);
- }
-
- FastBitmapDrawable drawable = drawableFactory.newIcon(mIcon, mInfo);
- mCenterDrawable = new PreloadIconDrawable(drawable, sPreloaderTheme);
+ mCenterDrawable = DrawableFactory.get(getContext())
+ .newPendingIcon(mIcon, getContext());
mCenterDrawable.setCallback(this);
mSettingIconDrawable = null;
applyState();
@@ -226,13 +218,10 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
int availableHeight = getHeight() - paddingTop - paddingBottom - 2 * minPadding;
if (mSettingIconDrawable == null) {
- int outset = (mCenterDrawable instanceof PreloadIconDrawable) ?
- ((PreloadIconDrawable) mCenterDrawable).getOutset() : 0;
- int maxSize = grid.iconSizePx + 2 * outset;
+ int maxSize = grid.iconSizePx;
int size = Math.min(maxSize, Math.min(availableWidth, availableHeight));
mRect.set(0, 0, size, size);
- mRect.inset(outset, outset);
mRect.offsetTo((getWidth() - mRect.width()) / 2, (getHeight() - mRect.height()) / 2);
mCenterDrawable.setBounds(mRect);
} else {
diff --git a/src/com/android/launcher3/PreloadIconDrawable.java b/src/com/android/launcher3/PreloadIconDrawable.java
deleted file mode 100644
index 973e688d9..000000000
--- a/src/com/android/launcher3/PreloadIconDrawable.java
+++ /dev/null
@@ -1,252 +0,0 @@
-package com.android.launcher3;
-
-import android.animation.ObjectAnimator;
-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.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
-
-public 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 MIN_SATURATION = 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 final Rect sTempRect = new Rect();
-
- private final RectF mIndicatorRect = new RectF();
- private boolean mIndicatorRectDirty;
-
- private final Paint mPaint;
- public 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 float mAnimationProgress = ANIMATION_PROGRESS_STOPPED;
- private ObjectAnimator mAnimator;
-
- 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());
- applyPreloaderTheme(theme);
- onLevelChange(0);
- }
-
- public void applyPreloaderTheme(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 = 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));
- 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;
- mBgDrawable.setAlpha(255);
- mBgDrawable.draw(canvas);
-
- if (mProgress >= 100) {
- canvas.drawOval(mIndicatorRect, mPaint);
- } else if (mProgress > 0) {
- canvas.drawArc(mIndicatorRect, -90, mProgress * 3.6f, false, mPaint);
- }
- } else {
- iconScale = 1;
- }
-
- canvas.save();
- canvas.scale(iconScale, iconScale, r.exactCenterX(), r.exactCenterY());
- mIcon.draw(canvas);
- canvas.restore();
- }
-
- @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;
-
- // Stop Animation
- if (mAnimator != null) {
- mAnimator.cancel();
- 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).setIsDisabled(level < 100);
- }
-
- 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;
- }
-
- public boolean hasNotCompleted() {
- return mAnimationProgress < ANIMATION_PROGRESS_COMPLETED;
- }
-
- @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_SATURATION) {
- mIndicatorColor = DEFAULT_COLOR;
- return mIndicatorColor;
- }
- hsv[2] = Math.max(MIN_LIGHTNESS, hsv[2]);
- mIndicatorColor = Color.HSVToColor(hsv);
- return mIndicatorColor;
- }
-}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 981941892..c80d4a883 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -72,6 +72,7 @@ import com.android.launcher3.dragndrop.SpringLoadedDragController;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.graphics.DragPreviewProvider;
+import com.android.launcher3.graphics.PreloadIconDrawable;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 973245b92..5dc963311 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -53,13 +53,11 @@ import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.FolderInfo;
import com.android.launcher3.FolderInfo.FolderListener;
-import com.android.launcher3.IconCache;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.OnAlarmListener;
-import com.android.launcher3.PreloadIconDrawable;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.SimpleOnStylusPressListener;
@@ -245,7 +243,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
};
public Drawable prepareCreate(final View destView) {
- Drawable animateDrawable = getTopDrawable((TextView) destView);
+ Drawable animateDrawable = ((TextView) destView).getCompoundDrawables()[1];
computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(),
destView.getMeasuredWidth());
return animateDrawable;
@@ -270,7 +268,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
}
public void performDestroyAnimation(final View finalView, Runnable onCompleteRunnable) {
- Drawable animateDrawable = getTopDrawable((TextView) finalView);
+ Drawable animateDrawable = ((TextView) finalView).getCompoundDrawables()[1];
computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(),
finalView.getMeasuredWidth());
@@ -771,11 +769,6 @@ public class FolderIcon extends FrameLayout implements FolderListener {
}
}
- private Drawable getTopDrawable(TextView v) {
- Drawable d = v.getCompoundDrawables()[1];
- return (d instanceof PreloadIconDrawable) ? ((PreloadIconDrawable) d).mIcon : d;
- }
-
class FolderPreviewItemAnim {
ValueAnimator mValueAnimator;
float finalScale;
@@ -892,7 +885,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
for (int i = 0; i < mDrawingParams.size(); i++) {
PreviewItemDrawingParams p = mDrawingParams.get(i);
- p.drawable = getTopDrawable((TextView) items.get(i));
+ p.drawable = ((TextView) items.get(i)).getCompoundDrawables()[1];
if (!animate || FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON) {
computePreviewItemDrawingParams(i, nItemsInPreview, p);
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index 1a470ff11..bb136f7a3 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -27,7 +27,6 @@ import android.widget.TextView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppWidgetHostView;
-import com.android.launcher3.PreloadIconDrawable;
import com.android.launcher3.R;
import com.android.launcher3.Workspace;
import com.android.launcher3.config.ProviderConfig;
@@ -185,10 +184,6 @@ public class DragPreviewProvider {
} else {
bounds.offsetTo(0, 0);
}
- if (d instanceof PreloadIconDrawable) {
- int inset = -((PreloadIconDrawable) d).getOutset();
- bounds.inset(inset, inset);
- }
return bounds;
}
diff --git a/src/com/android/launcher3/graphics/DrawableFactory.java b/src/com/android/launcher3/graphics/DrawableFactory.java
index 4d4d5087f..249344792 100644
--- a/src/com/android/launcher3/graphics/DrawableFactory.java
+++ b/src/com/android/launcher3/graphics/DrawableFactory.java
@@ -21,12 +21,14 @@ import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Process;
import android.os.UserHandle;
import android.support.annotation.UiThread;
+import android.util.Log;
import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.ItemInfo;
@@ -41,9 +43,13 @@ import java.util.HashMap;
*/
public class DrawableFactory {
+ private static final String TAG = "DrawableFactory";
+
private static DrawableFactory sInstance;
private static final Object LOCK = new Object();
+ private Path mPreloadProgressPath;
+
public static DrawableFactory get(Context context) {
synchronized (LOCK) {
if (sInstance == null) {
@@ -61,9 +67,38 @@ public class DrawableFactory {
* Returns a FastBitmapDrawable with the icon.
*/
public FastBitmapDrawable newIcon(Bitmap icon, ItemInfo info) {
- FastBitmapDrawable d = new FastBitmapDrawable(icon);
- d.setFilterBitmap(true);
- return d;
+ return new FastBitmapDrawable(icon);
+ }
+
+ /**
+ * Returns a FastBitmapDrawable with the icon.
+ */
+ public PreloadIconDrawable newPendingIcon(Bitmap icon, Context context) {
+ if (mPreloadProgressPath == null) {
+ mPreloadProgressPath = getPreloadProgressPath(context);
+ }
+ return new PreloadIconDrawable(icon, mPreloadProgressPath);
+ }
+
+
+ protected Path getPreloadProgressPath(Context context) {
+ if (Utilities.isAtLeastO()) {
+ try {
+ // Try to load the path from Mask Icon
+ Drawable maskIcon = context.getDrawable(R.drawable.mask_drawable_wrapper);
+ maskIcon.setBounds(0, 0,
+ PreloadIconDrawable.PATH_SIZE, PreloadIconDrawable.PATH_SIZE);
+ return (Path) maskIcon.getClass().getMethod("getIconMask").invoke(maskIcon);
+ } catch (Exception e) {
+ Log.e(TAG, "Error loading mask icon", e);
+ }
+ }
+
+ // Create a circle static from top center and going clockwise.
+ Path p = new Path();
+ p.moveTo(PreloadIconDrawable.PATH_SIZE / 2, 0);
+ p.addArc(0, 0, PreloadIconDrawable.PATH_SIZE, PreloadIconDrawable.PATH_SIZE, -90, 360);
+ return p;
}
public AllAppsBackgroundDrawable getAllAppsBackground(Context context) {
diff --git a/src/com/android/launcher3/graphics/FixedScaleDrawable.java b/src/com/android/launcher3/graphics/FixedScaleDrawable.java
new file mode 100644
index 000000000..4be4bd552
--- /dev/null
+++ b/src/com/android/launcher3/graphics/FixedScaleDrawable.java
@@ -0,0 +1,41 @@
+package com.android.launcher3.graphics;
+
+import android.annotation.TargetApi;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.graphics.Canvas;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.DrawableWrapper;
+import android.os.Build;
+import android.util.AttributeSet;
+
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * Extension of {@link DrawableWrapper} which scales the child drawables by a fixed amount.
+ */
+@TargetApi(Build.VERSION_CODES.N)
+public class FixedScaleDrawable extends DrawableWrapper {
+
+ // TODO b/33553066 use the constant defined in MaskableIconDrawable
+ private static final float LEGACY_ICON_SCALE = .7f * .6667f;
+
+ public FixedScaleDrawable() {
+ super(new ColorDrawable());
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
+ canvas.scale(LEGACY_ICON_SCALE, LEGACY_ICON_SCALE,
+ getBounds().exactCenterX(), getBounds().exactCenterY());
+ super.draw(canvas);
+ canvas.restoreToCount(saveCount);
+ }
+
+ @Override
+ public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs) { }
+
+ @Override
+ public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) { }
+}
diff --git a/src/com/android/launcher3/graphics/IconPalette.java b/src/com/android/launcher3/graphics/IconPalette.java
index 27aeaba34..991038cca 100644
--- a/src/com/android/launcher3/graphics/IconPalette.java
+++ b/src/com/android/launcher3/graphics/IconPalette.java
@@ -32,16 +32,42 @@ public class IconPalette {
private static final boolean DEBUG = false;
private static final String TAG = "IconPalette";
- public int backgroundColor;
- public int textColor;
- public int secondaryColor;
+ private static final float MIN_PRELOAD_COLOR_SATURATION = 0.2f;
+ private static final float MIN_PRELOAD_COLOR_LIGHTNESS = 0.6f;
+ private static final int DEFAULT_PRELOAD_COLOR = 0xFF009688;
+
+ public final int dominantColor;
+ public final int backgroundColor;
+ public final int textColor;
+ public final int secondaryColor;
+
+ private IconPalette(int color) {
+ dominantColor = color;
+ backgroundColor = getMutedColor(dominantColor);
+ textColor = getTextColorForBackground(backgroundColor);
+ secondaryColor = getLowContrastColor(backgroundColor);
+ }
+
+ /**
+ * Returns a color suitable for the progress bar color of preload icon.
+ */
+ public int getPreloadProgressColor() {
+ int result = dominantColor;
+
+ // Make sure that the dominant color has enough saturation to be visible properly.
+ float[] hsv = new float[3];
+ Color.colorToHSV(result, hsv);
+ if (hsv[1] < MIN_PRELOAD_COLOR_SATURATION) {
+ result = DEFAULT_PRELOAD_COLOR;
+ } else {
+ hsv[2] = Math.max(MIN_PRELOAD_COLOR_LIGHTNESS, hsv[2]);
+ result = Color.HSVToColor(hsv);
+ }
+ return result;
+ }
public static IconPalette fromDominantColor(int dominantColor) {
- IconPalette palette = new IconPalette();
- palette.backgroundColor = getMutedColor(dominantColor);
- palette.textColor = getTextColorForBackground(palette.backgroundColor);
- palette.secondaryColor = getLowContrastColor(palette.backgroundColor);
- return palette;
+ return new IconPalette(dominantColor);
}
/**
diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java
index 472b9135b..3fffb5bb5 100644
--- a/src/com/android/launcher3/graphics/LauncherIcons.java
+++ b/src/com/android/launcher3/graphics/LauncherIcons.java
@@ -29,13 +29,10 @@ import android.graphics.PaintFlagsDrawFilter;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.PaintDrawable;
-import android.graphics.drawable.ScaleDrawable;
import android.os.Process;
import android.os.UserHandle;
-import android.view.Gravity;
import com.android.launcher3.AppInfo;
import com.android.launcher3.IconCache;
@@ -52,8 +49,6 @@ import com.android.launcher3.shortcuts.ShortcutInfoCompat;
* Helper methods for generating various launcher icons
*/
public class LauncherIcons {
- // TODO b/33553066 use the constant defined in MaskableIconDrawable
- private static final float LEGACY_ICON_SCALE = .7f * .6667f;
private static final Rect sOldBounds = new Rect();
private static final Canvas sCanvas = new Canvas();
@@ -236,17 +231,16 @@ public class LauncherIcons {
if (!(ProviderConfig.IS_DOGFOOD_BUILD && Utilities.isAtLeastO())) {
return drawable;
}
- int color = context.getResources().getColor(R.color.legacy_icon_background);
- ColorDrawable colorDrawable = new ColorDrawable(color);
- ScaleDrawable scaleDrawable = new ScaleDrawable(drawable,
- Gravity.CENTER, LEGACY_ICON_SCALE, LEGACY_ICON_SCALE);
- scaleDrawable.setLevel(1);
+
try {
Class clazz = Class.forName("android.graphics.drawable.MaskableIconDrawable");
- if (!clazz.isAssignableFrom(drawable.getClass())){
+ if (!clazz.isAssignableFrom(drawable.getClass())) {
+ Drawable maskWrapper =
+ context.getDrawable(R.drawable.mask_drawable_wrapper).mutate();
+ ((FixedScaleDrawable) clazz.getMethod("getForeground").invoke(maskWrapper))
+ .setDrawable(drawable);
- return (Drawable) clazz.getConstructor(Drawable.class, Drawable.class)
- .newInstance(colorDrawable, scaleDrawable);
+ return maskWrapper;
}
} catch (Exception e) {
return drawable;
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
new file mode 100644
index 000000000..bc07ce1a5
--- /dev/null
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.launcher3.graphics;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PathMeasure;
+import android.graphics.Rect;
+import android.util.Property;
+import android.util.SparseArray;
+import android.view.animation.LinearInterpolator;
+
+import com.android.launcher3.FastBitmapDrawable;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Extension of {@link FastBitmapDrawable} which shows a progress bar around the icon.
+ */
+public class PreloadIconDrawable extends FastBitmapDrawable {
+
+ private static final Property<PreloadIconDrawable, Float> INTERNAL_STATE =
+ new Property<PreloadIconDrawable, Float>(Float.TYPE, "internalStateProgress") {
+ @Override
+ public Float get(PreloadIconDrawable object) {
+ return object.mInternalStateProgress;
+ }
+
+ @Override
+ public void set(PreloadIconDrawable object, Float value) {
+ object.setInternalProgress(value);
+ }
+ };
+
+ public static final int PATH_SIZE = 100;
+
+ private static final float PROGRESS_WIDTH = 7;
+ private static final float PROGRESS_GAP = 2;
+ private static final int MAX_PAINT_ALPHA = 255;
+
+ private static final long DURATION_SCALE = 500;
+
+ // The smaller the number, the faster the animation would be.
+ // Duration = COMPLETE_ANIM_FRACTION * DURATION_SCALE
+ private static final float COMPLETE_ANIM_FRACTION = 0.3f;
+
+ private static final int COLOR_TRACK = 0x77EEEEEE;
+ private static final int COLOR_SHADOW = 0x55000000;
+
+ private static final float SMALL_SCALE = 0.75f;
+
+ private static final SparseArray<WeakReference<Bitmap>> sShadowCache = new SparseArray<>();
+
+ private final Matrix mTmpMatrix = new Matrix();
+ private final PathMeasure mPathMeasure = new PathMeasure();
+
+ // Path in [0, 100] bounds.
+ private final Path mProgressPath;
+
+ private final Path mScaledTrackPath;
+ private final Path mScaledProgressPath;
+ private final Paint mProgressPaint;
+
+ private Bitmap mShadowBitmap;
+ private int mIndicatorColor = 0;
+
+ private int mTrackAlpha;
+ private float mTrackLength;
+ private float mIconScale;
+
+ private boolean mRanFinishAnimation;
+
+ // Progress of the internal state. [0, 1] indicates the fraction of completed progress,
+ // [1, (1 + COMPLETE_ANIM_FRACTION)] indicates the progress of zoom animation.
+ private float mInternalStateProgress;
+
+ private ObjectAnimator mCurrentAnim;
+
+ /**
+ * @param progressPath fixed path in the bounds [0, 0, 100, 100] representing a progress bar.
+ */
+ public PreloadIconDrawable(Bitmap b, Path progressPath) {
+ super(b);
+ mProgressPath = progressPath;
+ mScaledTrackPath = new Path();
+ mScaledProgressPath = new Path();
+
+ mProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+ mProgressPaint.setStyle(Paint.Style.STROKE);
+ mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
+
+ setInternalProgress(0);
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ super.onBoundsChange(bounds);
+ mTmpMatrix.setScale(
+ (bounds.width() - PROGRESS_WIDTH - 2 * PROGRESS_GAP) / PATH_SIZE,
+ (bounds.height() - PROGRESS_WIDTH - 2 * PROGRESS_GAP) / PATH_SIZE);
+ mTmpMatrix.postTranslate(
+ bounds.left + PROGRESS_WIDTH / 2 + PROGRESS_GAP,
+ bounds.top + PROGRESS_WIDTH / 2 + PROGRESS_GAP);
+
+ mProgressPath.transform(mTmpMatrix, mScaledTrackPath);
+ float scale = bounds.width() / PATH_SIZE;
+ mProgressPaint.setStrokeWidth(PROGRESS_WIDTH * scale);
+
+ mShadowBitmap = getShadowBitmap(bounds.width(), bounds.height(),
+ (PROGRESS_GAP ) * scale);
+ mPathMeasure.setPath(mScaledTrackPath, true);
+ mTrackLength = mPathMeasure.getLength();
+
+ setInternalProgress(mInternalStateProgress);
+ }
+
+ private Bitmap getShadowBitmap(int width, int height, float shadowRadius) {
+ int key = (width << 16) | height;
+ WeakReference<Bitmap> shadowRef = sShadowCache.get(key);
+ Bitmap shadow = shadowRef != null ? shadowRef.get() : null;
+ if (shadow != null) {
+ return shadow;
+ }
+ shadow = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas c = new Canvas(shadow);
+ mProgressPaint.setShadowLayer(shadowRadius, 0, 0, COLOR_SHADOW);
+ mProgressPaint.setColor(COLOR_TRACK);
+ mProgressPaint.setAlpha(MAX_PAINT_ALPHA);
+ c.drawPath(mScaledTrackPath, mProgressPaint);
+ mProgressPaint.clearShadowLayer();
+ c.setBitmap(null);
+
+ sShadowCache.put(key, new WeakReference<>(shadow));
+ return shadow;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ if (mRanFinishAnimation) {
+ super.draw(canvas);
+ return;
+ }
+
+ // Draw track.
+ mProgressPaint.setColor(mIndicatorColor);
+ mProgressPaint.setAlpha(mTrackAlpha);
+ if (mShadowBitmap != null) {
+ canvas.drawBitmap(mShadowBitmap, getBounds().left, getBounds().top, mProgressPaint);
+ }
+ canvas.drawPath(mScaledProgressPath, mProgressPaint);
+
+ int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
+ Rect bounds = getBounds();
+
+ canvas.scale(mIconScale, mIconScale, bounds.exactCenterX(), bounds.exactCenterY());
+ drawInternal(canvas);
+ canvas.restoreToCount(saveCount);
+ }
+
+ /**
+ * Updates the install progress based on the level
+ */
+ @Override
+ protected boolean onLevelChange(int level) {
+ // Run the animation if we have already been bound.
+ updateInternalState(level * 0.01f, getBounds().width() > 0, false);
+ return true;
+ }
+
+ /**
+ * Runs the finish animation if it is has not been run after last call to
+ * {@link #onLevelChange}
+ */
+ public void maybePerformFinishedAnimation() {
+ // If the drawable was recently initialized, skip the progress animation.
+ if (mInternalStateProgress == 0) {
+ mInternalStateProgress = 1;
+ }
+ updateInternalState(1 + COMPLETE_ANIM_FRACTION, true, true);
+ }
+
+ public boolean hasNotCompleted() {
+ return !mRanFinishAnimation;
+ }
+
+ private void updateInternalState(float finalProgress, boolean shouldAnimate, boolean isFinish) {
+ if (mCurrentAnim != null) {
+ mCurrentAnim.cancel();
+ mCurrentAnim = null;
+ }
+
+ if (Float.compare(finalProgress, mInternalStateProgress) == 0) {
+ return;
+ }
+ if (!shouldAnimate || mRanFinishAnimation) {
+ setInternalProgress(finalProgress);
+ } else {
+ mCurrentAnim = ObjectAnimator.ofFloat(this, INTERNAL_STATE, finalProgress);
+ mCurrentAnim.setDuration(
+ (long) ((finalProgress - mInternalStateProgress) * DURATION_SCALE));
+ mCurrentAnim.setInterpolator(new LinearInterpolator());
+ if (isFinish) {
+ mCurrentAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mRanFinishAnimation = true;
+ }
+ });
+ }
+ mCurrentAnim.start();
+ }
+
+ }
+
+ /**
+ * Sets the internal progress and updates the UI accordingly
+ * for progress <= 0:
+ * - icon in the small scale and disabled state
+ * - progress track is visible
+ * - progress bar is not visible
+ * for 0 < progress < 1
+ * - icon in the small scale and disabled state
+ * - progress track is visible
+ * - progress bar is visible with dominant color. Progress bar is drawn as a fraction of
+ * {@link #mScaledTrackPath}.
+ * @see PathMeasure#getSegment(float, float, Path, boolean)
+ * for 1 <= progress < (1 + COMPLETE_ANIM_FRACTION)
+ * - we calculate fraction of progress in the above range
+ * - progress track is drawn with alpha based on fraction
+ * - progress bar is drawn at 100% with alpha based on fraction
+ * - icon is scaled up based on fraction and is drawn in enabled state
+ * for progress >= (1 + COMPLETE_ANIM_FRACTION)
+ * - only icon is drawn in normal state
+ */
+ private void setInternalProgress(float progress) {
+ mInternalStateProgress = progress;
+ if (progress <= 0) {
+ mIconScale = SMALL_SCALE;
+ mScaledTrackPath.reset();
+ mTrackAlpha = MAX_PAINT_ALPHA;
+ setIsDisabled(true);
+ } else if (mIndicatorColor == 0) {
+ // Update the indicator color
+ mIndicatorColor = getIconPalette().getPreloadProgressColor();
+ }
+
+ if (progress < 1 && progress > 0) {
+ mPathMeasure.getSegment(0, progress * mTrackLength, mScaledProgressPath, true);
+ mIconScale = SMALL_SCALE;
+ mTrackAlpha = MAX_PAINT_ALPHA;
+ setIsDisabled(true);
+ } else if (progress >= 1) {
+ setIsDisabled(false);
+ mScaledTrackPath.set(mScaledProgressPath);
+ float fraction = (progress - 1) / COMPLETE_ANIM_FRACTION;
+
+ if (fraction >= 1) {
+ // Animation has completed
+ mIconScale = 1;
+ mTrackAlpha = 0;
+ } else {
+ mTrackAlpha = Math.round((1 - fraction) * MAX_PAINT_ALPHA);
+ mIconScale = SMALL_SCALE + (1 - SMALL_SCALE) * fraction;
+ }
+ }
+ invalidateSelf();
+ }
+}