summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher3/graphics
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/launcher3/graphics
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/launcher3/graphics')
-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
6 files changed, 409 insertions, 29 deletions
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();
+ }
+}