Version-2: Prioritize the session-provided icon & label for archived apps during unarchival in the iconCache.
* Also ensures that apps are sorted based on their actual name, so that
they don't jump around when "Pending.." switches to "Downloading.."
* In case of faillure during unarchival, icons shown are reverted to that of PM supplied ones.
New UI: http://recall/-/gMbThhDGagWFqnJTbQCqSz/fPuzxUuU7cGXCNdygMkXAB
Test: atest CacheDataUpdatedTaskTest.java and locally verified.
Bug: 319495216
Flag: ACONFIG com.android.launcher3.enable_support_for_archiving TRUNKFOOD
Change-Id: I6410482706af900e273fdc6f7cf0b0692442364c
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index d124746..99fca62 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -441,17 +441,35 @@
@Override
public void execute(@NonNull final LauncherAppState app,
@NonNull final BgDataModel dataModel, @NonNull final AllAppsList apps) {
+ IconCache iconCache = app.getIconCache();
final IntSet removedIds = new IntSet();
+ HashSet<WorkspaceItemInfo> archivedItemsToCacheRefresh = new HashSet<>();
+ HashSet<String> archivedPackagesToCacheRefresh = new HashSet<>();
synchronized (dataModel) {
for (ItemInfo info : dataModel.itemsIdMap) {
if (info instanceof WorkspaceItemInfo
&& ((WorkspaceItemInfo) info).hasPromiseIconUi()
&& user.equals(info.user)
- && info.getIntent() != null
- && TextUtils.equals(packageName, info.getIntent().getPackage())) {
- removedIds.add(info.id);
+ && info.getIntent() != null) {
+ if (TextUtils.equals(packageName, info.getIntent().getPackage())) {
+ removedIds.add(info.id);
+ }
+ if (((WorkspaceItemInfo) info).isArchived()) {
+ WorkspaceItemInfo workspaceItem = (WorkspaceItemInfo) info;
+ // Remove package cache icon for archived app in case of a session
+ // failure.
+ mApp.getIconCache().removeIconsForPkg(packageName, user);
+ // Refresh icons on the workspace for archived apps.
+ iconCache.getTitleAndIcon(workspaceItem,
+ workspaceItem.usingLowResIcon());
+ archivedPackagesToCacheRefresh.add(packageName);
+ archivedItemsToCacheRefresh.add(workspaceItem);
+ }
}
}
+ if (!archivedPackagesToCacheRefresh.isEmpty()) {
+ apps.updateIconsAndLabels(archivedPackagesToCacheRefresh, user);
+ }
}
if (!removedIds.isEmpty()) {
@@ -459,6 +477,10 @@
ItemInfoMatcher.ofItemIds(removedIds),
"removed because install session failed");
}
+ if (!archivedItemsToCacheRefresh.isEmpty()) {
+ bindUpdatedWorkspaceItems(archivedItemsToCacheRefresh.stream().toList());
+ bindApplicationsIfNeeded();
+ }
}
});
}
diff --git a/src/com/android/launcher3/allapps/AppInfoComparator.java b/src/com/android/launcher3/allapps/AppInfoComparator.java
index 311a40e..a0867db 100644
--- a/src/com/android/launcher3/allapps/AppInfoComparator.java
+++ b/src/com/android/launcher3/allapps/AppInfoComparator.java
@@ -43,9 +43,7 @@
@Override
public int compare(AppInfo a, AppInfo b) {
// Order by the title in the current locale
- int result = mLabelComparator.compare(
- a.title == null ? "" : a.title.toString(),
- b.title == null ? "" : b.title.toString());
+ int result = mLabelComparator.compare(getSortingTitle(a), getSortingTitle(b));
if (result != 0) {
return result;
}
@@ -64,4 +62,14 @@
return aUserSerial.compareTo(bUserSerial);
}
}
+
+ private String getSortingTitle(AppInfo info) {
+ if (info.appTitle != null) {
+ return info.appTitle.toString();
+ }
+ if (info.title != null) {
+ return info.title.toString();
+ }
+ return "";
+ }
}
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 2f7f51e..d8fa90a 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -219,7 +219,19 @@
CacheEntry entry = cacheLocked(application.componentName,
application.user, () -> null, mLauncherActivityInfoCachingLogic,
false, application.usingLowResIcon());
- if (entry.bitmap != null && !isDefaultIcon(entry.bitmap, application.user)) {
+ if (entry.bitmap == null || isDefaultIcon(entry.bitmap, application.user)) {
+ return;
+ }
+
+ boolean preferPackageIcon = application.isArchived();
+ if (preferPackageIcon) {
+ String packageName = application.getTargetPackage();
+ CacheEntry packageEntry =
+ cacheLocked(new ComponentName(packageName, packageName + EMPTY_CLASS_NAME),
+ application.user, () -> null, mLauncherActivityInfoCachingLogic,
+ false, application.usingLowResIcon());
+ applyPackageEntry(packageEntry, application, entry);
+ } else {
applyCacheEntry(entry, application);
}
}
@@ -227,10 +239,14 @@
/**
* Fill in {@param info} with the icon and label for {@param activityInfo}
*/
+ @SuppressWarnings("NewApi")
public synchronized void getTitleAndIcon(ItemInfoWithIcon info,
LauncherActivityInfo activityInfo, boolean useLowResIcon) {
+ boolean isAppArchived = Utilities.enableSupportForArchiving() && activityInfo != null
+ && activityInfo.getActivityInfo().isArchived;
// If we already have activity info, no need to use package icon
- getTitleAndIcon(info, () -> activityInfo, false, useLowResIcon);
+ getTitleAndIcon(info, () -> activityInfo, isAppArchived, useLowResIcon,
+ isAppArchived);
}
/**
@@ -309,7 +325,7 @@
} else {
Intent intent = info.getIntent();
getTitleAndIcon(info, () -> mLauncherApps.resolveActivity(intent, info.user),
- true, useLowResIcon);
+ true, useLowResIcon, info.isArchived());
}
}
@@ -334,6 +350,28 @@
}
/**
+ * Fill in {@param mWorkspaceItemInfo} with the icon and label for {@param info}
+ */
+ public synchronized void getTitleAndIcon(
+ @NonNull ItemInfoWithIcon infoInOut,
+ @NonNull Supplier<LauncherActivityInfo> activityInfoProvider,
+ boolean usePkgIcon, boolean useLowResIcon, boolean preferPackageEntry) {
+ CacheEntry entry = cacheLocked(infoInOut.getTargetComponent(), infoInOut.user,
+ activityInfoProvider, mLauncherActivityInfoCachingLogic, usePkgIcon,
+ useLowResIcon);
+ if (preferPackageEntry) {
+ String packageName = infoInOut.getTargetPackage();
+ CacheEntry packageEntry = cacheLocked(
+ new ComponentName(packageName, packageName + EMPTY_CLASS_NAME),
+ infoInOut.user, activityInfoProvider, mLauncherActivityInfoCachingLogic,
+ usePkgIcon, useLowResIcon);
+ applyPackageEntry(packageEntry, infoInOut, entry);
+ } else {
+ applyCacheEntry(entry, infoInOut);
+ }
+ }
+
+ /**
* Creates an sql cursor for a query of a set of ItemInfoWithIcon icons and titles.
*
* @param iconRequestInfos List of IconRequestInfos representing titles and icons to query.
@@ -551,6 +589,19 @@
}
}
+ protected void applyPackageEntry(@NonNull final CacheEntry packageEntry,
+ @NonNull final ItemInfoWithIcon info, @NonNull final CacheEntry fallbackEntry) {
+ info.title = Utilities.trim(packageEntry.title);
+ info.appTitle = Utilities.trim(fallbackEntry.title);
+ info.contentDescription = packageEntry.contentDescription;
+ info.bitmap = packageEntry.bitmap;
+ if (packageEntry.bitmap == null) {
+ // TODO: entry.bitmap can never be null, so this should not happen at all.
+ Log.wtf(TAG, "Cannot find bitmap from the cache, default icon was loaded.");
+ info.bitmap = getDefaultIcon(info.user);
+ }
+ }
+
public Drawable getFullResIcon(LauncherActivityInfo info) {
return mIconProvider.getIcon(info, mIconDpi);
}
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index 86393a0..55849c2 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -160,6 +160,13 @@
public CharSequence title;
/**
+ * Optionally set: The appTitle might e.g. be different if {@code title} is used to
+ * display progress (e.g. Downloading..).
+ */
+ @Nullable
+ public CharSequence appTitle;
+
+ /**
* Content description of the item.
*/
@Nullable