From 04fe042e838e7a0e5e155985d1c1fff4d8336ea9 Mon Sep 17 00:00:00 2001 From: Rohit Goyal Date: Tue, 9 Jan 2024 21:23:55 +0530 Subject: [PATCH] Add support for progress bar for archived apps in AllApps view. Working Video: https://drive.google.com/file/d/1-cSD63FQLmqyeTkUuXqcSsjb03m31ULO/view?usp=sharing Test: TaplPromiseIconUiTest Bug: 302115555 Bug: 317108448 Flag: ACONFIG com.android.launcher3.enable_support_for_archiving DEVELOPMENT Change-Id: Iebaa338789430c5e0a004bd8b05bdbda87cd986e --- src/com/android/launcher3/BubbleTextView.java | 15 +++++++---- .../android/launcher3/model/AllAppsList.java | 21 +++++++++++++--- .../android/launcher3/model/LoaderTask.java | 25 ++++++++++++++++--- .../compat/TaplPromiseIconUiTest.java | 23 ++++++++++++----- 4 files changed, 66 insertions(+), 18 deletions(-) diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 5b497f26eb..ba65bea5b0 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -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); @@ -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; diff --git a/src/com/android/launcher3/model/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java index 7cbfc37f07..ccb89004e3 100644 --- a/src/com/android/launcher3/model/AllAppsList.java +++ b/src/com/android/launcher3/model/AllAppsList.java @@ -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; @@ -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"; @@ -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); @@ -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; } diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index 795450a247..922b14e1d2 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -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; @@ -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; @@ -143,6 +145,7 @@ public class LoaderTask implements Runnable { private final UserManagerState mUserManagerState; protected final Map mWidgetProvidersMap = new ArrayMap<>(); private Map mShortcutKeyToPinnedShortcuts; + private HashMap mInstallingPkgsCached; private boolean mStopped; @@ -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() { @@ -253,7 +257,7 @@ public class LoaderTask implements Runnable { Trace.beginSection("LoadAllApps"); List allActivityList; try { - allActivityList = loadAllApps(); + allActivityList = loadAllApps(); } finally { Trace.endSection(); } @@ -418,6 +422,9 @@ public class LoaderTask implements Runnable { final HashMap 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()); @@ -651,8 +658,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)); diff --git a/tests/src/com/android/launcher3/compat/TaplPromiseIconUiTest.java b/tests/src/com/android/launcher3/compat/TaplPromiseIconUiTest.java index bfa0d34abb..f1b044129a 100644 --- a/tests/src/com/android/launcher3/compat/TaplPromiseIconUiTest.java +++ b/tests/src/com/android/launcher3/compat/TaplPromiseIconUiTest.java @@ -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; @@ -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);