Merge "Consider tasks with INVALID_DISPLAY as it's on DEFAULT_DISPLAY" into main
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 17fb959..36185b1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -115,9 +115,8 @@
private static final Uri NAV_BAR_KIDS_MODE = Settings.Secure.getUriFor(
Settings.Secure.NAV_BAR_KIDS_MODE);
- private final Context mWindowContext;
+ private final Context mParentContext;
private final @Nullable Context mNavigationBarPanelContext;
- private WindowManager mWindowManager;
private final TaskbarNavButtonController mDefaultNavButtonController;
private final ComponentCallbacks mDefaultComponentCallbacks;
@@ -132,6 +131,8 @@
new NonDestroyableScopedUnfoldTransitionProgressProvider();
/** DisplayId - {@link TaskbarActivityContext} map for Connected Display. */
private final SparseArray<TaskbarActivityContext> mTaskbars = new SparseArray<>();
+ /** DisplayId - {@link Context} map for Connected Display. */
+ private final SparseArray<Context> mWindowContexts = new SparseArray<>();
/** DisplayId - {@link FrameLayout} map for Connected Display. */
private final SparseArray<FrameLayout> mRootLayouts = new SparseArray<>();
/** DisplayId - {@link Boolean} map indicating if RootLayout was added to window. */
@@ -243,36 +244,35 @@
Context context,
AllAppsActionManager allAppsActionManager,
TaskbarNavButtonCallbacks navCallbacks) {
- Display display =
- context.getSystemService(DisplayManager.class).getDisplay(context.getDisplayId());
- mWindowContext = context.createWindowContext(display,
- ENABLE_TASKBAR_NAVBAR_UNIFICATION ? TYPE_NAVIGATION_BAR : TYPE_NAVIGATION_BAR_PANEL,
- null);
+ mParentContext = context;
+ createWindowContext(context.getDisplayId());
mAllAppsActionManager = allAppsActionManager;
+ Display display = context.getSystemService(DisplayManager.class).getDisplay(
+ getDefaultDisplayId());
mNavigationBarPanelContext = ENABLE_TASKBAR_NAVBAR_UNIFICATION
? context.createWindowContext(display, TYPE_NAVIGATION_BAR_PANEL, null)
: null;
if (enableTaskbarNoRecreate()) {
- mWindowManager = mWindowContext.getSystemService(WindowManager.class);
createTaskbarRootLayout(getDefaultDisplayId());
}
mDefaultNavButtonController = createDefaultNavButtonController(context, navCallbacks);
mDefaultComponentCallbacks = createDefaultComponentCallbacks();
- SettingsCache.INSTANCE.get(mWindowContext)
+ SettingsCache.INSTANCE.get(getPrimaryWindowContext())
.register(USER_SETUP_COMPLETE_URI, mOnSettingsChangeListener);
- SettingsCache.INSTANCE.get(mWindowContext)
+ SettingsCache.INSTANCE.get(getPrimaryWindowContext())
.register(NAV_BAR_KIDS_MODE, mOnSettingsChangeListener);
Log.d(TASKBAR_NOT_DESTROYED_TAG, "registering component callbacks from constructor.");
- mWindowContext.registerComponentCallbacks(mDefaultComponentCallbacks);
- mShutdownReceiver.register(mWindowContext, Intent.ACTION_SHUTDOWN);
+ getPrimaryWindowContext().registerComponentCallbacks(mDefaultComponentCallbacks);
+ mShutdownReceiver.register(getPrimaryWindowContext(), Intent.ACTION_SHUTDOWN);
UI_HELPER_EXECUTOR.execute(() -> {
mSharedState.taskbarSystemActionPendingIntent = PendingIntent.getBroadcast(
- mWindowContext,
+ getPrimaryWindowContext(),
SYSTEM_ACTION_ID_TASKBAR,
- new Intent(ACTION_SHOW_TASKBAR).setPackage(mWindowContext.getPackageName()),
+ new Intent(ACTION_SHOW_TASKBAR).setPackage(
+ getPrimaryWindowContext().getPackageName()),
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
mTaskbarBroadcastReceiver.register(
- mWindowContext, RECEIVER_NOT_EXPORTED, ACTION_SHOW_TASKBAR);
+ getPrimaryWindowContext(), RECEIVER_NOT_EXPORTED, ACTION_SHOW_TASKBAR);
});
debugWhyTaskbarNotDestroyed("TaskbarManager created");
@@ -285,14 +285,15 @@
return new TaskbarNavButtonController(
context,
navCallbacks,
- SystemUiProxy.INSTANCE.get(mWindowContext),
+ SystemUiProxy.INSTANCE.get(getPrimaryWindowContext()),
new Handler(),
- new ContextualSearchInvoker(mWindowContext));
+ new ContextualSearchInvoker(getPrimaryWindowContext()));
}
private ComponentCallbacks createDefaultComponentCallbacks() {
return new ComponentCallbacks() {
- private Configuration mOldConfig = mWindowContext.getResources().getConfiguration();
+ private Configuration mOldConfig =
+ getPrimaryWindowContext().getResources().getConfiguration();
@Override
public void onConfigurationChanged(Configuration newConfig) {
@@ -302,7 +303,8 @@
"TaskbarManager#mComponentCallbacks.onConfigurationChanged: " + newConfig);
// TODO: adapt this logic to be specific to different displays.
DeviceProfile dp = mUserUnlocked
- ? LauncherAppState.getIDP(mWindowContext).getDeviceProfile(mWindowContext)
+ ? LauncherAppState.getIDP(getPrimaryWindowContext()).getDeviceProfile(
+ getPrimaryWindowContext())
: null;
int configDiff = mOldConfig.diff(newConfig) & ~SKIP_RECREATE_CONFIG_CHANGES;
@@ -354,6 +356,7 @@
int displayId = mTaskbars.keyAt(i);
destroyTaskbarForDisplay(displayId);
removeTaskbarRootViewFromWindow(displayId);
+ removeWindowContextFromMap(displayId);
}
}
@@ -372,7 +375,8 @@
}
// make this display-specific
DeviceProfile dp = mUserUnlocked ?
- LauncherAppState.getIDP(mWindowContext).getDeviceProfile(mWindowContext) : null;
+ LauncherAppState.getIDP(getWindowContext(displayId)).getDeviceProfile(
+ getWindowContext(displayId)) : null;
if (dp == null || !isTaskbarEnabled(dp)) {
removeTaskbarRootViewFromWindow(displayId);
}
@@ -419,7 +423,8 @@
*/
public void onUserUnlocked() {
mUserUnlocked = true;
- DisplayController.INSTANCE.get(mWindowContext).addChangeListener(mRecreationListener);
+ DisplayController.INSTANCE.get(getPrimaryWindowContext()).addChangeListener(
+ mRecreationListener);
recreateTaskbar();
addTaskbarRootViewToWindow(getDefaultDisplayId());
}
@@ -482,7 +487,8 @@
return ql.getUnfoldTransitionProgressProvider();
}
} else {
- return SystemUiProxy.INSTANCE.get(mWindowContext).getUnfoldTransitionProvider();
+ return SystemUiProxy.INSTANCE.get(
+ getPrimaryWindowContext()).getUnfoldTransitionProvider();
}
return null;
}
@@ -528,7 +534,8 @@
Log.d(ILLEGAL_ARGUMENT_WM_ADD_VIEW, "recreateTaskbarForDisplay: " + displayId);
// TODO: make this code display specific
DeviceProfile dp = mUserUnlocked ?
- LauncherAppState.getIDP(mWindowContext).getDeviceProfile(mWindowContext) : null;
+ LauncherAppState.getIDP(getWindowContext(displayId)).getDeviceProfile(
+ getWindowContext(displayId)) : null;
// All Apps action is unrelated to navbar unification, so we only need to check DP.
final boolean isLargeScreenTaskbar = dp != null && dp.isTaskbarPresent;
@@ -542,7 +549,7 @@
+ " FLAG_HIDE_NAVBAR_WINDOW=" + ENABLE_TASKBAR_NAVBAR_UNIFICATION
+ " dp.isTaskbarPresent=" + (dp == null ? "null" : dp.isTaskbarPresent));
if (!isTaskbarEnabled || !isLargeScreenTaskbar) {
- SystemUiProxy.INSTANCE.get(mWindowContext)
+ SystemUiProxy.INSTANCE.get(getPrimaryWindowContext())
.notifyTaskbarStatus(/* visible */ false, /* stashed */ false);
if (!isTaskbarEnabled) {
return;
@@ -754,23 +761,24 @@
mRecentsViewContainer = null;
debugWhyTaskbarNotDestroyed("TaskbarManager#destroy()");
removeActivityCallbacksAndListeners();
- mTaskbarBroadcastReceiver.unregisterReceiverSafely(mWindowContext);
- destroyAllTaskbars();
+ mTaskbarBroadcastReceiver.unregisterReceiverSafely(getPrimaryWindowContext());
+
if (mUserUnlocked) {
- DisplayController.INSTANCE.get(mWindowContext).removeChangeListener(
+ DisplayController.INSTANCE.get(getPrimaryWindowContext()).removeChangeListener(
mRecreationListener);
}
- SettingsCache.INSTANCE.get(mWindowContext)
+ SettingsCache.INSTANCE.get(getPrimaryWindowContext())
.unregister(USER_SETUP_COMPLETE_URI, mOnSettingsChangeListener);
- SettingsCache.INSTANCE.get(mWindowContext)
+ SettingsCache.INSTANCE.get(getPrimaryWindowContext())
.unregister(NAV_BAR_KIDS_MODE, mOnSettingsChangeListener);
Log.d(TASKBAR_NOT_DESTROYED_TAG, "unregistering component callbacks from destroy().");
- mWindowContext.unregisterComponentCallbacks(mDefaultComponentCallbacks);
- mShutdownReceiver.unregisterReceiverSafely(mWindowContext);
+ getPrimaryWindowContext().unregisterComponentCallbacks(mDefaultComponentCallbacks);
+ mShutdownReceiver.unregisterReceiverSafely(getPrimaryWindowContext());
+ destroyAllTaskbars();
}
public @Nullable TaskbarActivityContext getCurrentActivityContext() {
- return getTaskbarForDisplay(mWindowContext.getDisplayId());
+ return getTaskbarForDisplay(getDefaultDisplayId());
}
public void dumpLogs(String prefix, PrintWriter pw) {
@@ -786,7 +794,6 @@
taskbar.dumpLogs(prefix + "\t\t", pw);
}
}
-
}
private void addTaskbarRootViewToWindow(int displayId) {
@@ -800,8 +807,7 @@
if (!isTaskbarRootLayoutAddedForDisplay(displayId)) {
FrameLayout rootLayout = getTaskbarRootLayoutForDisplay(displayId);
if (rootLayout != null) {
- mWindowManager.addView(getTaskbarRootLayoutForDisplay(displayId),
- taskbar.getWindowLayoutParams());
+ getWindowManager(displayId).addView(rootLayout, taskbar.getWindowLayoutParams());
mAddedRootLayouts.put(displayId, true);
} else {
Log.d(ILLEGAL_ARGUMENT_WM_ADD_VIEW,
@@ -822,7 +828,7 @@
}
if (isTaskbarRootLayoutAddedForDisplay(displayId)) {
- mWindowManager.removeViewImmediate(rootLayout);
+ getWindowManager(displayId).removeViewImmediate(rootLayout);
mAddedRootLayouts.put(displayId, false);
removeTaskbarRootLayoutFromMap(displayId);
}
@@ -855,10 +861,10 @@
* Creates a {@link TaskbarActivityContext} for the given display and adds it to the map.
*/
private TaskbarActivityContext createTaskbarActivityContext(DeviceProfile dp, int displayId) {
- TaskbarActivityContext newTaskbar = new TaskbarActivityContext(mWindowContext,
+ TaskbarActivityContext newTaskbar = new TaskbarActivityContext(getWindowContext(displayId),
mNavigationBarPanelContext, dp, mDefaultNavButtonController,
mUnfoldProgressProvider, isDefaultDisplay(displayId),
- SystemUiProxy.INSTANCE.get(mWindowContext));
+ SystemUiProxy.INSTANCE.get(getPrimaryWindowContext()));
addTaskbarToMap(displayId, newTaskbar);
return newTaskbar;
@@ -892,7 +898,7 @@
*/
private void createTaskbarRootLayout(int displayId) {
Log.d(ILLEGAL_ARGUMENT_WM_ADD_VIEW, "createTaskbarRootLayout: " + displayId);
- FrameLayout newTaskbarRootLayout = new FrameLayout(mWindowContext) {
+ FrameLayout newTaskbarRootLayout = new FrameLayout(getWindowContext(displayId)) {
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// The motion events can be outside the view bounds of task bar, and hence
@@ -958,8 +964,75 @@
Log.d(NULL_TASKBAR_ROOT_LAYOUT_TAG, "mRootLayouts.size()=" + mRootLayouts.size());
}
+ /**
+ * Creates {@link Context} for the taskbar on the specified display and›› adds it to map.
+ * @param displayId The ID of the display for which to create the window context.
+ */
+ private void createWindowContext(int displayId) {
+ DisplayManager displayManager = mParentContext.getSystemService(DisplayManager.class);
+ if (displayManager == null) {
+ return;
+ }
+
+ Display display = displayManager.getDisplay(displayId);
+ if (display != null) {
+ int windowType = (ENABLE_TASKBAR_NAVBAR_UNIFICATION && isDefaultDisplay(displayId))
+ ? TYPE_NAVIGATION_BAR : TYPE_NAVIGATION_BAR_PANEL;
+ Context newContext = mParentContext.createWindowContext(display, windowType, null);
+ addWindowContextToMap(displayId, newContext);
+ }
+ }
+
+ /**
+ * Retrieves the window context of the taskbar for the specified display.
+ *
+ * @param displayId The ID of the display for which to retrieve the window context.
+ * @return The Window Context {@link Context} for a given display or {@code null}.
+ */
+ private Context getWindowContext(int displayId) {
+ return mWindowContexts.get(displayId);
+ }
+
+ @VisibleForTesting
+ public Context getPrimaryWindowContext() {
+ return getWindowContext(getDefaultDisplayId());
+ }
+
+ /**
+ * Retrieves the window manager {@link WindowManager} of the taskbar for the specified display.
+ *
+ * @param displayId The ID of the display for which to retrieve the window manager.
+ * @return The window manager {@link WindowManager} for a given display or {@code null}.
+ */
+ private WindowManager getWindowManager(int displayId) {
+ return getWindowContext(displayId).getSystemService(WindowManager.class);
+ }
+
+ /**
+ * Adds the window context {@link Context} to taskbar map, mapped to display ID.
+ *
+ * @param displayId The ID of the display to associate with the taskbar root layout.
+ * @param windowContext The window context {@link Context} to add to the map.
+ */
+ private void addWindowContextToMap(int displayId, @NonNull Context windowContext) {
+ if (!mWindowContexts.contains(displayId)) {
+ mWindowContexts.put(displayId, windowContext);
+ }
+ }
+
+ /**
+ * Removes the window context {@link Context} for given display ID from the taskbar map.
+ *
+ * @param displayId The ID of the display for which to remove the taskbar root layout.
+ */
+ private void removeWindowContextFromMap(int displayId) {
+ if (mWindowContexts.contains(displayId)) {
+ mWindowContexts.delete(displayId);
+ }
+ }
+
private int getDefaultDisplayId() {
- return mWindowContext.getDisplayId();
+ return mParentContext.getDisplayId();
}
/** Temp logs for b/254119092. */
@@ -974,9 +1047,14 @@
boolean activityTaskbarPresent = mActivity != null
&& mActivity.getDeviceProfile().isTaskbarPresent;
- // TODO: make this display specific
- boolean contextTaskbarPresent = mUserUnlocked && LauncherAppState.getIDP(mWindowContext)
- .getDeviceProfile(mWindowContext).isTaskbarPresent;
+ Context windowContext = getWindowContext(displayId);
+ if (windowContext == null) {
+ log.add("window context for displayId" + displayId);
+ return;
+ }
+
+ boolean contextTaskbarPresent = mUserUnlocked && LauncherAppState.getIDP(windowContext)
+ .getDeviceProfile(windowContext).isTaskbarPresent;
if (activityTaskbarPresent == contextTaskbarPresent) {
log.add("mActivity and mWindowContext agree taskbarIsPresent=" + contextTaskbarPresent);
Log.d(TASKBAR_NOT_DESTROYED_TAG, log.toString());
@@ -993,12 +1071,12 @@
log.add("\t\tmActivity.getDeviceProfile().isTaskbarPresent="
+ activityTaskbarPresent);
}
- log.add("\tmWindowContext logs:");
- log.add("\t\tmWindowContext=" + mWindowContext);
- log.add("\t\tmWindowContext.getResources().getConfiguration()="
- + mWindowContext.getResources().getConfiguration());
+ log.add("\tWindowContext logs:");
+ log.add("\t\tWindowContext=" + windowContext);
+ log.add("\t\tWindowContext.getResources().getConfiguration()="
+ + windowContext.getResources().getConfiguration());
if (mUserUnlocked) {
- log.add("\t\tLauncherAppState.getIDP().getDeviceProfile(mWindowContext)"
+ log.add("\t\tLauncherAppState.getIDP().getDeviceProfile(getPrimaryWindowContext())"
+ ".isTaskbarPresent=" + contextTaskbarPresent);
} else {
log.add("\t\tCouldn't get DeviceProfile because !mUserUnlocked");
@@ -1010,8 +1088,4 @@
private final DeviceProfile.OnDeviceProfileChangeListener mDebugActivityDeviceProfileChanged =
dp -> debugWhyTaskbarNotDestroyed("mActivity onDeviceProfileChanged");
- @VisibleForTesting
- public Context getWindowContext() {
- return mWindowContext;
- }
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
index e150568..90c9553 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
@@ -125,7 +125,8 @@
// Needs to be set on window context instead of sandbox context, because it does
// does not propagate between them. However, this change will impact created
// TaskbarActivityContext instances, since they wrap the window context.
- taskbarManager.windowContext.resources.configuration.setLayoutDirection(
+ // TODO: iterate through all window contexts and do this.
+ taskbarManager.primaryWindowContext.resources.configuration.setLayoutDirection(
RTL_LOCALE
)
}
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index e06895c..f740489 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -174,7 +174,8 @@
<declare-styleable name="GridDisplayOption">
<attr name="name" format="string" />
- <attr name="title" />
+ <attr name="gridTitle" format="string" />
+ <attr name="gridIconId" format="reference"/>
<attr name="numRows" format="integer" />
<attr name="numColumns" format="integer" />
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 753e017..e47a44a 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -993,7 +993,8 @@
private static final int DONT_INLINE_QSB = 0;
public final String name;
- public final String title;
+ public final String gridTitle;
+ public final int gridIconId;
public final int numRows;
public final int numColumns;
public final int numSearchContainerColumns;
@@ -1042,7 +1043,9 @@
TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.GridDisplayOption);
name = a.getString(R.styleable.GridDisplayOption_name);
- title = a.getString(R.styleable.GridDisplayOption_title);
+ gridTitle = a.getString(R.styleable.GridDisplayOption_gridTitle);
+ gridIconId = a.getResourceId(
+ R.styleable.GridDisplayOption_gridIconId, INVALID_RESOURCE_HANDLE);
deviceCategory = a.getInt(R.styleable.GridDisplayOption_deviceCategory,
DEVICE_CATEGORY_ALL);
mGridSizeSpecsId = a.getResourceId(
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index b76e098..fb48a4d 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -1708,7 +1708,7 @@
@Override
public boolean canInterceptEventsInSystemGestureRegion() {
- return true;
+ return !mIsEditingName;
}
/**
diff --git a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
index c9e688b..9edc386 100644
--- a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
+++ b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
@@ -86,10 +86,13 @@
private static final String TAG = "GridCustomizationsProvider";
+ // KEY_NAME is the name of the grid used internally while the KEY_GRID_TITLE is the translated
+ // string title of the grid.
private static final String KEY_NAME = "name";
private static final String KEY_GRID_TITLE = "grid_title";
private static final String KEY_ROWS = "rows";
private static final String KEY_COLS = "cols";
+ private static final String KEY_GRID_ICON_ID = "grid_icon_id";
private static final String KEY_PREVIEW_COUNT = "preview_count";
// is_default means if a certain option is currently set to the system
private static final String KEY_IS_DEFAULT = "is_default";
@@ -161,17 +164,18 @@
case KEY_LIST_OPTIONS: {
MatrixCursor cursor = new MatrixCursor(new String[]{
KEY_NAME, KEY_GRID_TITLE, KEY_ROWS, KEY_COLS, KEY_PREVIEW_COUNT,
- KEY_IS_DEFAULT});
+ KEY_IS_DEFAULT, KEY_GRID_ICON_ID});
InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(getContext());
for (GridOption gridOption : idp.parseAllGridOptions(getContext())) {
cursor.newRow()
.add(KEY_NAME, gridOption.name)
- .add(KEY_GRID_TITLE, gridOption.title)
+ .add(KEY_GRID_TITLE, gridOption.gridTitle)
.add(KEY_ROWS, gridOption.numRows)
.add(KEY_COLS, gridOption.numColumns)
.add(KEY_PREVIEW_COUNT, 1)
.add(KEY_IS_DEFAULT, idp.numColumns == gridOption.numColumns
- && idp.numRows == gridOption.numRows);
+ && idp.numRows == gridOption.numRows)
+ .add(KEY_GRID_ICON_ID, gridOption.gridIconId);
}
return cursor;
}
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 1623881..6a8d86b 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -42,6 +42,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.android.launcher3.Flags;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings.Favorites;
@@ -201,7 +202,7 @@
info.itemType = itemType;
info.title = getTitle();
// the fallback icon
- if (!loadIcon(info)) {
+ if (!loadIconFromDb(info)) {
info.bitmap = mIconCache.getDefaultIcon(info.user);
}
@@ -213,15 +214,15 @@
/**
* Loads the icon from the cursor and updates the {@param info} if the icon is an app resource.
*/
- protected boolean loadIcon(WorkspaceItemInfo info) {
- return createIconRequestInfo(info, false).loadWorkspaceIcon(mContext);
+ protected boolean loadIconFromDb(WorkspaceItemInfo info) {
+ return createIconRequestInfo(info, false).loadIconFromDbBlob(mContext);
}
public IconRequestInfo<WorkspaceItemInfo> createIconRequestInfo(
WorkspaceItemInfo wai, boolean useLowResIcon) {
byte[] iconBlob = itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT || restoreFlag != 0
+ || (wai.isInactiveArchive() && Flags.restoreArchivedAppIconsFromDb())
? getIconBlob() : null;
-
return new IconRequestInfo<>(wai, mActivityInfo, iconBlob, useLowResIcon);
}
@@ -312,7 +313,7 @@
info.intent = intent;
// the fallback icon
- if (!loadIcon(info)) {
+ if (!loadIconFromDb(info)) {
mIconCache.getTitleAndIcon(info, DEFAULT_LOOKUP_FLAG);
}
@@ -375,20 +376,11 @@
info.intent = newIntent;
UserCache userCache = UserCache.getInstance(mContext);
UserIconInfo userIconInfo = userCache.getUserInfo(user);
-
- if (loadIcon) {
- mIconCache.getTitleAndIcon(info, mActivityInfo,
- DEFAULT_LOOKUP_FLAG.withUseLowRes(useLowResIcon));
- if (mIconCache.isDefaultIcon(info.bitmap, user)) {
- loadIcon(info);
- }
- }
-
if (mActivityInfo != null) {
AppInfo.updateRuntimeFlagsForActivityTarget(info, mActivityInfo, userIconInfo,
ApiWrapper.INSTANCE.get(mContext), mPmHelper);
}
-
+ loadWorkspaceTitleAndIcon(useLowResIcon, loadIcon, info);
// from the db
if (TextUtils.isEmpty(info.title)) {
if (loadIcon) {
@@ -407,6 +399,32 @@
return info;
}
+ @VisibleForTesting
+ void loadWorkspaceTitleAndIcon(
+ boolean useLowResIcon,
+ boolean loadIconFromCache,
+ WorkspaceItemInfo info
+ ) {
+ boolean isPreArchived = Flags.enableSupportForArchiving()
+ && Flags.restoreArchivedAppIconsFromDb()
+ && info.isInactiveArchive();
+ boolean preArchivedIconNotFound = isPreArchived && !loadIconFromDb(info);
+ if (preArchivedIconNotFound) {
+ Log.d(TAG, "loadIconFromDb failed for pre-archived icon, loading from cache."
+ + " Component=" + info.getTargetComponent());
+ mIconCache.getTitleAndIcon(info, mActivityInfo,
+ DEFAULT_LOOKUP_FLAG.withUseLowRes(useLowResIcon));
+ } else if (loadIconFromCache && !info.isInactiveArchive()) {
+ mIconCache.getTitleAndIcon(info, mActivityInfo,
+ DEFAULT_LOOKUP_FLAG.withUseLowRes(useLowResIcon));
+ if (mIconCache.isDefaultIcon(info.bitmap, user)) {
+ Log.d(TAG, "Default Icon found in cache, trying DB instead. "
+ + " Component=" + info.getTargetComponent());
+ loadIconFromDb(info);
+ }
+ }
+ }
+
/**
* Returns a {@link ContentWriter} which can be used to update the current item.
*/
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index fee9696..3ee029b 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -26,6 +26,7 @@
import static com.android.launcher3.LauncherSettings.Favorites.DESKTOP_ICON_FLAG;
import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
import static com.android.launcher3.icons.CacheableShortcutInfo.convertShortcutsToCacheableShortcuts;
+import static com.android.launcher3.icons.cache.CacheLookupFlag.DEFAULT_LOOKUP_FLAG;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION;
@@ -106,11 +107,13 @@
import com.android.launcher3.widget.WidgetInflater;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CancellationException;
@@ -154,6 +157,8 @@
private Map<ShortcutKey, ShortcutInfo> mShortcutKeyToPinnedShortcuts;
private HashMap<PackageUserKey, SessionInfo> mInstallingPkgsCached;
+ private List<IconRequestInfo<WorkspaceItemInfo>> mWorkspaceIconRequestInfos = new ArrayList<>();
+
private boolean mStopped;
private final Set<PackageUserKey> mPendingPackages = new HashSet<>();
@@ -410,7 +415,7 @@
protected void loadWorkspace(
List<CacheableShortcutInfo> allDeepShortcuts,
String selection,
- LoaderMemoryLogger memoryLogger,
+ @Nullable LoaderMemoryLogger memoryLogger,
@Nullable LauncherRestoreEventLogger restoreEventLogger
) {
Trace.beginSection("LoadWorkspace");
@@ -474,13 +479,12 @@
final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>();
queryPinnedShortcutsForUnlockedUsers(context, unlockedUsers);
- List<IconRequestInfo<WorkspaceItemInfo>> iconRequestInfos = new ArrayList<>();
-
+ mWorkspaceIconRequestInfos = new ArrayList<>();
WorkspaceItemProcessor itemProcessor = new WorkspaceItemProcessor(c, memoryLogger,
mUserCache, mUserManagerState, mLauncherApps, mPendingPackages,
mShortcutKeyToPinnedShortcuts, mApp, mBgDataModel,
mWidgetProvidersMap, installingPkgs, isSdCardReady,
- widgetInflater, mPmHelper, iconRequestInfos, unlockedUsers,
+ widgetInflater, mPmHelper, mWorkspaceIconRequestInfos, unlockedUsers,
allDeepShortcuts);
if (mStopped) {
@@ -490,7 +494,7 @@
itemProcessor.processItem();
}
}
- tryLoadWorkspaceIconsInBulk(iconRequestInfos);
+ tryLoadWorkspaceIconsInBulk(mWorkspaceIconRequestInfos);
} finally {
IOUtils.closeSilently(c);
}
@@ -621,7 +625,9 @@
for (IconRequestInfo<WorkspaceItemInfo> iconRequestInfo : iconRequestInfos) {
WorkspaceItemInfo wai = iconRequestInfo.itemInfo;
if (mIconCache.isDefaultIcon(wai.bitmap, wai.user)) {
- iconRequestInfo.loadWorkspaceIcon(mApp.getContext());
+ logASplit("tryLoadWorkspaceIconsInBulk: default icon found for "
+ + wai.getTargetComponent() + ", will attempt to load from iconBlob");
+ iconRequestInfo.loadIconFromDbBlob(mApp.getContext());
}
}
} finally {
@@ -702,7 +708,7 @@
// Clear the list of apps
mBgAllAppsList.clear();
- List<IconRequestInfo<AppInfo>> iconRequestInfos = new ArrayList<>();
+ List<IconRequestInfo<AppInfo>> allAppsItemRequestInfos = new ArrayList<>();
boolean isWorkProfileQuiet = false;
boolean isPrivateProfileQuiet = false;
for (UserHandle user : profiles) {
@@ -742,15 +748,14 @@
}
}
- iconRequestInfos.add(new IconRequestInfo<>(
- appInfo, app, /* useLowResIcon= */ false));
- mBgAllAppsList.add(
- appInfo, app, false);
+ IconRequestInfo<AppInfo> iconRequestInfo = getAppInfoIconRequestInfo(
+ appInfo, app, mWorkspaceIconRequestInfos);
+ allAppsItemRequestInfos.add(iconRequestInfo);
+ mBgAllAppsList.add(appInfo, app, false);
}
allActivityList.addAll(apps);
}
-
if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) {
// get all active sessions and add them to the all apps list
for (PackageInstaller.SessionInfo info :
@@ -761,7 +766,7 @@
false);
if (promiseAppInfo != null) {
- iconRequestInfos.add(new IconRequestInfo<>(
+ allAppsItemRequestInfos.add(new IconRequestInfo<>(
promiseAppInfo,
/* launcherActivityInfo= */ null,
promiseAppInfo.getMatchingLookupFlag().useLowRes()));
@@ -770,9 +775,22 @@
}
Trace.beginSection("LoadAllAppsIconsInBulk");
+
try {
- mIconCache.getTitlesAndIconsInBulk(iconRequestInfos);
- iconRequestInfos.forEach(iconRequestInfo ->
+ mIconCache.getTitlesAndIconsInBulk(allAppsItemRequestInfos);
+ if (Flags.restoreArchivedAppIconsFromDb()) {
+ for (IconRequestInfo<AppInfo> iconRequestInfo : allAppsItemRequestInfos) {
+ AppInfo appInfo = iconRequestInfo.itemInfo;
+ if (mIconCache.isDefaultIcon(appInfo.bitmap, appInfo.user)) {
+ logASplit("LoadAllAppsIconsInBulk: default icon found for "
+ + appInfo.getTargetComponent()
+ + ", will attempt to load from iconBlob: "
+ + Arrays.toString(iconRequestInfo.iconBlob));
+ iconRequestInfo.loadIconFromDbBlob(mApp.getContext());
+ }
+ }
+ }
+ allAppsItemRequestInfos.forEach(iconRequestInfo ->
mBgAllAppsList.updateSectionName(iconRequestInfo.itemInfo));
} finally {
Trace.endSection();
@@ -795,6 +813,51 @@
return allActivityList;
}
+ @NonNull
+ @VisibleForTesting
+ IconRequestInfo<AppInfo> getAppInfoIconRequestInfo(
+ AppInfo appInfo,
+ LauncherActivityInfo activityInfo,
+ List<IconRequestInfo<WorkspaceItemInfo>> workspaceRequestInfos
+ ) {
+ if (Flags.restoreArchivedAppIconsFromDb()) {
+ Optional<IconRequestInfo<WorkspaceItemInfo>> workspaceIconRequest =
+ workspaceRequestInfos.stream()
+ .filter(request -> appInfo.getTargetComponent().equals(
+ request.itemInfo.getTargetComponent()))
+ .findFirst();
+
+ if (workspaceIconRequest.isPresent() && activityInfo.getApplicationInfo().isArchived) {
+ logASplit("getAppInfoIconRequestInfo:"
+ + " matching archived info found, loading icon blob into icon request."
+ + " Component=" + appInfo.getTargetComponent());
+ IconRequestInfo<AppInfo> iconRequestInfo = new IconRequestInfo<>(
+ appInfo,
+ activityInfo,
+ workspaceIconRequest.get().iconBlob,
+ false /* useLowResIcon= */
+ );
+ if (!iconRequestInfo.loadIconFromDbBlob(mApp.getContext())) {
+ Log.d(TAG, "AppInfo Icon failed to load from blob, using cache.");
+ mIconCache.getTitleAndIcon(
+ appInfo,
+ iconRequestInfo.launcherActivityInfo,
+ DEFAULT_LOOKUP_FLAG
+ );
+ }
+ return iconRequestInfo;
+ } else {
+ Log.d(TAG, "App not archived or workspace info not found"
+ + ", creating IconRequestInfo without icon blob."
+ + " Component:" + appInfo.getTargetComponent()
+ + ", isArchived: " + activityInfo.getApplicationInfo().isArchived);
+ }
+ }
+ logASplit("Loading IconRequestInfo without iconBlob for AppInfo: "
+ + appInfo.getTargetComponent());
+ return new IconRequestInfo<>(appInfo, activityInfo, false /* useLowResIcon= */);
+ }
+
private List<ShortcutInfo> loadDeepShortcuts() {
List<ShortcutInfo> allShortcuts = new ArrayList<>();
mBgDataModel.deepShortcutMap.clear();
diff --git a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
index de1df2e..90f11a3 100644
--- a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
+++ b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
@@ -286,7 +286,7 @@
// If the pinned deep shortcut is no longer published,
// use the last saved icon instead of the default.
val csi = CacheableShortcutInfo(pinnedShortcut, appInfoWrapper)
- iconCache.getShortcutIcon(info, csi, c::loadIcon)
+ iconCache.getShortcutIcon(info, csi, c::loadIconFromDb)
if (appInfoWrapper.isSuspended()) {
info.runtimeStatusFlags =
info.runtimeStatusFlags or ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED
diff --git a/src/com/android/launcher3/model/data/IconRequestInfo.java b/src/com/android/launcher3/model/data/IconRequestInfo.java
index e77e527..42af018 100644
--- a/src/com/android/launcher3/model/data/IconRequestInfo.java
+++ b/src/com/android/launcher3/model/data/IconRequestInfo.java
@@ -64,23 +64,25 @@
}
/**
- * Loads this request's item info's title. This method should only be used on IconRequestInfos
- * for WorkspaceItemInfos.
+ * Loads this request's item info's title and icon from given iconBlob from Launcher.db.
+ * This method should only be used on {@link IconRequestInfo} for {@link WorkspaceItemInfo}
+ * or {@link AppInfo}.
*/
- public boolean loadWorkspaceIcon(Context context) {
- if (!(itemInfo instanceof WorkspaceItemInfo)) {
+ public boolean loadIconFromDbBlob(Context context) {
+ if (!(itemInfo instanceof WorkspaceItemInfo) && !(itemInfo instanceof AppInfo)) {
throw new IllegalStateException(
- "loadWorkspaceIcon should only be use for a WorkspaceItemInfos: " + itemInfo);
+ "loadIconFromDb should only be used for either WorkspaceItemInfo or AppInfo: "
+ + itemInfo);
}
try (LauncherIcons li = LauncherIcons.obtain(context)) {
- WorkspaceItemInfo info = (WorkspaceItemInfo) itemInfo;
- // Failed to load from resource, try loading from DB.
+ ItemInfoWithIcon info = itemInfo;
if (iconBlob == null) {
+ Log.d(TAG, "loadIconFromDb: icon blob null, returning. Component="
+ + info.getTargetComponent());
return false;
}
- info.bitmap = li.createIconBitmap(decodeByteArray(
- iconBlob, 0, iconBlob.length));
+ info.bitmap = li.createIconBitmap(decodeByteArray(iconBlob, 0, iconBlob.length));
return true;
} catch (Exception e) {
Log.e(TAG, "Failed to decode byte array for info " + itemInfo, e);
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java
index 63359ec..11047fb 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -16,6 +16,9 @@
package com.android.launcher3.model;
+import static android.graphics.BitmapFactory.decodeByteArray;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+
import static androidx.test.InstrumentationRegistry.getContext;
import static com.android.launcher3.LauncherSettings.Favorites.APPWIDGET_ID;
@@ -40,8 +43,11 @@
import static com.android.launcher3.LauncherSettings.Favorites.SPANY;
import static com.android.launcher3.LauncherSettings.Favorites.TITLE;
import static com.android.launcher3.LauncherSettings.Favorites._ID;
+import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_ARCHIVED;
import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY;
+import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
@@ -52,13 +58,19 @@
import android.content.Context;
import android.content.Intent;
import android.database.MatrixCursor;
+import android.graphics.Bitmap;
import android.os.Process;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.launcher3.Flags;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.Executors;
@@ -67,6 +79,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -77,6 +90,9 @@
@RunWith(AndroidJUnit4.class)
public class LoaderCursorTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+
private LauncherModelHelper mModelHelper;
private LauncherAppState mApp;
private PackageManagerHelper mPmHelper;
@@ -87,6 +103,12 @@
private LoaderCursor mLoaderCursor;
+ private static byte[] sTestBlob = new byte[] {
+ -119, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 1, 0, 0, 0, 1,
+ 8, 4, 0, 0, 0, -75, 28, 12, 2, 0, 0, 0, 11, 73, 68, 65, 84, 120, -38, 99, 100, 96, 0,
+ 0, 0, 6, 0, 2, 48, -127, -48, 47, 0, 0, 0, 0, 73, 69, 78, 68, -82, 66, 96, -126
+ };
+
@Before
public void setup() {
mModelHelper = new LauncherModelHelper();
@@ -119,7 +141,8 @@
.add(PROFILE_ID, 0)
.add(ITEM_TYPE, itemType)
.add(TITLE, title)
- .add(CONTAINER, CONTAINER_DESKTOP);
+ .add(CONTAINER, CONTAINER_DESKTOP)
+ .add(ICON, sTestBlob);
}
@Test
@@ -161,7 +184,12 @@
@Test
public void loadSimpleShortcut() {
- initCursor(ITEM_TYPE_DEEP_SHORTCUT, "my-shortcut");
+ mCursor.newRow()
+ .add(_ID, 1)
+ .add(PROFILE_ID, 0)
+ .add(ITEM_TYPE, ITEM_TYPE_DEEP_SHORTCUT)
+ .add(TITLE, "my-shortcut")
+ .add(CONTAINER, CONTAINER_DESKTOP);
assertTrue(mLoaderCursor.moveToNext());
WorkspaceItemInfo info = mLoaderCursor.loadSimpleWorkspaceItem();
@@ -223,6 +251,68 @@
newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 3), true));
}
+ @Test
+ @EnableFlags(Flags.FLAG_RESTORE_ARCHIVED_APP_ICONS_FROM_DB)
+ public void ifArchivedWithFlag_whenloadWorkspaceTitleAndIcon_thenLoadIconFromDb() {
+ // Given
+ initCursor(ITEM_TYPE_APPLICATION, "title");
+ assertTrue(mLoaderCursor.moveToNext());
+ WorkspaceItemInfo itemInfo = new WorkspaceItemInfo();
+ itemInfo.bitmap = null;
+ itemInfo.runtimeStatusFlags |= FLAG_ARCHIVED;
+ Bitmap expectedBitmap = LauncherIcons.obtain(mContext)
+ .createIconBitmap(decodeByteArray(sTestBlob, 0, sTestBlob.length))
+ .icon;
+ // When
+ mLoaderCursor.loadWorkspaceTitleAndIcon(false, true, itemInfo);
+ // Then
+ assertThat(itemInfo.bitmap.icon).isNotNull();
+ assertThat(itemInfo.bitmap.icon.sameAs(expectedBitmap)).isTrue();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_RESTORE_ARCHIVED_APP_ICONS_FROM_DB)
+ public void ifArchivedWithFlag_whenLoadIconFromDb_thenLoadIconFromBlob() {
+ // Given
+ initCursor(ITEM_TYPE_APPLICATION, "title");
+ assertTrue(mLoaderCursor.moveToNext());
+ WorkspaceItemInfo itemInfo = new WorkspaceItemInfo();
+ itemInfo.runtimeStatusFlags |= FLAG_ARCHIVED;
+ // Then
+ assertTrue(mLoaderCursor.loadIconFromDb(itemInfo));
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_RESTORE_ARCHIVED_APP_ICONS_FROM_DB)
+ public void ifArchivedWithoutFlag_whenLoadWorkspaceTitleAndIcon_thenDoNotLoadFromDb() {
+ // Given
+ initCursor(ITEM_TYPE_APPLICATION, "title");
+ assertTrue(mLoaderCursor.moveToNext());
+ WorkspaceItemInfo itemInfo = new WorkspaceItemInfo();
+ itemInfo.bitmap = null;
+ itemInfo.runtimeStatusFlags |= FLAG_ARCHIVED;
+ Intent intent = new Intent();
+ intent.setComponent(new ComponentName("package", "class"));
+ itemInfo.intent = intent;
+ // When
+ mLoaderCursor.loadWorkspaceTitleAndIcon(false, false, itemInfo);
+ // Then
+ assertThat(itemInfo.bitmap).isNull();
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_RESTORE_ARCHIVED_APP_ICONS_FROM_DB)
+ public void ifArchivedWithoutFlag_whenLoadIconFromDb_thenDoNotLoadFromBlob() {
+ // Given
+ initCursor(ITEM_TYPE_APPLICATION, "title");
+ assertTrue(mLoaderCursor.moveToNext());
+ WorkspaceItemInfo itemInfo = new WorkspaceItemInfo();
+ itemInfo.runtimeStatusFlags |= FLAG_ARCHIVED;
+ // Then
+ assertFalse(mLoaderCursor.loadIconFromDb(itemInfo));
+ }
+
+
private ItemInfo newItemInfo(int cellX, int cellY, int spanX, int spanY,
int container, int screenId) {
ItemInfo info = new ItemInfo();
diff --git a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
index dba7603..cdb45fc 100644
--- a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
+++ b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
@@ -1,7 +1,10 @@
package com.android.launcher3.model
import android.appwidget.AppWidgetManager
+import android.content.ComponentName
import android.content.Intent
+import android.content.pm.ApplicationInfo
+import android.content.pm.LauncherActivityInfo
import android.os.Process
import android.os.UserHandle
import android.platform.test.annotations.DisableFlags
@@ -28,6 +31,9 @@
import com.android.launcher3.icons.IconCache
import com.android.launcher3.icons.cache.CachingLogic
import com.android.launcher3.icons.cache.IconCacheUpdateHandler
+import com.android.launcher3.model.data.AppInfo
+import com.android.launcher3.model.data.IconRequestInfo
+import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.pm.UserCache
import com.android.launcher3.provider.RestoreDbTask
import com.android.launcher3.ui.TestViewHelpers
@@ -38,8 +44,11 @@
import com.android.launcher3.util.TestUtil
import com.android.launcher3.util.UserIconInfo
import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
import dagger.BindsInstance
import dagger.Component
+import java.util.concurrent.CountDownLatch
+import java.util.function.Predicate
import junit.framework.Assert.assertEquals
import org.junit.After
import org.junit.Before
@@ -58,12 +67,11 @@
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
-import java.util.concurrent.CountDownLatch
-import java.util.function.Predicate
private const val INSERTION_STATEMENT_FILE = "databases/workspace_items.sql"
@@ -480,6 +488,157 @@
verify(spyContext, times(0)).sendBroadcast(any())
}
+ @Test
+ @EnableFlags(Flags.FLAG_RESTORE_ARCHIVED_APP_ICONS_FROM_DB)
+ fun `When flag on then archived AllApps icons found on Workspace loaded from db`() {
+ // Given
+ // Given
+ val activityInfo: LauncherActivityInfo = mock()
+ val applicationInfo: ApplicationInfo = mock<ApplicationInfo>().apply { isArchived = true }
+ whenever(activityInfo.applicationInfo).thenReturn(applicationInfo)
+ val expectedIconBlob = byteArrayOf(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08)
+ val expectedComponent = ComponentName("package", "class")
+ val workspaceIconRequests =
+ listOf(
+ IconRequestInfo<WorkspaceItemInfo>(
+ WorkspaceItemInfo().apply {
+ intent = Intent().apply { component = expectedComponent }
+ },
+ activityInfo,
+ expectedIconBlob,
+ false, /* useLowResIcon */
+ )
+ )
+ val expectedAppInfo = AppInfo().apply { componentName = expectedComponent }
+ // When
+ val loader =
+ LoaderTask(
+ app,
+ bgAllAppsList,
+ BgDataModel(),
+ modelDelegate,
+ launcherBinder,
+ widgetsFilterDataProvider,
+ )
+ val actualIconRequest =
+ loader.getAppInfoIconRequestInfo(expectedAppInfo, activityInfo, workspaceIconRequests)
+ // Then
+ assertThat(actualIconRequest.iconBlob).isEqualTo(expectedIconBlob)
+ assertThat(actualIconRequest.itemInfo).isEqualTo(expectedAppInfo)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_RESTORE_ARCHIVED_APP_ICONS_FROM_DB)
+ fun `When flag on then unarchived AllApps icons not loaded from db`() {
+ // Given
+ val activityInfo: LauncherActivityInfo = mock()
+ val applicationInfo: ApplicationInfo = mock<ApplicationInfo>().apply { isArchived = false }
+ whenever(activityInfo.applicationInfo).thenReturn(applicationInfo)
+ val expectedIconBlob = byteArrayOf(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08)
+ val expectedComponent = ComponentName("package", "class")
+ val workspaceIconRequests =
+ listOf(
+ IconRequestInfo<WorkspaceItemInfo>(
+ WorkspaceItemInfo().apply {
+ intent = Intent().apply { component = expectedComponent }
+ },
+ activityInfo,
+ expectedIconBlob,
+ false, /* useLowResIcon */
+ )
+ )
+ val expectedAppInfo = AppInfo().apply { componentName = expectedComponent }
+ // When
+ val loader =
+ LoaderTask(
+ app,
+ bgAllAppsList,
+ BgDataModel(),
+ modelDelegate,
+ launcherBinder,
+ widgetsFilterDataProvider,
+ )
+ val actualIconRequest =
+ loader.getAppInfoIconRequestInfo(expectedAppInfo, activityInfo, workspaceIconRequests)
+ // Then
+ assertThat(actualIconRequest.iconBlob).isNull()
+ assertThat(actualIconRequest.itemInfo).isEqualTo(expectedAppInfo)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_RESTORE_ARCHIVED_APP_ICONS_FROM_DB)
+ fun `When flag on then archived AllApps icon not found on Workspace not loaded from db`() {
+ // Given
+ val activityInfo: LauncherActivityInfo = mock()
+ val applicationInfo: ApplicationInfo = mock<ApplicationInfo>().apply { isArchived = true }
+ whenever(activityInfo.applicationInfo).thenReturn(applicationInfo)
+ val expectedIconBlob = byteArrayOf(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08)
+ val expectedComponent = ComponentName("package", "class")
+ val workspaceIconRequests =
+ listOf(
+ IconRequestInfo<WorkspaceItemInfo>(
+ WorkspaceItemInfo().apply {
+ intent = Intent().apply { component = expectedComponent }
+ },
+ activityInfo,
+ expectedIconBlob,
+ false, /* useLowResIcon */
+ )
+ )
+ val expectedAppInfo =
+ AppInfo().apply { componentName = ComponentName("differentPkg", "differentClass") }
+ // When
+ val loader =
+ LoaderTask(
+ app,
+ bgAllAppsList,
+ BgDataModel(),
+ modelDelegate,
+ launcherBinder,
+ widgetsFilterDataProvider,
+ )
+ val actualIconRequest =
+ loader.getAppInfoIconRequestInfo(expectedAppInfo, activityInfo, workspaceIconRequests)
+ // Then
+ assertThat(actualIconRequest.iconBlob).isNull()
+ assertThat(actualIconRequest.itemInfo).isEqualTo(expectedAppInfo)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_RESTORE_ARCHIVED_APP_ICONS_FROM_DB)
+ fun `When flag off then archived AllApps icons not loaded from db`() {
+ // Given
+ val activityInfo: LauncherActivityInfo = mock()
+ val applicationInfo: ApplicationInfo = mock<ApplicationInfo>().apply { isArchived = true }
+ whenever(activityInfo.applicationInfo).thenReturn(applicationInfo)
+ val expectedIconBlob = byteArrayOf(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08)
+ val workspaceIconRequests =
+ listOf(
+ IconRequestInfo<WorkspaceItemInfo>(
+ WorkspaceItemInfo(),
+ activityInfo,
+ expectedIconBlob,
+ false, /* useLowResIcon */
+ )
+ )
+ val expectedAppInfo = AppInfo()
+ // When
+ val loader =
+ LoaderTask(
+ app,
+ bgAllAppsList,
+ BgDataModel(),
+ modelDelegate,
+ launcherBinder,
+ widgetsFilterDataProvider,
+ )
+ val actualIconRequest =
+ loader.getAppInfoIconRequestInfo(expectedAppInfo, activityInfo, workspaceIconRequests)
+ // Then
+ assertThat(actualIconRequest.iconBlob).isNull()
+ assertThat(actualIconRequest.itemInfo).isEqualTo(expectedAppInfo)
+ }
+
@LauncherAppSingleton
@Component(modules = [AllModulesForTest::class])
interface TestComponent : LauncherAppComponent {