Add UI updates for incremental app installs.

1. Changed Preload Icon UI to be grayscale while the app is not startable.
2. Added progress bar for when app is installed but still ownloading.
3. Updated Preload Icon progress and click handling to use new incremental api.

Progress bar color updates will follow in a separate CL.

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

Bug: 171008815

Test: manual

Change-Id: I5874a5146d79a8c91d7d90ff0b9c1c427a3c95dd
diff --git a/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
index e43df21..412ace0 100644
--- a/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
@@ -67,7 +67,7 @@
         for (ItemInfo info : mModelHelper.getBgDataModel().itemsIdMap) {
             if (info instanceof WorkspaceItemInfo) {
                 assertEquals(updates.contains(info.id) ? progress: 0,
-                        ((WorkspaceItemInfo) info).getInstallProgress());
+                        ((WorkspaceItemInfo) info).getProgressLevel());
             } else {
                 assertEquals(updates.contains(info.id) ? progress: -1,
                         ((LauncherAppWidgetInfo) info).installProgress);
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 3eb52ad..f44f88b 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -50,6 +50,7 @@
 import android.view.ViewDebug;
 import android.widget.TextView;
 
+import androidx.annotation.Nullable;
 import androidx.core.graphics.ColorUtils;
 
 import com.android.launcher3.Launcher.OnResumeCallback;
@@ -71,7 +72,6 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.PackageItemInfo;
-import com.android.launcher3.model.data.PromiseAppInfo;
 import com.android.launcher3.model.data.RemoteActionItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.SafeCloseable;
@@ -287,10 +287,7 @@
     public void applyFromWorkspaceItem(WorkspaceItemInfo info, boolean promiseStateChanged) {
         applyIconAndLabel(info);
         setTag(info);
-        if (promiseStateChanged || (info.hasPromiseIconUi())) {
-            applyPromiseState(promiseStateChanged);
-        }
-
+        applyLoadingState(promiseStateChanged);
         applyDotState(info, false /* animate */);
     }
 
@@ -303,9 +300,8 @@
         // Verify high res immediately
         verifyHighRes();
 
-        if (info instanceof PromiseAppInfo) {
-            PromiseAppInfo promiseAppInfo = (PromiseAppInfo) info;
-            applyProgressLevel(promiseAppInfo.level);
+        if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) {
+            applyProgressLevel(info.getProgressLevel());
         }
         applyDotState(info, false /* animate */);
     }
@@ -335,6 +331,10 @@
         mDotParams.color = IconPalette.getMutedColor(info.bitmap.color, 0.54f);
 
         setIcon(iconDrawable);
+        applyLabel(info);
+    }
+
+    private void applyLabel(ItemInfoWithIcon info) {
         setText(info.title);
         if (info.contentDescription != null) {
             setContentDescription(info.isDisabled()
@@ -595,21 +595,35 @@
         mLongPressHelper.cancelLongPress();
     }
 
-    public void applyPromiseState(boolean promiseStateChanged) {
+    /** Applies the loading progress value to the progress bar.
+     *
+     * If this app is installing, the progress bar will be updated with the installation progress.
+     * If this app is installed and downloading incrementally, the progress bar will be updated
+     * with the total download progress.
+     */
+    public void applyLoadingState(boolean promiseStateChanged) {
         if (getTag() instanceof WorkspaceItemInfo) {
             WorkspaceItemInfo info = (WorkspaceItemInfo) getTag();
-            final boolean isPromise = info.hasPromiseIconUi();
-            final int progressLevel = isPromise ?
-                    ((info.hasStatusFlag(WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE) ?
-                            info.getInstallProgress() : 0)) : 100;
-
-            PreloadIconDrawable preloadDrawable = applyProgressLevel(progressLevel);
-            if (preloadDrawable != null && promiseStateChanged) {
-                preloadDrawable.maybePerformFinishedAnimation();
+            int progressLevel = info.getProgressLevel();
+            if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_INCREMENTAL_DOWNLOAD_ACTIVE)
+                    != 0) {
+                updateProgressBarUi(progressLevel, progressLevel == 100);
+            } else if (info.hasPromiseIconUi() || (info.runtimeStatusFlags
+                        & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
+                updateProgressBarUi(progressLevel, promiseStateChanged);
             }
         }
     }
 
+    private void updateProgressBarUi(int progressLevel, boolean maybePerformFinishedAnimation) {
+        PreloadIconDrawable preloadDrawable = applyProgressLevel(progressLevel);
+        if (preloadDrawable != null && maybePerformFinishedAnimation) {
+            preloadDrawable.maybePerformFinishedAnimation();
+        }
+    }
+
+    /** 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();
@@ -629,9 +643,11 @@
                 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;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index e099d85..4d1b7661 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -137,7 +137,6 @@
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.model.data.PromiseAppInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.pm.PinRequestHelper;
@@ -2517,8 +2516,8 @@
     }
 
     @Override
-    public void bindPromiseAppProgressUpdated(PromiseAppInfo app) {
-        mAppsView.getAppsStore().updatePromiseAppProgress(app);
+    public void bindIncrementalDownloadProgressUpdated(AppInfo app) {
+        mAppsView.getAppsStore().updateProgressBar(app);
     }
 
     @Override
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 8458152..e89b9b0 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -48,6 +48,7 @@
 import com.android.launcher3.model.LoaderTask;
 import com.android.launcher3.model.ModelDelegate;
 import com.android.launcher3.model.ModelWriter;
+import com.android.launcher3.model.PackageIncrementalDownloadUpdatedTask;
 import com.android.launcher3.model.PackageInstallStateChangedTask;
 import com.android.launcher3.model.PackageUpdatedTask;
 import com.android.launcher3.model.ShortcutsChangedTask;
@@ -196,6 +197,15 @@
     }
 
     @Override
+    public void onPackageLoadingProgressChanged(
+                String packageName, UserHandle user, float progress) {
+        if (Utilities.ATLEAST_S) {
+            enqueueModelUpdateTask(new PackageIncrementalDownloadUpdatedTask(
+                    packageName, user, progress));
+        }
+    }
+
+    @Override
     public void onShortcutsChanged(String packageName, List<ShortcutInfo> shortcuts,
             UserHandle user) {
         enqueueModelUpdateTask(new ShortcutsChangedTask(packageName, shortcuts, user, true));
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 5c2f35b..df5d234 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -107,12 +107,13 @@
     public static final String[] EMPTY_STRING_ARRAY = new String[0];
     public static final Person[] EMPTY_PERSON_ARRAY = new Person[0];
 
-    public static final boolean ATLEAST_R = BuildCompat.isAtLeastR();
+    public static final boolean ATLEAST_P = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
 
     public static final boolean ATLEAST_Q = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
 
-    public static final boolean ATLEAST_P =
-            Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
+    public static final boolean ATLEAST_R = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R;
+
+    public static final boolean ATLEAST_S = BuildCompat.isAtLeastS();
 
     /**
      * Set on a motion event dispatched from the nav bar. See {@link MotionEvent#setEdgeFlags(int)}.
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 777ea3c..65eba20 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -3154,7 +3154,7 @@
         ItemOperator op = (info, v) -> {
             if (info instanceof WorkspaceItemInfo && v instanceof BubbleTextView
                     && updates.contains(info)) {
-                ((BubbleTextView) v).applyPromiseState(false /* promiseStateChanged */);
+                ((BubbleTextView) v).applyLoadingState(false /* promiseStateChanged */);
             } else if (v instanceof PendingAppWidgetHostView
                     && info instanceof LauncherAppWidgetInfo
                     && updates.contains(info)) {
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
index 3ae0a18..00bdb70 100644
--- a/src/com/android/launcher3/allapps/AllAppsStore.java
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -24,7 +24,6 @@
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.PromiseAppInfo;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.PackageUserKey;
 
@@ -145,10 +144,17 @@
         });
     }
 
-    public void updatePromiseAppProgress(PromiseAppInfo app) {
+    /**
+     * Sets the AppInfo's associated icon's progress bar.
+     *
+     * If this app is installed and supports incremental downloads, the progress bar will be updated
+     * the app's total download progress. Otherwise, the progress bar will be updated to the app's
+     * installation progress.
+     */
+    public void updateProgressBar(AppInfo app) {
         updateAllIcons((child) -> {
             if (child.getTag() == app) {
-                child.applyProgressLevel(app.level);
+                child.applyProgressLevel(app.getProgressLevel());
             }
         });
     }
diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java
index 7f8a15c..9ae7faf 100644
--- a/src/com/android/launcher3/folder/PreviewItemManager.java
+++ b/src/com/android/launcher3/folder/PreviewItemManager.java
@@ -39,6 +39,7 @@
 
 import com.android.launcher3.Utilities;
 import com.android.launcher3.graphics.PreloadIconDrawable;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.views.ActivityContext;
 
@@ -394,9 +395,10 @@
     }
 
     private void setDrawable(PreviewItemDrawingParams p, WorkspaceItemInfo item) {
-        if (item.hasPromiseIconUi()) {
+        if (item.hasPromiseIconUi() || (item.runtimeStatusFlags
+                    & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) {
             PreloadIconDrawable drawable = newPendingIcon(mContext, item);
-            drawable.setLevel(item.getInstallProgress());
+            drawable.setLevel(item.getProgressLevel());
             p.drawable = drawable;
         } else {
             p.drawable = newIcon(mContext, item);
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index e85b056..9971990 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -117,6 +117,8 @@
         mIndicatorColor = IconPalette.getPreloadProgressColor(context, mIconColor);
 
         setInternalProgress(0);
+
+        setIsDisabled(!info.isAppStartable());
     }
 
     @Override
@@ -266,14 +268,12 @@
             mIconScale = SMALL_SCALE;
             mScaledTrackPath.reset();
             mTrackAlpha = MAX_PAINT_ALPHA;
-            setIsDisabled(true);
         }
 
         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(mItem.isDisabled());
             mScaledTrackPath.set(mScaledProgressPath);
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index c236fa6..56dbbd3 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -34,6 +34,7 @@
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.InstallSessionHelper;
+import com.android.launcher3.pm.PackageInstallInfo;
 import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.PackageManagerHelper;
@@ -132,7 +133,9 @@
                             continue;
                         }
                     } else {
-                        workspaceInfo.setInstallProgress((int) sessionInfo.getProgress());
+                        workspaceInfo.setProgressLevel(
+                                (int) (sessionInfo.getProgress() * 100),
+                                PackageInstallInfo.STATUS_INSTALLING);
                     }
 
                     if (hasActivity) {
diff --git a/src/com/android/launcher3/model/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java
index 2695e66..8d5cf74 100644
--- a/src/com/android/launcher3/model/AllAppsList.java
+++ b/src/com/android/launcher3/model/AllAppsList.java
@@ -21,11 +21,11 @@
 
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.LauncherApps;
 import android.os.LocaleList;
-import android.os.Process;
 import android.os.UserHandle;
 import android.util.Log;
 
@@ -37,7 +37,6 @@
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.PromiseAppInfo;
 import com.android.launcher3.pm.PackageInstallInfo;
 import com.android.launcher3.util.FlagOp;
 import com.android.launcher3.util.ItemInfoMatcher;
@@ -150,7 +149,7 @@
                 .getApplicationInfo(installInfo.packageName, installInfo.user, 0);
         // only if not yet installed
         if (applicationInfo == null) {
-            PromiseAppInfo info = new PromiseAppInfo(installInfo);
+            AppInfo info = new AppInfo(installInfo);
             mIconCache.getTitleAndIcon(info, info.usingLowResIcon());
             info.sectionName = mIndex.computeSectionName(info.title);
 
@@ -159,24 +158,26 @@
         }
     }
 
-    public PromiseAppInfo updatePromiseInstallInfo(PackageInstallInfo installInfo) {
-        UserHandle user = Process.myUserHandle();
-        for (int i=0; i < data.size(); i++) {
+    /** Updates the given PackageInstallInfo's associated AppInfo's installation info. */
+    public List<AppInfo> updatePromiseInstallInfo(PackageInstallInfo installInfo) {
+        List<AppInfo> updatedAppInfos = new ArrayList<>();
+        UserHandle user = installInfo.user;
+        for (int i = data.size() - 1; i >= 0; i--) {
             final AppInfo appInfo = data.get(i);
             final ComponentName tgtComp = appInfo.getTargetComponent();
             if (tgtComp != null && tgtComp.getPackageName().equals(installInfo.packageName)
-                    && appInfo.user.equals(user)
-                    && appInfo instanceof PromiseAppInfo) {
-                final PromiseAppInfo promiseAppInfo = (PromiseAppInfo) appInfo;
-                if (installInfo.state == PackageInstallInfo.STATUS_INSTALLING) {
-                    promiseAppInfo.level = installInfo.progress;
-                    return promiseAppInfo;
+                    && appInfo.user.equals(user)) {
+                if (installInfo.state == PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING
+                            || installInfo.state == PackageInstallInfo.STATUS_INSTALLING) {
+                    appInfo.setProgressLevel(installInfo);
+
+                    updatedAppInfos.add(appInfo);
                 } else if (installInfo.state == PackageInstallInfo.STATUS_FAILED) {
                     removeApp(i);
                 }
             }
         }
-        return null;
+        return updatedAppInfos;
     }
 
     private void removeApp(int index) {
@@ -268,8 +269,14 @@
                 if (applicationInfo == null) {
                     add(new AppInfo(context, info, user), info);
                 } else {
+                    Intent launchIntent = AppInfo.makeLaunchIntent(info);
+
                     mIconCache.getTitleAndIcon(applicationInfo, info, true /* useLowResIcon */);
                     applicationInfo.sectionName = mIndex.computeSectionName(applicationInfo.title);
+                    applicationInfo.setProgressLevel(
+                            PackageManagerHelper.getLoadingProgress(info),
+                            PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING);
+                    applicationInfo.intent = launchIntent;
 
                     mDataChanged = true;
                 }
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index c217a47..2d860a4 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -39,7 +39,6 @@
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.model.data.PromiseAppInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.shortcuts.ShortcutKey;
@@ -459,7 +458,11 @@
         void preAddApps();
         void bindAppsAdded(IntArray newScreens,
                 ArrayList<ItemInfo> addNotAnimated, ArrayList<ItemInfo> addAnimated);
-        void bindPromiseAppProgressUpdated(PromiseAppInfo app);
+
+        /**
+         * Binds updated incremental download progress
+         */
+        void bindIncrementalDownloadProgressUpdated(AppInfo app);
         void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated);
         void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets);
         void bindRestoreItemsChange(HashSet<ItemInfo> updates);
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 532834e..19d9af9 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -35,11 +35,13 @@
 import android.util.Log;
 import android.util.LongSparseArray;
 
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.config.FeatureFlags;
@@ -92,6 +94,9 @@
     private final int restoredIndex;
     private final int intentIndex;
 
+    @Nullable
+    private LauncherActivityInfo mActivityInfo;
+
     // Properties loaded per iteration
     public long serialNumber;
     public UserHandle user;
@@ -132,6 +137,8 @@
     public boolean moveToNext() {
         boolean result = super.moveToNext();
         if (result) {
+            mActivityInfo = null;
+
             // Load common properties.
             itemType = getInt(itemTypeIndex);
             container = getInt(containerIndex);
@@ -245,6 +252,10 @@
         return info;
     }
 
+    public LauncherActivityInfo getLauncherActivityInfo() {
+        return mActivityInfo;
+    }
+
     /**
      * Make an WorkspaceItemInfo object for a shortcut that is an application.
      */
@@ -264,25 +275,25 @@
         Intent newIntent = new Intent(Intent.ACTION_MAIN, null);
         newIntent.addCategory(Intent.CATEGORY_LAUNCHER);
         newIntent.setComponent(componentName);
-        LauncherActivityInfo lai = mContext.getSystemService(LauncherApps.class)
+        mActivityInfo = mContext.getSystemService(LauncherApps.class)
                 .resolveActivity(newIntent, user);
-        if ((lai == null) && !allowMissingTarget) {
+        if ((mActivityInfo == null) && !allowMissingTarget) {
             Log.d(TAG, "Missing activity found in getShortcutInfo: " + componentName);
             return null;
         }
 
         final WorkspaceItemInfo info = new WorkspaceItemInfo();
-        info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+        info.itemType = Favorites.ITEM_TYPE_APPLICATION;
         info.user = user;
         info.intent = newIntent;
 
-        mIconCache.getTitleAndIcon(info, lai, useLowResIcon);
+        mIconCache.getTitleAndIcon(info, mActivityInfo, useLowResIcon);
         if (mIconCache.isDefaultIcon(info.bitmap, user)) {
             loadIcon(info);
         }
 
-        if (lai != null) {
-            AppInfo.updateRuntimeFlagsForActivityTarget(info, lai);
+        if (mActivityInfo != null) {
+            AppInfo.updateRuntimeFlagsForActivityTarget(info, mActivityInfo);
         }
 
         // from the db
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 8e085ce..f74c8b5 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -69,6 +69,7 @@
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.model.data.PackageItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -591,11 +592,24 @@
                                 if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) {
                                     tempPackageKey.update(targetPkg, c.user);
                                     SessionInfo si = installingPkgs.get(tempPackageKey);
-                                    if (si == null) {
-                                        info.status &= ~WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE;
-                                    } else {
-                                        info.setInstallProgress((int) (si.getProgress() * 100));
-                                    }
+                                        LauncherActivityInfo activityInfo =
+                                                c.getLauncherActivityInfo();
+                                        if (si == null) {
+                                            info.runtimeStatusFlags &=
+                                                    ~ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
+                                        } else if (activityInfo == null) {
+                                            int installProgress = (int) (si.getProgress() * 100);
+
+                                            info.setProgressLevel(
+                                                    installProgress,
+                                                    PackageInstallInfo.STATUS_INSTALLING);
+                                        } else {
+                                            info.setProgressLevel(
+                                                    PackageManagerHelper
+                                                            .getLoadingProgress(activityInfo),
+                                                    PackageInstallInfo
+                                                            .STATUS_INSTALLED_DOWNLOADING);
+                                        }
                                 }
 
                                 c.checkAndAddItem(info, mBgDataModel);
diff --git a/src/com/android/launcher3/model/PackageIncrementalDownloadUpdatedTask.java b/src/com/android/launcher3/model/PackageIncrementalDownloadUpdatedTask.java
new file mode 100644
index 0000000..e3e8769
--- /dev/null
+++ b/src/com/android/launcher3/model/PackageIncrementalDownloadUpdatedTask.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2020 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.model;
+
+import android.content.ComponentName;
+import android.os.UserHandle;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.pm.PackageInstallInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Handles updates due to incremental download progress updates.
+ */
+public class PackageIncrementalDownloadUpdatedTask extends BaseModelUpdateTask {
+
+    private final UserHandle mUser;
+    private final int mProgress;
+    private final String mPackageName;
+
+    public PackageIncrementalDownloadUpdatedTask(
+            String packageName, UserHandle user, float progress) {
+        mUser = user;
+        mProgress = 1 - progress > 0.001 ? (int) (100 * progress) : 100;
+        mPackageName = packageName;
+    }
+
+    @Override
+    public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList appsList) {
+        PackageInstallInfo downloadInfo = new PackageInstallInfo(
+                mPackageName,
+                PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING,
+                mProgress,
+                mUser);
+
+        synchronized (appsList) {
+            List<AppInfo> updatedAppInfos = appsList.updatePromiseInstallInfo(downloadInfo);
+            if (!updatedAppInfos.isEmpty()) {
+                for (AppInfo appInfo : updatedAppInfos) {
+                    appInfo.runtimeStatusFlags &= ~ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
+                    scheduleCallbackTask(
+                            c -> c.bindIncrementalDownloadProgressUpdated(appInfo));
+                }
+            }
+            bindApplicationsIfNeeded();
+        }
+
+        final ArrayList<WorkspaceItemInfo> updatedWorkspaceItems = new ArrayList<>();
+        synchronized (dataModel) {
+            dataModel.forAllWorkspaceItemInfos(mUser, si -> {
+                ComponentName cn = si.getTargetComponent();
+                if ((cn != null) && cn.getPackageName().equals(mPackageName)) {
+                    si.runtimeStatusFlags &= ~ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
+                    si.setProgressLevel(downloadInfo);
+                    updatedWorkspaceItems.add(si);
+                }
+            });
+        }
+        bindUpdatedWorkspaceItems(updatedWorkspaceItems);
+    }
+}
diff --git a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
index 8369c48..8215edd 100644
--- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
+++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
@@ -20,14 +20,15 @@
 import android.content.pm.PackageManager;
 
 import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.model.data.PromiseAppInfo;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.PackageInstallInfo;
 import com.android.launcher3.util.InstantAppResolver;
 
 import java.util.HashSet;
+import java.util.List;
 
 /**
  * Handles changes due to a sessions updates for a currently installing app.
@@ -59,9 +60,11 @@
         }
 
         synchronized (apps) {
-            PromiseAppInfo updated = apps.updatePromiseInstallInfo(mInstallInfo);
-            if (updated != null) {
-                scheduleCallbackTask(c -> c.bindPromiseAppProgressUpdated(updated));
+            List<AppInfo> updatedAppInfos = apps.updatePromiseInstallInfo(mInstallInfo);
+            if (!updatedAppInfos.isEmpty()) {
+                for (AppInfo appInfo : updatedAppInfos) {
+                    scheduleCallbackTask(c -> c.bindIncrementalDownloadProgressUpdated(appInfo));
+                }
             }
             bindApplicationsIfNeeded();
         }
@@ -71,11 +74,13 @@
             dataModel.forAllWorkspaceItemInfos(mInstallInfo.user, si -> {
                 ComponentName cn = si.getTargetComponent();
                 if (si.hasPromiseIconUi() && (cn != null)
-                        && mInstallInfo.packageName.equals(cn.getPackageName())) {
-                    si.setInstallProgress(mInstallInfo.progress);
+                        && cn.getPackageName().equals(mInstallInfo.packageName)) {
+                    int installProgress = mInstallInfo.progress;
+
+                    si.setProgressLevel(installProgress, PackageInstallInfo.STATUS_INSTALLING);
                     if (mInstallInfo.state == PackageInstallInfo.STATUS_FAILED) {
                         // Mark this info as broken.
-                        si.status &= ~WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE;
+                        si.runtimeStatusFlags &= ~ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
                     }
                     updates.add(si);
                 }
diff --git a/src/com/android/launcher3/model/data/AppInfo.java b/src/com/android/launcher3/model/data/AppInfo.java
index aee1f2a..39247c2 100644
--- a/src/com/android/launcher3/model/data/AppInfo.java
+++ b/src/com/android/launcher3/model/data/AppInfo.java
@@ -28,10 +28,13 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.pm.PackageInstallInfo;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.PackageManagerHelper;
 
@@ -104,13 +107,37 @@
         this.intent = intent;
     }
 
+    public AppInfo(@NonNull PackageInstallInfo installInfo) {
+        componentName = installInfo.componentName;
+        intent = new Intent(Intent.ACTION_MAIN)
+            .addCategory(Intent.CATEGORY_LAUNCHER)
+            .setComponent(componentName)
+            .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+        user = installInfo.user;
+    }
+
     @Override
     protected String dumpProperties() {
         return super.dumpProperties() + " componentName=" + componentName;
     }
 
     public WorkspaceItemInfo makeWorkspaceItem() {
-        return new WorkspaceItemInfo(this);
+        WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(this);
+
+        if ((runtimeStatusFlags & FLAG_INSTALL_SESSION_ACTIVE) != 0) {
+            // We need to update the component name when the apk is installed
+            workspaceItemInfo.status |= WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
+            // Since the user is manually placing it on homescreen, it should not be auto-removed
+            // later
+            workspaceItemInfo.status |= WorkspaceItemInfo.FLAG_RESTORE_STARTED;
+            workspaceItemInfo.status |= FLAG_INSTALL_SESSION_ACTIVE;
+        }
+        if ((runtimeStatusFlags & FLAG_INCREMENTAL_DOWNLOAD_ACTIVE) != 0) {
+            workspaceItemInfo.runtimeStatusFlags |= FLAG_INCREMENTAL_DOWNLOAD_ACTIVE;
+        }
+
+        return workspaceItemInfo;
     }
 
     public ComponentKey toComponentKey() {
@@ -129,6 +156,12 @@
                         | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
     }
 
+    @Nullable
+    @Override
+    public ComponentName getTargetComponent() {
+        return componentName;
+    }
+
     public static void updateRuntimeFlagsForActivityTarget(
             ItemInfoWithIcon info, LauncherActivityInfo lai) {
         ApplicationInfo appInfo = lai.getApplicationInfo();
@@ -143,6 +176,11 @@
             // The icon for a non-primary user is badged, hence it's not exactly an adaptive icon.
             info.runtimeStatusFlags |= FLAG_ADAPTIVE_ICON;
         }
+
+        // Sets the progress level, installation and incremental download flags.
+        info.setProgressLevel(
+                PackageManagerHelper.getLoadingProgress(lai),
+                PackageInstallInfo.STATUS_INSTALLED);
     }
 
     @Override
diff --git a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
index d95f94f..b8a71d3 100644
--- a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
@@ -16,7 +16,15 @@
 
 package com.android.launcher3.model.data;
 
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.pm.PackageInstallInfo;
+import com.android.launcher3.util.PackageManagerHelper;
 
 /**
  * Represents an ItemInfo which also holds an icon.
@@ -88,11 +96,34 @@
     public static final int FLAG_ICON_BADGED = 1 << 9;
 
     /**
+     * The icon is being installed. If {@link WorkspaceItemInfo#FLAG_RESTORED_ICON} or
+     * {@link WorkspaceItemInfo#FLAG_AUTOINSTALL_ICON} is set, then the icon is either being
+     * installed or is in a broken state.
+     */
+    public static final int FLAG_INSTALL_SESSION_ACTIVE = 1 << 10;
+
+    /**
+     * This icon is still being downloaded.
+     */
+    public static final int FLAG_INCREMENTAL_DOWNLOAD_ACTIVE = 1 << 11;
+
+    public static final int FLAG_SHOW_DOWNLOAD_PROGRESS_MASK = FLAG_INSTALL_SESSION_ACTIVE
+            | FLAG_INCREMENTAL_DOWNLOAD_ACTIVE;
+
+    /**
      * Status associated with the system state of the underlying item. This is calculated every
      * time a new info is created and not persisted on the disk.
      */
     public int runtimeStatusFlags = 0;
 
+    /**
+     * The download progress of the package that this shortcut represents. For legacy apps, this
+     * will always be the installation progress. For apps that support incremental downloads, this
+     * will only match be the installation progress until the app is installed, then this will the
+     * total download progress.
+     */
+    private int mProgressLevel = 100;
+
     protected ItemInfoWithIcon() { }
 
     protected ItemInfoWithIcon(ItemInfoWithIcon info) {
@@ -114,6 +145,72 @@
     }
 
     /**
+     * Returns whether the app this shortcut represents is able to be started. For legacy apps,
+     * this returns whether it is fully installed. For apps that support incremental downloads,
+     * this returns whether the app is either fully downloaded or has installed and is downloading
+     * incrementally.
+     */
+    public boolean isAppStartable() {
+        return ((runtimeStatusFlags & FLAG_INSTALL_SESSION_ACTIVE) == 0)
+                && (((runtimeStatusFlags & FLAG_INCREMENTAL_DOWNLOAD_ACTIVE) != 0)
+                    || mProgressLevel == 100);
+    }
+
+    /**
+     * Returns the download progress for the app this shortcut represents. If this app is not yet
+     * installed or does not support incremental downloads, this will return the installation
+     * progress.
+     */
+    public int getProgressLevel() {
+        if ((runtimeStatusFlags & FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) {
+            return mProgressLevel;
+        }
+        return 100;
+    }
+
+    /**
+     * Sets the download progress for the app this shortcut represents. If this app is not yet
+     * installed or does not support incremental downloads, this will set
+     * {@code FLAG_INSTALL_SESSION_ACTIVE}. If this app is downloading incrementally, this will
+     * set {@code FLAG_INCREMENTAL_DOWNLOAD_ACTIVE}. Otherwise, this will remove both flags.
+     */
+    public void setProgressLevel(PackageInstallInfo installInfo) {
+        setProgressLevel(installInfo.progress, installInfo.state);
+    }
+
+    /**
+     * Sets the download progress for the app this shortcut represents.
+     */
+    public void setProgressLevel(int progress, int status) {
+        if (status == PackageInstallInfo.STATUS_INSTALLING) {
+            mProgressLevel = progress;
+            runtimeStatusFlags = progress < 100
+                    ? runtimeStatusFlags | FLAG_INSTALL_SESSION_ACTIVE
+                    : runtimeStatusFlags & ~FLAG_INSTALL_SESSION_ACTIVE;
+        } else if (status == PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING) {
+            mProgressLevel = progress;
+            runtimeStatusFlags = runtimeStatusFlags & ~FLAG_INSTALL_SESSION_ACTIVE;
+            runtimeStatusFlags = progress < 100
+                    ? runtimeStatusFlags | FLAG_INCREMENTAL_DOWNLOAD_ACTIVE
+                    : runtimeStatusFlags & ~FLAG_INCREMENTAL_DOWNLOAD_ACTIVE;
+        } else {
+            mProgressLevel = status == PackageInstallInfo.STATUS_INSTALLED ? 100 : 0;
+            runtimeStatusFlags = runtimeStatusFlags & ~FLAG_INSTALL_SESSION_ACTIVE;
+            runtimeStatusFlags = runtimeStatusFlags & ~FLAG_INCREMENTAL_DOWNLOAD_ACTIVE;
+        }
+    }
+
+    /** Creates an intent to that launches the app store at this app's page. */
+    @Nullable
+    public Intent getMarketIntent(Context context) {
+        ComponentName componentName = getTargetComponent();
+
+        return componentName != null
+                ? new PackageManagerHelper(context).getMarketIntent(componentName.getPackageName())
+                : null;
+    }
+
+    /**
      * @return a copy of this
      */
     public abstract ItemInfoWithIcon clone();
diff --git a/src/com/android/launcher3/model/data/PromiseAppInfo.java b/src/com/android/launcher3/model/data/PromiseAppInfo.java
deleted file mode 100644
index b6231ed..0000000
--- a/src/com/android/launcher3/model/data/PromiseAppInfo.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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.model.data;
-
-import android.content.Context;
-import android.content.Intent;
-
-import androidx.annotation.NonNull;
-
-import com.android.launcher3.pm.PackageInstallInfo;
-import com.android.launcher3.util.PackageManagerHelper;
-
-public class PromiseAppInfo extends AppInfo {
-
-    public int level = 0;
-
-    public PromiseAppInfo(@NonNull PackageInstallInfo installInfo) {
-        componentName = installInfo.componentName;
-        intent = new Intent(Intent.ACTION_MAIN)
-                .addCategory(Intent.CATEGORY_LAUNCHER)
-                .setComponent(componentName)
-                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                        | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-    }
-
-    @Override
-    public WorkspaceItemInfo makeWorkspaceItem() {
-        WorkspaceItemInfo shortcut = new WorkspaceItemInfo(this);
-        shortcut.setInstallProgress(level);
-        // We need to update the component name when the apk is installed
-        shortcut.status |= WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
-        // Since the user is manually placing it on homescreen, it should not be auto-removed later
-        shortcut.status |= WorkspaceItemInfo.FLAG_RESTORE_STARTED;
-        return shortcut;
-    }
-
-    public Intent getMarketIntent(Context context) {
-        return new PackageManagerHelper(context).getMarketIntent(componentName.getPackageName());
-    }
-}
diff --git a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
index 1e1d093..690e904 100644
--- a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
@@ -58,20 +58,14 @@
     public static final int FLAG_AUTOINSTALL_ICON = 1 << 1;
 
     /**
-     * The icon is being installed. If {@link #FLAG_RESTORED_ICON} or {@link #FLAG_AUTOINSTALL_ICON}
-     * is set, then the icon is either being installed or is in a broken state.
-     */
-    public static final int FLAG_INSTALL_SESSION_ACTIVE = 1 << 2;
-
-    /**
      * Indicates that the widget restore has started.
      */
-    public static final int FLAG_RESTORE_STARTED = 1 << 3;
+    public static final int FLAG_RESTORE_STARTED = 1 << 2;
 
     /**
      * Web UI supported.
      */
-    public static final int FLAG_SUPPORTS_WEB_UI = 1 << 4;
+    public static final int FLAG_SUPPORTS_WEB_UI = 1 << 3;
 
     /**
      * The intent used to start the application.
@@ -98,11 +92,6 @@
      */
     @NonNull private String[] personKeys = Utilities.EMPTY_STRING_ARRAY;
 
-    /**
-     * The installation progress [0-100] of the package that this shortcut represents.
-     */
-    private int mInstallProgress;
-
 
     public WorkspaceItemInfo() {
         itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
@@ -114,7 +103,6 @@
         intent = new Intent(info.intent);
         iconResource = info.iconResource;
         status = info.status;
-        mInstallProgress = info.mInstallProgress;
         personKeys = info.personKeys.clone();
     }
 
@@ -168,15 +156,6 @@
         return isPromise() && !hasStatusFlag(FLAG_SUPPORTS_WEB_UI);
     }
 
-    public int getInstallProgress() {
-        return mInstallProgress;
-    }
-
-    public void setInstallProgress(int progress) {
-        mInstallProgress = progress;
-        status |= FLAG_INSTALL_SESSION_ACTIVE;
-    }
-
     public void updateFromDeepShortcutInfo(ShortcutInfo shortcutInfo, Context context) {
         // {@link ShortcutInfo#getActivity} can change during an update. Recreate the intent
         intent = ShortcutKey.makeIntent(shortcutInfo);
diff --git a/src/com/android/launcher3/pm/PackageInstallInfo.java b/src/com/android/launcher3/pm/PackageInstallInfo.java
index 7997d16..fad904f 100644
--- a/src/com/android/launcher3/pm/PackageInstallInfo.java
+++ b/src/com/android/launcher3/pm/PackageInstallInfo.java
@@ -25,7 +25,8 @@
 
     public static final int STATUS_INSTALLED = 0;
     public static final int STATUS_INSTALLING = 1;
-    public static final int STATUS_FAILED = 2;
+    public static final int STATUS_INSTALLED_DOWNLOADING = 2;
+    public static final int STATUS_FAILED = 3;
 
     public final ComponentName componentName;
     public final String packageName;
@@ -56,5 +57,4 @@
     public static PackageInstallInfo fromState(int state, String packageName, UserHandle user) {
         return new PackageInstallInfo(packageName, state, 0 /* progress */, user);
     }
-
 }
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
index b496608..0266345 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -35,8 +35,8 @@
 import com.android.launcher3.model.BgDataModel;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.model.data.PromiseAppInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.popup.PopupDataProvider;
@@ -215,8 +215,8 @@
             ArrayList<ItemInfo> addAnimated) { }
 
     @Override
-    public void bindPromiseAppProgressUpdated(PromiseAppInfo app) {
-        mAppsView.getAppsStore().updatePromiseAppProgress(app);
+    public void bindIncrementalDownloadProgressUpdated(AppInfo app) {
+        mAppsView.getAppsStore().updateProgressBar(app);
     }
 
     @Override
@@ -315,9 +315,11 @@
         if (tag instanceof ItemInfo) {
             ItemInfo item = (ItemInfo) tag;
             Intent intent;
-            if (item instanceof PromiseAppInfo) {
-                PromiseAppInfo promiseAppInfo = (PromiseAppInfo) item;
-                intent = promiseAppInfo.getMarketIntent(this);
+            if (item instanceof ItemInfoWithIcon
+                    && (((ItemInfoWithIcon) item).runtimeStatusFlags
+                        & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
+                ItemInfoWithIcon appInfo = (ItemInfoWithIcon) item;
+                intent = appInfo.getMarketIntent(this);
             } else {
                 intent = item.getIntent();
             }
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index 9b9cb0a..4158735 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -49,8 +49,8 @@
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.model.data.PromiseAppInfo;
 import com.android.launcher3.model.data.RemoteActionItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.InstallSessionHelper;
@@ -231,8 +231,12 @@
                     ? shortcut.getIntent().getComponent().getPackageName()
                     : shortcut.getIntent().getPackage();
             if (!TextUtils.isEmpty(packageName)) {
-                onClickPendingAppItem(v, launcher, packageName,
-                        shortcut.hasStatusFlag(WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE));
+                onClickPendingAppItem(
+                        v,
+                        launcher,
+                        packageName,
+                        (shortcut.runtimeStatusFlags
+                                & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0);
                 return;
             }
         }
@@ -266,9 +270,12 @@
         TestLogging.recordEvent(
                 TestProtocol.SEQUENCE_MAIN, "start: startAppShortcutOrInfoActivity");
         Intent intent;
-        if (item instanceof PromiseAppInfo) {
-            PromiseAppInfo promiseAppInfo = (PromiseAppInfo) item;
-            intent = promiseAppInfo.getMarketIntent(launcher);
+        if (item instanceof ItemInfoWithIcon
+                && (((ItemInfoWithIcon) item).runtimeStatusFlags
+                    & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
+            ItemInfoWithIcon appInfo = (ItemInfoWithIcon) item;
+            intent = new PackageManagerHelper(launcher)
+                    .getMarketIntent(appInfo.getTargetComponent().getPackageName());
         } else {
             intent = item.getIntent();
         }
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index 523545a..7b26427 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -45,10 +45,11 @@
 
 import com.android.launcher3.PendingAddItemInfo;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.model.data.PromiseAppInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 
 import java.net.URISyntaxException;
@@ -200,9 +201,12 @@
      * Starts the details activity for {@code info}
      */
     public void startDetailsActivityForInfo(ItemInfo info, Rect sourceBounds, Bundle opts) {
-        if (info instanceof PromiseAppInfo) {
-            PromiseAppInfo promiseAppInfo = (PromiseAppInfo) info;
-            mContext.startActivity(promiseAppInfo.getMarketIntent(mContext));
+        if (info instanceof ItemInfoWithIcon
+                && (((ItemInfoWithIcon) info).runtimeStatusFlags
+                    & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
+            ItemInfoWithIcon appInfo = (ItemInfoWithIcon) info;
+            mContext.startActivity(new PackageManagerHelper(mContext)
+                    .getMarketIntent(appInfo.getTargetComponent().getPackageName()));
             return;
         }
         ComponentName componentName = null;
@@ -319,4 +323,12 @@
         }
         return false;
     }
+
+    /** Returns the incremental download progress for the given shortcut's app. */
+    public static int getLoadingProgress(LauncherActivityInfo info) {
+        if (Utilities.ATLEAST_S) {
+            return (int) (100 * info.getLoadingProgress());
+        }
+        return 100;
+    }
 }