Loading src/com/android/launcher3/BubbleTextView.java +10 −5 Original line number Diff line number Diff line Loading @@ -933,9 +933,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, if (mIcon instanceof PreloadIconDrawable) { preloadIconDrawable = (PreloadIconDrawable) mIcon; preloadIconDrawable.setLevel(progressLevel); // TODO(b/302115555): For archived apps, show icon as disabled if active session // exists. preloadIconDrawable.setIsDisabled(info.getProgressLevel() == 0); preloadIconDrawable.setIsDisabled(isIconDisabled(info)); } else { preloadIconDrawable = makePreloadIcon(); setIcon(preloadIconDrawable); Loading @@ -960,11 +958,18 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, final PreloadIconDrawable preloadDrawable = newPendingIcon(getContext(), info); preloadDrawable.setLevel(progressLevel); // TODO(b/302115555): For archived apps, show icon as disabled if active session exists. preloadDrawable.setIsDisabled(info.getProgressLevel() == 0); preloadDrawable.setIsDisabled(isIconDisabled(info)); return preloadDrawable; } private boolean isIconDisabled(ItemInfoWithIcon info) { if (info.isArchived()) { return info.getProgressLevel() == 0 && (info.runtimeStatusFlags & FLAG_INSTALL_SESSION_ACTIVE) != 0; } return info.getProgressLevel() == 0; } public void applyDotState(ItemInfo itemInfo, boolean animate) { if (mIcon instanceof FastBitmapDrawable) { boolean wasDotted = mDotInfo != null; Loading src/com/android/launcher3/model/AllAppsList.java +17 −4 Original line number Diff line number Diff line Loading @@ -16,8 +16,10 @@ package com.android.launcher3.model; import static com.android.launcher3.Flags.enableSupportForArchiving; import static com.android.launcher3.model.data.AppInfo.COMPONENT_KEY_COMPARATOR; import static com.android.launcher3.model.data.AppInfo.EMPTY_ARRAY; import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_ARCHIVED; import android.content.ComponentName; import android.content.Context; Loading Loading @@ -53,6 +55,7 @@ import java.util.function.Predicate; /** * Stores the list of all applications for the all apps view. */ @SuppressWarnings("NewApi") public class AllAppsList { private static final String TAG = "AllAppsList"; Loading Loading @@ -200,13 +203,18 @@ public class AllAppsList { if (tgtComp != null && tgtComp.getPackageName().equals(installInfo.packageName) && appInfo.user.equals(user)) { if (installInfo.state == PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING || installInfo.state == PackageInstallInfo.STATUS_INSTALLING) { || installInfo.state == PackageInstallInfo.STATUS_INSTALLING // In case unarchival fails, we would want to keep the icon and update // back the progress to 0 for the all apps view without removing the // icon, which is contrary to what happens during normal app installation // flow. || (installInfo.state == PackageInstallInfo.STATUS_FAILED && appInfo.isArchived())) { if (appInfo.isAppStartable() && installInfo.state == PackageInstallInfo.STATUS_INSTALLING) { && installInfo.state == PackageInstallInfo.STATUS_INSTALLING && !appInfo.isArchived()) { continue; } // TODO(b/302115555): Handle the case when archived apps are to be updated // during unarchival start. appInfo.setProgressLevel(installInfo); updatedAppInfos.add(appInfo); Loading Loading @@ -322,6 +330,11 @@ public class AllAppsList { PackageManagerHelper.getLoadingProgress(info), PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING); applicationInfo.intent = launchIntent; if (enableSupportForArchiving() && info.getActivityInfo().isArchived) { // In case an app is archived, the respective item flag corresponding to // archiving should also be applied during package updates applicationInfo.runtimeStatusFlags |= FLAG_ARCHIVED; } mDataChanged = true; } Loading src/com/android/launcher3/model/LoaderTask.java +22 −3 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.launcher3.model; import static com.android.launcher3.BuildConfig.WIDGET_ON_FIRST_SCREEN; import static com.android.launcher3.Flags.enableLauncherBrMetricsFixed; import static com.android.launcher3.Flags.enableSupportForArchiving; import static com.android.launcher3.LauncherPrefs.IS_FIRST_LOAD_AFTER_RESTORE; import static com.android.launcher3.LauncherPrefs.SHOULD_SHOW_SMARTSPACE; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR; Loading @@ -30,6 +31,7 @@ import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED; import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_WORK_PROFILE_QUIET_MODE_ENABLED; import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems; import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission; Loading Loading @@ -143,6 +145,7 @@ public class LoaderTask implements Runnable { private final UserManagerState mUserManagerState; protected final Map<ComponentKey, AppWidgetProviderInfo> mWidgetProvidersMap = new ArrayMap<>(); private Map<ShortcutKey, ShortcutInfo> mShortcutKeyToPinnedShortcuts; private HashMap<PackageUserKey, SessionInfo> mInstallingPkgsCached; private boolean mStopped; Loading Loading @@ -170,6 +173,7 @@ public class LoaderTask implements Runnable { mSessionHelper = InstallSessionHelper.INSTANCE.get(mApp.getContext()); mIconCache = mApp.getIconCache(); mUserManagerState = userManagerState; mInstallingPkgsCached = null; } protected synchronized void waitForIdle() { Loading Loading @@ -417,6 +421,9 @@ public class LoaderTask implements Runnable { final HashMap<PackageUserKey, SessionInfo> installingPkgs = mSessionHelper.getActiveSessions(); if (enableSupportForArchiving()) { mInstallingPkgsCached = installingPkgs; } installingPkgs.forEach(mApp.getIconCache()::updateSessionCache); FileLog.d(TAG, "loadWorkspace: Packages with active install sessions: " + installingPkgs.keySet().stream().map(info -> info.mPackageName).toList()); Loading Loading @@ -649,8 +656,20 @@ public class LoaderTask implements Runnable { for (int i = 0; i < apps.size(); i++) { LauncherActivityInfo app = apps.get(i); AppInfo appInfo = new AppInfo(app, user, quietMode); // TODO(b/302115555): Handle the case when archived apps with active sessions are // loaded. if (enableSupportForArchiving() && app.getApplicationInfo().isArchived) { // For archived apps, include progress info in case there is a pending // install session post restart of device. String appPackageName = app.getApplicationInfo().packageName; SessionInfo si = mInstallingPkgsCached != null ? mInstallingPkgsCached.get( new PackageUserKey(appPackageName, user)) : mSessionHelper.getActiveSessionInfo(user, appPackageName); if (si != null) { appInfo.runtimeStatusFlags |= FLAG_INSTALL_SESSION_ACTIVE; appInfo.setProgressLevel((int) (si.getProgress() * 100), PackageInstallInfo.STATUS_INSTALLING); } } iconRequestInfos.add(new IconRequestInfo<>( appInfo, app, /* useLowResIcon= */ false)); Loading tests/src/com/android/launcher3/compat/TaplPromiseIconUiTest.java +17 −6 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.tapl.AllApps; import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator; import com.android.launcher3.util.TestUtil; Loading Loading @@ -143,16 +144,26 @@ public class TaplPromiseIconUiTest extends AbstractLauncherUiTest { assertThat(mDevice.executeShellCommand(String.format("pm archive %s", DUMMY_PACKAGE))) .isEqualTo("Success\n"); final ItemOperator findPromiseApp = (info, view) -> info != null && TextUtils.equals(info.title, DUMMY_LABEL); // Create and add test session mSessionId = createSession(DUMMY_PACKAGE, /* label= */ "", Bitmap.createBitmap(100, 100, Bitmap.Config.ALPHA_8)); // Verify promise icon is added waitForLauncherCondition("Test Promise App not found on workspace", launcher -> launcher.getWorkspace().getFirstMatch(findPromiseApp) != null); // Verify promise icon is added to all apps view. The icon may not be added to the // workspace even if there might be no icon present for archived app. But icon will // always be in all apps view. In case an icon is not added, an exception would be thrown. final AllApps allApps = mLauncher.getWorkspace().switchToAllApps(); // Wait for the promise icon to be added. waitForLauncherCondition( DUMMY_PACKAGE + " app was not found on all apps after being archived", launcher -> { try { allApps.getAppIcon(DUMMY_LABEL); } catch (Throwable t) { return false; } return true; }); // Remove session mTargetContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId); Loading Loading
src/com/android/launcher3/BubbleTextView.java +10 −5 Original line number Diff line number Diff line Loading @@ -933,9 +933,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, if (mIcon instanceof PreloadIconDrawable) { preloadIconDrawable = (PreloadIconDrawable) mIcon; preloadIconDrawable.setLevel(progressLevel); // TODO(b/302115555): For archived apps, show icon as disabled if active session // exists. preloadIconDrawable.setIsDisabled(info.getProgressLevel() == 0); preloadIconDrawable.setIsDisabled(isIconDisabled(info)); } else { preloadIconDrawable = makePreloadIcon(); setIcon(preloadIconDrawable); Loading @@ -960,11 +958,18 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, final PreloadIconDrawable preloadDrawable = newPendingIcon(getContext(), info); preloadDrawable.setLevel(progressLevel); // TODO(b/302115555): For archived apps, show icon as disabled if active session exists. preloadDrawable.setIsDisabled(info.getProgressLevel() == 0); preloadDrawable.setIsDisabled(isIconDisabled(info)); return preloadDrawable; } private boolean isIconDisabled(ItemInfoWithIcon info) { if (info.isArchived()) { return info.getProgressLevel() == 0 && (info.runtimeStatusFlags & FLAG_INSTALL_SESSION_ACTIVE) != 0; } return info.getProgressLevel() == 0; } public void applyDotState(ItemInfo itemInfo, boolean animate) { if (mIcon instanceof FastBitmapDrawable) { boolean wasDotted = mDotInfo != null; Loading
src/com/android/launcher3/model/AllAppsList.java +17 −4 Original line number Diff line number Diff line Loading @@ -16,8 +16,10 @@ package com.android.launcher3.model; import static com.android.launcher3.Flags.enableSupportForArchiving; import static com.android.launcher3.model.data.AppInfo.COMPONENT_KEY_COMPARATOR; import static com.android.launcher3.model.data.AppInfo.EMPTY_ARRAY; import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_ARCHIVED; import android.content.ComponentName; import android.content.Context; Loading Loading @@ -53,6 +55,7 @@ import java.util.function.Predicate; /** * Stores the list of all applications for the all apps view. */ @SuppressWarnings("NewApi") public class AllAppsList { private static final String TAG = "AllAppsList"; Loading Loading @@ -200,13 +203,18 @@ public class AllAppsList { if (tgtComp != null && tgtComp.getPackageName().equals(installInfo.packageName) && appInfo.user.equals(user)) { if (installInfo.state == PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING || installInfo.state == PackageInstallInfo.STATUS_INSTALLING) { || installInfo.state == PackageInstallInfo.STATUS_INSTALLING // In case unarchival fails, we would want to keep the icon and update // back the progress to 0 for the all apps view without removing the // icon, which is contrary to what happens during normal app installation // flow. || (installInfo.state == PackageInstallInfo.STATUS_FAILED && appInfo.isArchived())) { if (appInfo.isAppStartable() && installInfo.state == PackageInstallInfo.STATUS_INSTALLING) { && installInfo.state == PackageInstallInfo.STATUS_INSTALLING && !appInfo.isArchived()) { continue; } // TODO(b/302115555): Handle the case when archived apps are to be updated // during unarchival start. appInfo.setProgressLevel(installInfo); updatedAppInfos.add(appInfo); Loading Loading @@ -322,6 +330,11 @@ public class AllAppsList { PackageManagerHelper.getLoadingProgress(info), PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING); applicationInfo.intent = launchIntent; if (enableSupportForArchiving() && info.getActivityInfo().isArchived) { // In case an app is archived, the respective item flag corresponding to // archiving should also be applied during package updates applicationInfo.runtimeStatusFlags |= FLAG_ARCHIVED; } mDataChanged = true; } Loading
src/com/android/launcher3/model/LoaderTask.java +22 −3 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.launcher3.model; import static com.android.launcher3.BuildConfig.WIDGET_ON_FIRST_SCREEN; import static com.android.launcher3.Flags.enableLauncherBrMetricsFixed; import static com.android.launcher3.Flags.enableSupportForArchiving; import static com.android.launcher3.LauncherPrefs.IS_FIRST_LOAD_AFTER_RESTORE; import static com.android.launcher3.LauncherPrefs.SHOULD_SHOW_SMARTSPACE; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR; Loading @@ -30,6 +31,7 @@ import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED; import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_WORK_PROFILE_QUIET_MODE_ENABLED; import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems; import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission; Loading Loading @@ -143,6 +145,7 @@ public class LoaderTask implements Runnable { private final UserManagerState mUserManagerState; protected final Map<ComponentKey, AppWidgetProviderInfo> mWidgetProvidersMap = new ArrayMap<>(); private Map<ShortcutKey, ShortcutInfo> mShortcutKeyToPinnedShortcuts; private HashMap<PackageUserKey, SessionInfo> mInstallingPkgsCached; private boolean mStopped; Loading Loading @@ -170,6 +173,7 @@ public class LoaderTask implements Runnable { mSessionHelper = InstallSessionHelper.INSTANCE.get(mApp.getContext()); mIconCache = mApp.getIconCache(); mUserManagerState = userManagerState; mInstallingPkgsCached = null; } protected synchronized void waitForIdle() { Loading Loading @@ -417,6 +421,9 @@ public class LoaderTask implements Runnable { final HashMap<PackageUserKey, SessionInfo> installingPkgs = mSessionHelper.getActiveSessions(); if (enableSupportForArchiving()) { mInstallingPkgsCached = installingPkgs; } installingPkgs.forEach(mApp.getIconCache()::updateSessionCache); FileLog.d(TAG, "loadWorkspace: Packages with active install sessions: " + installingPkgs.keySet().stream().map(info -> info.mPackageName).toList()); Loading Loading @@ -649,8 +656,20 @@ public class LoaderTask implements Runnable { for (int i = 0; i < apps.size(); i++) { LauncherActivityInfo app = apps.get(i); AppInfo appInfo = new AppInfo(app, user, quietMode); // TODO(b/302115555): Handle the case when archived apps with active sessions are // loaded. if (enableSupportForArchiving() && app.getApplicationInfo().isArchived) { // For archived apps, include progress info in case there is a pending // install session post restart of device. String appPackageName = app.getApplicationInfo().packageName; SessionInfo si = mInstallingPkgsCached != null ? mInstallingPkgsCached.get( new PackageUserKey(appPackageName, user)) : mSessionHelper.getActiveSessionInfo(user, appPackageName); if (si != null) { appInfo.runtimeStatusFlags |= FLAG_INSTALL_SESSION_ACTIVE; appInfo.setProgressLevel((int) (si.getProgress() * 100), PackageInstallInfo.STATUS_INSTALLING); } } iconRequestInfos.add(new IconRequestInfo<>( appInfo, app, /* useLowResIcon= */ false)); Loading
tests/src/com/android/launcher3/compat/TaplPromiseIconUiTest.java +17 −6 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.tapl.AllApps; import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator; import com.android.launcher3.util.TestUtil; Loading Loading @@ -143,16 +144,26 @@ public class TaplPromiseIconUiTest extends AbstractLauncherUiTest { assertThat(mDevice.executeShellCommand(String.format("pm archive %s", DUMMY_PACKAGE))) .isEqualTo("Success\n"); final ItemOperator findPromiseApp = (info, view) -> info != null && TextUtils.equals(info.title, DUMMY_LABEL); // Create and add test session mSessionId = createSession(DUMMY_PACKAGE, /* label= */ "", Bitmap.createBitmap(100, 100, Bitmap.Config.ALPHA_8)); // Verify promise icon is added waitForLauncherCondition("Test Promise App not found on workspace", launcher -> launcher.getWorkspace().getFirstMatch(findPromiseApp) != null); // Verify promise icon is added to all apps view. The icon may not be added to the // workspace even if there might be no icon present for archived app. But icon will // always be in all apps view. In case an icon is not added, an exception would be thrown. final AllApps allApps = mLauncher.getWorkspace().switchToAllApps(); // Wait for the promise icon to be added. waitForLauncherCondition( DUMMY_PACKAGE + " app was not found on all apps after being archived", launcher -> { try { allApps.getAppIcon(DUMMY_LABEL); } catch (Throwable t) { return false; } return true; }); // Remove session mTargetContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId); Loading