Update FloatingIconView to display PreloadIconDrawables during swipe home animations.

Swiping up to go home on an app that is downloading incrementally would cause a jittery animation. Updated FloatingIconView to animate directly to a PreloadIconDrawable.

Demo: https://drive.google.com/file/d/1ddr8OGR4c1ZneyQ0VkkAAxGpwNcP8Wyn/view?usp=sharing

Fixes: 177685929

Test: manual
Change-Id: I4cd2daa18f6d3fed42a9b666063e0b1c1c46e5d9
(cherry picked from commit b8cab8d8786a7de6adfcd5b77af9408e832ed0c2)
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 22eb15a..575d6cd 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -301,7 +301,7 @@
         verifyHighRes();
 
         if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) {
-            applyProgressLevel(info.getProgressLevel());
+            applyProgressLevel();
         }
         applyDotState(info, false /* animate */);
     }
@@ -603,21 +603,20 @@
      * with the total download progress.
      */
     public void applyLoadingState(boolean promiseStateChanged) {
-        if (getTag() instanceof WorkspaceItemInfo) {
+        if (getTag() instanceof ItemInfoWithIcon) {
             WorkspaceItemInfo info = (WorkspaceItemInfo) getTag();
-            int progressLevel = info.getProgressLevel();
             if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_INCREMENTAL_DOWNLOAD_ACTIVE)
                     != 0) {
-                updateProgressBarUi(progressLevel, progressLevel == 100);
+                updateProgressBarUi(info.getProgressLevel() == 100);
             } else if (info.hasPromiseIconUi() || (info.runtimeStatusFlags
-                    & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
-                updateProgressBarUi(progressLevel, promiseStateChanged);
+                        & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
+                updateProgressBarUi(promiseStateChanged);
             }
         }
     }
 
-    private void updateProgressBarUi(int progressLevel, boolean maybePerformFinishedAnimation) {
-        PreloadIconDrawable preloadDrawable = applyProgressLevel(progressLevel);
+    private void updateProgressBarUi(boolean maybePerformFinishedAnimation) {
+        PreloadIconDrawable preloadDrawable = applyProgressLevel();
         if (preloadDrawable != null && maybePerformFinishedAnimation) {
             preloadDrawable.maybePerformFinishedAnimation();
         }
@@ -625,38 +624,59 @@
 
     /** Applies the given progress level to the this icon's progress bar. */
     @Nullable
-    public PreloadIconDrawable applyProgressLevel(int progressLevel) {
-        if (getTag() instanceof ItemInfoWithIcon) {
-            ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();
-            if (progressLevel >= 100) {
-                setContentDescription(info.contentDescription != null
-                        ? info.contentDescription : "");
-            } else if (progressLevel > 0) {
-                setContentDescription(getContext()
-                        .getString(R.string.app_downloading_title, info.title,
-                                NumberFormat.getPercentInstance().format(progressLevel * 0.01)));
+    public PreloadIconDrawable applyProgressLevel() {
+        if (!(getTag() instanceof ItemInfoWithIcon)) {
+            return null;
+        }
+
+        ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();
+        int progressLevel = info.getProgressLevel();
+        if (progressLevel >= 100) {
+            setContentDescription(info.contentDescription != null
+                    ? info.contentDescription : "");
+        } else if (progressLevel > 0) {
+            setContentDescription(getContext()
+                    .getString(R.string.app_downloading_title, info.title,
+                            NumberFormat.getPercentInstance().format(progressLevel * 0.01)));
+        } else {
+            setContentDescription(getContext()
+                    .getString(R.string.app_waiting_download_title, info.title));
+        }
+        if (mIcon != null) {
+            PreloadIconDrawable preloadIconDrawable;
+            if (mIcon instanceof PreloadIconDrawable) {
+                preloadIconDrawable = (PreloadIconDrawable) mIcon;
+                preloadIconDrawable.setLevel(progressLevel);
+                preloadIconDrawable.setIsDisabled(!info.isAppStartable());
             } else {
-                setContentDescription(getContext()
-                        .getString(R.string.app_waiting_download_title, info.title));
+                preloadIconDrawable = makePreloadIcon();
+                setIcon(preloadIconDrawable);
             }
-            if (mIcon != null) {
-                final PreloadIconDrawable preloadDrawable;
-                if (mIcon instanceof PreloadIconDrawable) {
-                    preloadDrawable = (PreloadIconDrawable) mIcon;
-                    preloadDrawable.setLevel(progressLevel);
-                    preloadDrawable.setIsDisabled(!info.isAppStartable());
-                } else {
-                    preloadDrawable = newPendingIcon(getContext(), info);
-                    preloadDrawable.setLevel(progressLevel);
-                    preloadDrawable.setIsDisabled(!info.isAppStartable());
-                    setIcon(preloadDrawable);
-                }
-                return preloadDrawable;
-            }
+            return preloadIconDrawable;
         }
         return null;
     }
 
+    /**
+     * Creates a PreloadIconDrawable with the appropriate progress level without mutating this
+     * object.
+     */
+    @Nullable
+    public PreloadIconDrawable makePreloadIcon() {
+        if (!(getTag() instanceof ItemInfoWithIcon)) {
+            return null;
+        }
+
+        ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();
+        int progressLevel = info.getProgressLevel();
+        final PreloadIconDrawable preloadDrawable = newPendingIcon(getContext(), info);
+
+        preloadDrawable.setLevel(progressLevel);
+        preloadDrawable.setIsDisabled(!info.isAppStartable());
+
+        return preloadDrawable;
+    }
+
     public void applyDotState(ItemInfo itemInfo, boolean animate) {
         if (mIcon instanceof FastBitmapDrawable) {
             boolean wasDotted = mDotInfo != null;
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index 139d4a8..b1fe4a2 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -276,15 +276,15 @@
 
     @Override
     public ConstantState getConstantState() {
-        return new MyConstantState(mBitmap, mIconColor, mIsDisabled);
+        return new FastBitmapConstantState(mBitmap, mIconColor, mIsDisabled);
     }
 
-    protected static class MyConstantState extends ConstantState {
+    protected static class FastBitmapConstantState extends ConstantState {
         protected final Bitmap mBitmap;
         protected final int mIconColor;
         protected final boolean mIsDisabled;
 
-        public MyConstantState(Bitmap bitmap, int color, boolean isDisabled) {
+        public FastBitmapConstantState(Bitmap bitmap, int color, boolean isDisabled) {
             mBitmap = bitmap;
             mIconColor = color;
             mIsDisabled = isDisabled;
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
index 00bdb70..769cb5e 100644
--- a/src/com/android/launcher3/allapps/AllAppsStore.java
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -154,7 +154,7 @@
     public void updateProgressBar(AppInfo app) {
         updateAllIcons((child) -> {
             if (child.getTag() == app) {
-                child.applyProgressLevel(app.getProgressLevel());
+                child.applyProgressLevel();
             }
         });
     }
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index 9971990..304d496 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -105,6 +105,10 @@
     private ObjectAnimator mCurrentAnim;
 
     public PreloadIconDrawable(ItemInfoWithIcon info, Context context) {
+        this(info, IconPalette.getPreloadProgressColor(context, info.bitmap.color));
+    }
+
+    public PreloadIconDrawable(ItemInfoWithIcon info, int indicatorColor) {
         super(info.bitmap);
         mItem = info;
         mShapePath = getShapePath();
@@ -114,7 +118,7 @@
         mProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
         mProgressPaint.setStyle(Paint.Style.STROKE);
         mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
-        mIndicatorColor = IconPalette.getPreloadProgressColor(context, mIconColor);
+        mIndicatorColor = indicatorColor;
 
         setInternalProgress(0);
 
@@ -297,4 +301,42 @@
     public static PreloadIconDrawable newPendingIcon(Context context, ItemInfoWithIcon info) {
         return new PreloadIconDrawable(info, context);
     }
+
+    @Override
+    public ConstantState getConstantState() {
+        return new PreloadIconConstantState(
+                mBitmap, mIconColor, !mItem.isAppStartable(), mItem, mIndicatorColor);
+    }
+
+    protected static class PreloadIconConstantState extends FastBitmapConstantState {
+
+        protected final ItemInfoWithIcon mInfo;
+        protected final int mIndicatorColor;
+        protected final int mLevel;
+
+        public PreloadIconConstantState(
+                Bitmap bitmap,
+                int iconColor,
+                boolean isDisabled,
+                ItemInfoWithIcon info,
+                int indicatorcolor) {
+            super(bitmap, iconColor, isDisabled);
+            mInfo = info;
+            mIndicatorColor = indicatorcolor;
+            mLevel = info.getProgressLevel();
+        }
+
+        @Override
+        public PreloadIconDrawable newDrawable() {
+            PreloadIconDrawable drawable = new PreloadIconDrawable(mInfo, mIndicatorColor);
+            drawable.setLevel(mLevel);
+            drawable.setIsDisabled(mIsDisabled);
+            return drawable;
+        }
+
+        @Override
+        public int getChangingConfigurations() {
+            return 0;
+        }
+    }
 }
diff --git a/src/com/android/launcher3/icons/ClockDrawableWrapper.java b/src/com/android/launcher3/icons/ClockDrawableWrapper.java
index b7dd092..1bd252b 100644
--- a/src/com/android/launcher3/icons/ClockDrawableWrapper.java
+++ b/src/com/android/launcher3/icons/ClockDrawableWrapper.java
@@ -308,7 +308,7 @@
             return new ClockConstantState(mInfo, isDisabled());
         }
 
-        private static class ClockConstantState extends MyConstantState {
+        private static class ClockConstantState extends FastBitmapConstantState {
 
             private final ClockBitmapInfo mInfo;
 
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 1857c5a..23c3722 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -51,8 +51,10 @@
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
 import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.graphics.PreloadIconDrawable;
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.shortcuts.DeepShortcutView;
 
@@ -252,12 +254,26 @@
     @SuppressWarnings("WrongThread")
     private static void getIconResult(Launcher l, View originalView, ItemInfo info, RectF pos,
             IconLoadResult iconLoadResult) {
-        Drawable drawable = null;
+        Drawable drawable;
+        Drawable btvIcon;
         Drawable badge = null;
         boolean supportsAdaptiveIcons = ADAPTIVE_ICON_WINDOW_ANIM.get()
                 && !info.isDisabled(); // Use original icon for disabled icons.
-        Drawable btvIcon = originalView instanceof BubbleTextView
-                ? ((BubbleTextView) originalView).getIcon() : null;
+
+        if (originalView instanceof BubbleTextView) {
+            BubbleTextView btv = (BubbleTextView) originalView;
+
+            if (info instanceof ItemInfoWithIcon
+                    && (((ItemInfoWithIcon) info).runtimeStatusFlags
+                        & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) {
+                btvIcon = btv.makePreloadIcon();
+            } else {
+                btvIcon = btv.getIcon();
+            }
+        } else {
+            btvIcon = null;
+        }
+
         if (info instanceof SystemShortcut) {
             if (originalView instanceof ImageView) {
                 drawable = ((ImageView) originalView).getDrawable();
@@ -266,6 +282,9 @@
             } else {
                 drawable = originalView.getBackground();
             }
+        } else if (btvIcon instanceof PreloadIconDrawable) {
+            // Force the progress bar to display.
+            drawable = btvIcon;
         } else {
             int width = (int) pos.width();
             int height = (int) pos.height();