From 05ca3a808901daf971f93e57685a83ea67faeebc Mon Sep 17 00:00:00 2001 From: Stefan Andonian Date: Tue, 22 Nov 2022 20:02:07 +0000 Subject: [PATCH] Refactor Workspace Loading / Processing code. There are no behavioral changes (other than we no longer are recycling objects while processing workspace items), but this refactor will setup the next change to load the first workspace page before other workspace pages. Bug: 251502424 Test: Workspace, app widgets, and containers loaded properly. Change-Id: I437aab40000d841d7fcc4380d05ab4a8f5d5c2ad --- .../model/QuickstepModelDelegate.java | 34 +- .../android/launcher3/model/LoaderCursor.java | 208 ++-- .../android/launcher3/model/LoaderTask.java | 959 +++++++++--------- .../launcher3/model/ModelDelegate.java | 23 +- .../launcher3/model/LoaderCursorTest.java | 11 +- 5 files changed, 672 insertions(+), 563 deletions(-) diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java index ad6ce7dbd2..2e1318b019 100644 --- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java +++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java @@ -112,10 +112,23 @@ public class QuickstepModelDelegate extends ModelDelegate { } @Override - @WorkerThread - public void loadItems(UserManagerState ums, Map pinnedShortcuts) { + public void loadHotseatItems(UserManagerState ums, + Map pinnedShortcuts) { // TODO: Implement caching and preloading - super.loadItems(ums, pinnedShortcuts); + super.loadHotseatItems(ums, pinnedShortcuts); + + WorkspaceItemFactory hotseatFactory = new WorkspaceItemFactory(mApp, ums, pinnedShortcuts, + mIDP.numDatabaseHotseatIcons, mHotseatState.containerId); + FixedContainerItems hotseatItems = new FixedContainerItems(mHotseatState.containerId, + mHotseatState.storage.read(mApp.getContext(), hotseatFactory, ums.allUsers::get)); + mDataModel.extraItems.put(mHotseatState.containerId, hotseatItems); + } + + @Override + public void loadAllAppsItems(UserManagerState ums, + Map pinnedShortcuts) { + // TODO: Implement caching and preloading + super.loadAllAppsItems(ums, pinnedShortcuts); WorkspaceItemFactory allAppsFactory = new WorkspaceItemFactory(mApp, ums, pinnedShortcuts, mIDP.numDatabaseAllAppsColumns, mAllAppsState.containerId); @@ -123,17 +136,22 @@ public class QuickstepModelDelegate extends ModelDelegate { mAllAppsState.containerId, mAllAppsState.storage.read(mApp.getContext(), allAppsFactory, ums.allUsers::get)); mDataModel.extraItems.put(mAllAppsState.containerId, allAppsPredictionItems); + } - WorkspaceItemFactory hotseatFactory = new WorkspaceItemFactory(mApp, ums, pinnedShortcuts, - mIDP.numDatabaseHotseatIcons, mHotseatState.containerId); - FixedContainerItems hotseatItems = new FixedContainerItems(mHotseatState.containerId, - mHotseatState.storage.read(mApp.getContext(), hotseatFactory, ums.allUsers::get)); - mDataModel.extraItems.put(mHotseatState.containerId, hotseatItems); + @Override + public void loadWidgetsRecommendationItems() { + // TODO: Implement caching and preloading + super.loadWidgetsRecommendationItems(); // Widgets prediction isn't used frequently. And thus, it is not persisted on disk. mDataModel.extraItems.put(mWidgetsRecommendationState.containerId, new FixedContainerItems(mWidgetsRecommendationState.containerId, new ArrayList<>())); + } + + @Override + public void markActive() { + super.markActive(); mActive = true; } diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java index 6c62b31cbe..0a6a7cda9c 100644 --- a/src/com/android/launcher3/model/LoaderCursor.java +++ b/src/com/android/launcher3/model/LoaderCursor.java @@ -37,7 +37,6 @@ 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; @@ -72,24 +71,32 @@ public class LoaderCursor extends CursorWrapper { private final IconCache mIconCache; private final InvariantDeviceProfile mIDP; - private final IntArray itemsToRemove = new IntArray(); - private final IntArray restoredRows = new IntArray(); - private final IntSparseArrayMap occupied = new IntSparseArrayMap<>(); + private final IntArray mItemsToRemove = new IntArray(); + private final IntArray mRestoredRows = new IntArray(); + private final IntSparseArrayMap mOccupied = new IntSparseArrayMap<>(); - private final int iconPackageIndex; - private final int iconResourceIndex; - private final int iconIndex; - public final int titleIndex; + private final int mIconPackageIndex; + private final int mIconResourceIndex; + private final int mIconIndex; + public final int mTitleIndex; - private final int idIndex; - private final int containerIndex; - private final int itemTypeIndex; - private final int screenIndex; - private final int cellXIndex; - private final int cellYIndex; - private final int profileIdIndex; - private final int restoredIndex; - private final int intentIndex; + private final int mIdIndex; + private final int mContainerIndex; + private final int mItemTypeIndex; + private final int mScreenIndex; + private final int mCellXIndex; + private final int mCellYIndex; + private final int mProfileIdIndex; + private final int mRestoredIndex; + private final int mIntentIndex; + + private final int mAppWidgetIdIndex; + private final int mAppWidgetProviderIndex; + private final int mSpanXIndex; + private final int mSpanYIndex; + private final int mRankIndex; + private final int mOptionsIndex; + private final int mAppWidgetSourceIndex; @Nullable private LauncherActivityInfo mActivityInfo; @@ -114,20 +121,28 @@ public class LoaderCursor extends CursorWrapper { mPM = mContext.getPackageManager(); // Init column indices - iconIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.ICON); - iconPackageIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE); - iconResourceIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE); - titleIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE); + mIconIndex = getColumnIndexOrThrow(Favorites.ICON); + mIconPackageIndex = getColumnIndexOrThrow(Favorites.ICON_PACKAGE); + mIconResourceIndex = getColumnIndexOrThrow(Favorites.ICON_RESOURCE); + mTitleIndex = getColumnIndexOrThrow(Favorites.TITLE); - idIndex = getColumnIndexOrThrow(LauncherSettings.Favorites._ID); - containerIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER); - itemTypeIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE); - screenIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN); - cellXIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX); - cellYIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY); - profileIdIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.PROFILE_ID); - restoredIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.RESTORED); - intentIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT); + mIdIndex = getColumnIndexOrThrow(Favorites._ID); + mContainerIndex = getColumnIndexOrThrow(Favorites.CONTAINER); + mItemTypeIndex = getColumnIndexOrThrow(Favorites.ITEM_TYPE); + mScreenIndex = getColumnIndexOrThrow(Favorites.SCREEN); + mCellXIndex = getColumnIndexOrThrow(Favorites.CELLX); + mCellYIndex = getColumnIndexOrThrow(Favorites.CELLY); + mProfileIdIndex = getColumnIndexOrThrow(Favorites.PROFILE_ID); + mRestoredIndex = getColumnIndexOrThrow(Favorites.RESTORED); + mIntentIndex = getColumnIndexOrThrow(Favorites.INTENT); + + mAppWidgetIdIndex = getColumnIndexOrThrow(Favorites.APPWIDGET_ID); + mAppWidgetProviderIndex = getColumnIndexOrThrow(Favorites.APPWIDGET_PROVIDER); + mSpanXIndex = getColumnIndexOrThrow(Favorites.SPANX); + mSpanYIndex = getColumnIndexOrThrow(Favorites.SPANY); + mRankIndex = getColumnIndexOrThrow(Favorites.RANK); + mOptionsIndex = getColumnIndexOrThrow(Favorites.OPTIONS); + mAppWidgetSourceIndex = getColumnIndexOrThrow(Favorites.APPWIDGET_SOURCE); } @Override @@ -137,18 +152,18 @@ public class LoaderCursor extends CursorWrapper { mActivityInfo = null; // Load common properties. - itemType = getInt(itemTypeIndex); - container = getInt(containerIndex); - id = getInt(idIndex); - serialNumber = getInt(profileIdIndex); + itemType = getInt(mItemTypeIndex); + container = getInt(mContainerIndex); + id = getInt(mIdIndex); + serialNumber = getInt(mProfileIdIndex); user = allUsers.get(serialNumber); - restoreFlag = getInt(restoredIndex); + restoreFlag = getInt(mRestoredIndex); } return result; } public Intent parseIntent() { - String intentDescription = getString(intentIndex); + String intentDescription = getString(mIntentIndex); try { return TextUtils.isEmpty(intentDescription) ? null : Intent.parseUri(intentDescription, 0); @@ -185,14 +200,14 @@ public class LoaderCursor extends CursorWrapper { public IconRequestInfo createIconRequestInfo( WorkspaceItemInfo wai, boolean useLowResIcon) { - String packageName = itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT - ? getString(iconPackageIndex) : null; - String resourceName = itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT - ? getString(iconResourceIndex) : null; - byte[] iconBlob = itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT + String packageName = itemType == Favorites.ITEM_TYPE_SHORTCUT + ? getString(mIconPackageIndex) : null; + String resourceName = itemType == Favorites.ITEM_TYPE_SHORTCUT + ? getString(mIconResourceIndex) : null; + byte[] iconBlob = itemType == Favorites.ITEM_TYPE_SHORTCUT || itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT || restoreFlag != 0 - ? getBlob(iconIndex) : null; + ? getBlob(mIconIndex) : null; return new IconRequestInfo<>( wai, mActivityInfo, packageName, resourceName, iconBlob, useLowResIcon); @@ -202,7 +217,70 @@ public class LoaderCursor extends CursorWrapper { * Returns the title or empty string */ private String getTitle() { - return Utilities.trim(getString(titleIndex)); + return Utilities.trim(getString(mTitleIndex)); + } + + /** + * When loading an app widget for the workspace, returns it's app widget id + */ + public int getAppWidgetId() { + return getInt(mAppWidgetIdIndex); + } + + /** + * When loading an app widget for the workspace, returns the widget provider + */ + public String getAppWidgetProvider() { + return getString(mAppWidgetProviderIndex); + } + + /** + * Returns the x position for the item in the cell layout's grid + */ + public int getSpanX() { + return getInt(mSpanXIndex); + } + + /** + * Returns the y position for the item in the cell layout's grid + */ + public int getSpanY() { + return getInt(mSpanYIndex); + } + + /** + * Returns the rank for the item + */ + public int getRank() { + return getInt(mRankIndex); + } + + /** + * Returns the options for the item + */ + public int getOptions() { + return getInt(mOptionsIndex); + } + + /** + * When loading an app widget for the workspace, returns it's app widget source + */ + public int getAppWidgetSource() { + return getInt(mAppWidgetSourceIndex); + } + + /** + * Returns the screen that the item is on + */ + public int getScreen() { + return getInt(mScreenIndex); + } + + /** + * Returns the UX container that the item is in + */ + public int getContainer() { + return getInt(mContainerIndex); } /** @@ -320,7 +398,7 @@ public class LoaderCursor extends CursorWrapper { */ public void markDeleted(String reason) { FileLog.e(TAG, reason); - itemsToRemove.add(id); + mItemsToRemove.add(id); } /** @@ -328,10 +406,10 @@ public class LoaderCursor extends CursorWrapper { * @return true is any item was removed. */ public boolean commitDeleted() { - if (itemsToRemove.size() > 0) { + if (mItemsToRemove.size() > 0) { // Remove dead items mContext.getContentResolver().delete(mContentUri, Utilities.createDbSelectionQuery( - LauncherSettings.Favorites._ID, itemsToRemove), null); + Favorites._ID, mItemsToRemove), null); return true; } return false; @@ -342,7 +420,7 @@ public class LoaderCursor extends CursorWrapper { */ public void markRestored() { if (restoreFlag != 0) { - restoredRows.add(id); + mRestoredRows.add(id); restoreFlag = 0; } } @@ -352,13 +430,13 @@ public class LoaderCursor extends CursorWrapper { } public void commitRestoredItems() { - if (restoredRows.size() > 0) { + if (mRestoredRows.size() > 0) { // Update restored items that no longer require special handling ContentValues values = new ContentValues(); - values.put(LauncherSettings.Favorites.RESTORED, 0); + values.put(Favorites.RESTORED, 0); mContext.getContentResolver().update(mContentUri, values, Utilities.createDbSelectionQuery( - LauncherSettings.Favorites._ID, restoredRows), null); + Favorites._ID, mRestoredRows), null); } } @@ -366,8 +444,7 @@ public class LoaderCursor extends CursorWrapper { * Returns true is the item is on workspace or hotseat */ public boolean isOnWorkspaceOrHotseat() { - return container == LauncherSettings.Favorites.CONTAINER_DESKTOP || - container == LauncherSettings.Favorites.CONTAINER_HOTSEAT; + return container == Favorites.CONTAINER_DESKTOP || container == Favorites.CONTAINER_HOTSEAT; } /** @@ -381,9 +458,9 @@ public class LoaderCursor extends CursorWrapper { public void applyCommonProperties(ItemInfo info) { info.id = id; info.container = container; - info.screenId = getInt(screenIndex); - info.cellX = getInt(cellXIndex); - info.cellY = getInt(cellYIndex); + info.screenId = getInt(mScreenIndex); + info.cellX = getInt(mCellXIndex); + info.cellY = getInt(mCellYIndex); } public void checkAndAddItem(ItemInfo info, BgDataModel dataModel) { @@ -396,7 +473,7 @@ public class LoaderCursor extends CursorWrapper { */ public void checkAndAddItem( ItemInfo info, BgDataModel dataModel, LoaderMemoryLogger logger) { - if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { + if (info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) { // Ensure that it is a valid intent. An exception here will // cause the item loading to get skipped ShortcutKey.fromItemInfo(info); @@ -413,9 +490,9 @@ public class LoaderCursor extends CursorWrapper { */ protected boolean checkItemPlacement(ItemInfo item) { int containerIndex = item.screenId; - if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { + if (item.container == Favorites.CONTAINER_HOTSEAT) { final GridOccupancy hotseatOccupancy = - occupied.get(LauncherSettings.Favorites.CONTAINER_HOTSEAT); + mOccupied.get(Favorites.CONTAINER_HOTSEAT); if (item.screenId >= mIDP.numDatabaseHotseatIcons) { Log.e(TAG, "Error loading shortcut " + item @@ -438,19 +515,18 @@ public class LoaderCursor extends CursorWrapper { } else { final GridOccupancy occupancy = new GridOccupancy(mIDP.numDatabaseHotseatIcons, 1); occupancy.cells[item.screenId][0] = true; - occupied.put(LauncherSettings.Favorites.CONTAINER_HOTSEAT, occupancy); + mOccupied.put(Favorites.CONTAINER_HOTSEAT, occupancy); return true; } - } else if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) { + } else if (item.container != Favorites.CONTAINER_DESKTOP) { // Skip further checking if it is not the hotseat or workspace container return true; } final int countX = mIDP.numColumns; final int countY = mIDP.numRows; - if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP && - item.cellX < 0 || item.cellY < 0 || - item.cellX + item.spanX > countX || item.cellY + item.spanY > countY) { + if (item.container == Favorites.CONTAINER_DESKTOP && item.cellX < 0 || item.cellY < 0 + || item.cellX + item.spanX > countX || item.cellY + item.spanY > countY) { Log.e(TAG, "Error loading shortcut " + item + " into cell (" + containerIndex + "-" + item.screenId + ":" + item.cellX + "," + item.cellY @@ -458,7 +534,7 @@ public class LoaderCursor extends CursorWrapper { return false; } - if (!occupied.containsKey(item.screenId)) { + if (!mOccupied.containsKey(item.screenId)) { GridOccupancy screen = new GridOccupancy(countX + 1, countY + 1); if (item.screenId == Workspace.FIRST_SCREEN_ID && FeatureFlags.QSB_ON_FIRST_SCREEN) { // Mark the first X columns (X is width of the search container) in the first row as @@ -468,9 +544,9 @@ public class LoaderCursor extends CursorWrapper { int spanY = 1; screen.markCells(0, 0, spanX, spanY, true); } - occupied.put(item.screenId, screen); + mOccupied.put(item.screenId, screen); } - final GridOccupancy occupancy = occupied.get(item.screenId); + final GridOccupancy occupancy = mOccupied.get(item.screenId); // Check if any workspace icons overlap with each other if (occupancy.isRegionVacant(item.cellX, item.cellY, item.spanX, item.spanY)) { diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index 46a6a66c76..da9be499c8 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -58,7 +58,8 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel; -import com.android.launcher3.LauncherSettings; +import com.android.launcher3.LauncherSettings.Favorites; +import com.android.launcher3.LauncherSettings.Settings; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.folder.Folder; @@ -198,7 +199,7 @@ public class LoaderTask implements Runnable { } Object traceToken = TraceHelper.INSTANCE.beginSection(TAG); - TimingLogger logger = new TimingLogger(TAG, "run"); + TimingLogger timingLogger = new TimingLogger(TAG, "run"); LoaderMemoryLogger memoryLogger = new LoaderMemoryLogger(); try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) { List allShortcuts = new ArrayList<>(); @@ -208,7 +209,7 @@ public class LoaderTask implements Runnable { } finally { Trace.endSection(); } - logASplit(logger, "loadWorkspace"); + logASplit(timingLogger, "loadWorkspace"); // Sanitize data re-syncs widgets/shortcuts based on the workspace loaded from db. // sanitizeData should not be invoked if the workspace is loaded from a db different @@ -216,22 +217,23 @@ public class LoaderTask implements Runnable { // (e.g. both grid preview and minimal device mode uses a different db) if (mApp.getInvariantDeviceProfile().dbFile.equals(mDbName)) { verifyNotStopped(); - sanitizeData(); - logASplit(logger, "sanitizeData"); + sanitizeFolders(mItemsDeleted); + sanitizeWidgetsShortcutsAndPackages(); + logASplit(timingLogger, "sanitizeData"); } verifyNotStopped(); mLauncherBinder.bindWorkspace(true /* incrementBindId */); - logASplit(logger, "bindWorkspace"); + logASplit(timingLogger, "bindWorkspace"); mModelDelegate.workspaceLoadComplete(); // Notify the installer packages of packages with active installs on the first screen. sendFirstScreenActiveInstallsBroadcast(); - logASplit(logger, "sendFirstScreenActiveInstallsBroadcast"); + logASplit(timingLogger, "sendFirstScreenActiveInstallsBroadcast"); // Take a break waitForIdle(); - logASplit(logger, "step 1 complete"); + logASplit(timingLogger, "step 1 complete"); verifyNotStopped(); // second step @@ -242,11 +244,11 @@ public class LoaderTask implements Runnable { } finally { Trace.endSection(); } - logASplit(logger, "loadAllApps"); + logASplit(timingLogger, "loadAllApps"); verifyNotStopped(); mLauncherBinder.bindAllApps(); - logASplit(logger, "bindAllApps"); + logASplit(timingLogger, "bindAllApps"); verifyNotStopped(); IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler(); @@ -254,69 +256,69 @@ public class LoaderTask implements Runnable { updateHandler.updateIcons(allActivityList, LauncherActivityCachingLogic.newInstance(mApp.getContext()), mApp.getModel()::onPackageIconsUpdated); - logASplit(logger, "update icon cache"); + logASplit(timingLogger, "update icon cache"); verifyNotStopped(); - logASplit(logger, "save shortcuts in icon cache"); + logASplit(timingLogger, "save shortcuts in icon cache"); updateHandler.updateIcons(allShortcuts, new ShortcutCachingLogic(), mApp.getModel()::onPackageIconsUpdated); // Take a break waitForIdle(); - logASplit(logger, "step 2 complete"); + logASplit(timingLogger, "step 2 complete"); verifyNotStopped(); // third step List allDeepShortcuts = loadDeepShortcuts(); - logASplit(logger, "loadDeepShortcuts"); + logASplit(timingLogger, "loadDeepShortcuts"); verifyNotStopped(); mLauncherBinder.bindDeepShortcuts(); - logASplit(logger, "bindDeepShortcuts"); + logASplit(timingLogger, "bindDeepShortcuts"); verifyNotStopped(); - logASplit(logger, "save deep shortcuts in icon cache"); + logASplit(timingLogger, "save deep shortcuts in icon cache"); updateHandler.updateIcons(allDeepShortcuts, new ShortcutCachingLogic(), (pkgs, user) -> { }); // Take a break waitForIdle(); - logASplit(logger, "step 3 complete"); + logASplit(timingLogger, "step 3 complete"); verifyNotStopped(); // fourth step List allWidgetsList = mBgDataModel.widgetsModel.update(mApp, null); - logASplit(logger, "load widgets"); + logASplit(timingLogger, "load widgets"); verifyNotStopped(); mLauncherBinder.bindWidgets(); - logASplit(logger, "bindWidgets"); + logASplit(timingLogger, "bindWidgets"); verifyNotStopped(); updateHandler.updateIcons(allWidgetsList, new ComponentWithIconCachingLogic(mApp.getContext(), true), mApp.getModel()::onWidgetLabelsUpdated); - logASplit(logger, "save widgets in icon cache"); + logASplit(timingLogger, "save widgets in icon cache"); // fifth step loadFolderNames(); verifyNotStopped(); updateHandler.finish(); - logASplit(logger, "finish icon update"); + logASplit(timingLogger, "finish icon update"); mModelDelegate.modelLoadComplete(); transaction.commit(); memoryLogger.clearLogs(); } catch (CancellationException e) { // Loader stopped, ignore - logASplit(logger, "Cancelled"); + logASplit(timingLogger, "Cancelled"); } catch (Exception e) { memoryLogger.printLogs(); throw e; } finally { - logger.dumpToLog(); + timingLogger.dumpToLog(); } TraceHelper.INSTANCE.endSection(traceToken); } @@ -326,9 +328,10 @@ public class LoaderTask implements Runnable { this.notify(); } - private void loadWorkspace(List allDeepShortcuts, LoaderMemoryLogger logger) { - loadWorkspace(allDeepShortcuts, LauncherSettings.Favorites.CONTENT_URI, - null /* selection */, logger); + private void loadWorkspace( + List allDeepShortcuts, LoaderMemoryLogger memoryLogger) { + loadWorkspace(allDeepShortcuts, Favorites.CONTENT_URI, + null /* selection */, memoryLogger); } protected void loadWorkspace( @@ -340,7 +343,7 @@ public class LoaderTask implements Runnable { List allDeepShortcuts, Uri contentUri, String selection, - @Nullable LoaderMemoryLogger logger) { + @Nullable LoaderMemoryLogger memoryLogger) { final Context context = mApp.getContext(); final ContentResolver contentResolver = context.getContentResolver(); final PackageManagerHelper pmHelper = new PackageManagerHelper(context); @@ -356,13 +359,11 @@ public class LoaderTask implements Runnable { if (clearDb) { Log.d(TAG, "loadWorkspace: resetting launcher database"); - LauncherSettings.Settings.call(contentResolver, - LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB); + Settings.call(contentResolver, Settings.METHOD_CREATE_EMPTY_DB); } Log.d(TAG, "loadWorkspace: loading default favorites"); - LauncherSettings.Settings.call(contentResolver, - LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES); + Settings.call(contentResolver, Settings.METHOD_LOAD_DEFAULT_FAVORITES); synchronized (mBgDataModel) { mBgDataModel.clear(); @@ -380,24 +381,8 @@ public class LoaderTask implements Runnable { contentResolver.query(contentUri, null, selection, null, null), contentUri, mApp, mUserManagerState); final Bundle extras = c.getExtras(); - mDbName = extras == null - ? null : extras.getString(LauncherSettings.Settings.EXTRA_DB_NAME); + mDbName = extras == null ? null : extras.getString(Settings.EXTRA_DB_NAME); try { - final int appWidgetIdIndex = c.getColumnIndexOrThrow( - LauncherSettings.Favorites.APPWIDGET_ID); - final int appWidgetProviderIndex = c.getColumnIndexOrThrow( - LauncherSettings.Favorites.APPWIDGET_PROVIDER); - final int spanXIndex = c.getColumnIndexOrThrow - (LauncherSettings.Favorites.SPANX); - final int spanYIndex = c.getColumnIndexOrThrow( - LauncherSettings.Favorites.SPANY); - final int rankIndex = c.getColumnIndexOrThrow( - LauncherSettings.Favorites.RANK); - final int optionsIndex = c.getColumnIndexOrThrow( - LauncherSettings.Favorites.OPTIONS); - final int sourceContainerIndex = c.getColumnIndexOrThrow( - LauncherSettings.Favorites.APPWIDGET_SOURCE); - final LongSparseArray unlockedUsers = new LongSparseArray<>(); mUserManagerState.init(mUserCache, mUserManager); @@ -425,437 +410,23 @@ public class LoaderTask implements Runnable { unlockedUsers.put(serialNo, userUnlocked); } - WorkspaceItemInfo info; - LauncherAppWidgetInfo appWidgetInfo; - LauncherAppWidgetProviderInfo widgetProviderInfo; - Intent intent; - String targetPkg; List> iconRequestInfos = new ArrayList<>(); while (!mStopped && c.moveToNext()) { - try { - if (c.user == null) { - // User has been deleted, remove the item. - c.markDeleted("User has been deleted"); - continue; - } - - boolean allowMissingTarget = false; - switch (c.itemType) { - case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: - case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: - case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: - intent = c.parseIntent(); - if (intent == null) { - c.markDeleted("Invalid or null intent"); - continue; - } - - int disabledState = mUserManagerState.isUserQuiet(c.serialNumber) - ? WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER : 0; - ComponentName cn = intent.getComponent(); - targetPkg = cn == null ? intent.getPackage() : cn.getPackageName(); - - if (TextUtils.isEmpty(targetPkg) && - c.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) { - c.markDeleted("Only legacy shortcuts can have null package"); - continue; - } - - // If there is no target package, its an implicit intent - // (legacy shortcut) which is always valid - boolean validTarget = TextUtils.isEmpty(targetPkg) || - mLauncherApps.isPackageEnabled(targetPkg, c.user); - - // If it's a deep shortcut, we'll use pinned shortcuts to restore it - if (cn != null && validTarget && c.itemType - != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { - // If the apk is present and the shortcut points to a specific - // component. - - // If the component is already present - if (mLauncherApps.isActivityEnabled(cn, c.user)) { - // no special handling necessary for this item - c.markRestored(); - } else { - // Gracefully try to find a fallback activity. - intent = pmHelper.getAppLaunchIntent(targetPkg, c.user); - if (intent != null) { - c.restoreFlag = 0; - c.updater().put( - LauncherSettings.Favorites.INTENT, - intent.toUri(0)).commit(); - cn = intent.getComponent(); - } else { - c.markDeleted("Unable to find a launch target"); - continue; - } - } - } - // else if cn == null => can't infer much, leave it - // else if !validPkg => could be restored icon or missing sd-card - - if (!TextUtils.isEmpty(targetPkg) && !validTarget) { - // Points to a valid app (superset of cn != null) but the apk - // is not available. - - if (c.restoreFlag != 0) { - // Package is not yet available but might be - // installed later. - FileLog.d(TAG, "package not yet restored: " + targetPkg); - - tempPackageKey.update(targetPkg, c.user); - if (c.hasRestoreFlag(WorkspaceItemInfo.FLAG_RESTORE_STARTED)) { - // Restore has started once. - } else if (installingPkgs.containsKey(tempPackageKey)) { - // App restore has started. Update the flag - c.restoreFlag |= WorkspaceItemInfo.FLAG_RESTORE_STARTED; - c.updater().put(LauncherSettings.Favorites.RESTORED, - c.restoreFlag).commit(); - } else { - c.markDeleted("Unrestored app removed: " + targetPkg); - continue; - } - } else if (pmHelper.isAppOnSdcard(targetPkg, c.user)) { - // Package is present but not available. - disabledState |= WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE; - // Add the icon on the workspace anyway. - allowMissingTarget = true; - } else if (!isSdCardReady) { - // SdCard is not ready yet. Package might get available, - // once it is ready. - Log.d(TAG, "Missing pkg, will check later: " + targetPkg); - mPendingPackages.add(new PackageUserKey(targetPkg, c.user)); - // Add the icon on the workspace anyway. - allowMissingTarget = true; - } else { - // Do not wait for external media load anymore. - c.markDeleted("Invalid package removed: " + targetPkg); - continue; - } - } - - if ((c.restoreFlag & WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI) != 0) { - validTarget = false; - } - - if (validTarget) { - // The shortcut points to a valid target (either no target - // or something which is ready to be used) - c.markRestored(); - } - - boolean useLowResIcon = !c.isOnWorkspaceOrHotseat(); - - if (c.restoreFlag != 0) { - // Already verified above that user is same as default user - info = c.getRestoredItemInfo(intent); - } else if (c.itemType == - LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { - info = c.getAppShortcutInfo( - intent, - allowMissingTarget, - useLowResIcon, - !FeatureFlags.ENABLE_BULK_WORKSPACE_ICON_LOADING.get()); - } else if (c.itemType == - LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { - - ShortcutKey key = ShortcutKey.fromIntent(intent, c.user); - if (unlockedUsers.get(c.serialNumber)) { - ShortcutInfo pinnedShortcut = - shortcutKeyToPinnedShortcuts.get(key); - if (pinnedShortcut == null) { - // The shortcut is no longer valid. - c.markDeleted("Pinned shortcut not found"); - continue; - } - info = new WorkspaceItemInfo(pinnedShortcut, context); - // If the pinned deep shortcut is no longer published, - // use the last saved icon instead of the default. - mIconCache.getShortcutIcon(info, pinnedShortcut, c::loadIcon); - - if (pmHelper.isAppSuspended( - pinnedShortcut.getPackage(), info.user)) { - info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED; - } - intent = info.getIntent(); - allDeepShortcuts.add(pinnedShortcut); - } else { - // Create a shortcut info in disabled mode for now. - info = c.loadSimpleWorkspaceItem(); - info.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER; - } - } else { // item type == ITEM_TYPE_SHORTCUT - info = c.loadSimpleWorkspaceItem(); - - // Shortcuts are only available on the primary profile - if (!TextUtils.isEmpty(targetPkg) - && pmHelper.isAppSuspended(targetPkg, c.user)) { - disabledState |= FLAG_DISABLED_SUSPENDED; - } - info.options = c.getInt(optionsIndex); - - // App shortcuts that used to be automatically added to Launcher - // didn't always have the correct intent flags set, so do that - // here - if (intent.getAction() != null && - intent.getCategories() != null && - intent.getAction().equals(Intent.ACTION_MAIN) && - intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) { - intent.addFlags( - Intent.FLAG_ACTIVITY_NEW_TASK | - Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); - } - } - - if (info != null) { - if (info.itemType - != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { - // Skip deep shortcuts; their title and icons have already been - // loaded above. - iconRequestInfos.add( - c.createIconRequestInfo(info, useLowResIcon)); - } - - c.applyCommonProperties(info); - - info.intent = intent; - info.rank = c.getInt(rankIndex); - info.spanX = 1; - info.spanY = 1; - info.runtimeStatusFlags |= disabledState; - if (isSafeMode && !isSystemApp(context, intent)) { - info.runtimeStatusFlags |= FLAG_DISABLED_SAFEMODE; - } - LauncherActivityInfo activityInfo = c.getLauncherActivityInfo(); - if (activityInfo != null) { - info.setProgressLevel( - PackageManagerHelper - .getLoadingProgress(activityInfo), - PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING); - } - - if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) { - tempPackageKey.update(targetPkg, c.user); - SessionInfo si = installingPkgs.get(tempPackageKey); - 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); - } - } - - c.checkAndAddItem(info, mBgDataModel, logger); - } else { - throw new RuntimeException("Unexpected null WorkspaceItemInfo"); - } - break; - - case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: - FolderInfo folderInfo = mBgDataModel.findOrMakeFolder(c.id); - c.applyCommonProperties(folderInfo); - - // Do not trim the folder label, as is was set by the user. - folderInfo.title = c.getString(c.titleIndex); - folderInfo.spanX = 1; - folderInfo.spanY = 1; - folderInfo.options = c.getInt(optionsIndex); - - // no special handling required for restored folders - c.markRestored(); - - c.checkAndAddItem(folderInfo, mBgDataModel, logger); - break; - - case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: - if (WidgetsModel.GO_DISABLE_WIDGETS) { - c.markDeleted("Only legacy shortcuts can have null package"); - continue; - } - // Follow through - case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: - // Read all Launcher-specific widget details - boolean customWidget = c.itemType == - LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET; - - int appWidgetId = c.getInt(appWidgetIdIndex); - String savedProvider = c.getString(appWidgetProviderIndex); - final ComponentName component; - - boolean isSearchWidget = (c.getInt(optionsIndex) - & LauncherAppWidgetInfo.OPTION_SEARCH_WIDGET) != 0; - if (isSearchWidget) { - component = QsbContainerView.getSearchComponentName(context); - if (component == null) { - c.markDeleted("Discarding SearchWidget without packagename "); - continue; - } - } else { - component = ComponentName.unflattenFromString(savedProvider); - } - final boolean isIdValid = !c.hasRestoreFlag( - LauncherAppWidgetInfo.FLAG_ID_NOT_VALID); - final boolean wasProviderReady = !c.hasRestoreFlag( - LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY); - - ComponentKey providerKey = new ComponentKey(component, c.user); - if (!mWidgetProvidersMap.containsKey(providerKey)) { - mWidgetProvidersMap.put(providerKey, - widgetHelper.findProvider(component, c.user)); - } - final AppWidgetProviderInfo provider = - mWidgetProvidersMap.get(providerKey); - - final boolean isProviderReady = isValidProvider(provider); - if (!isSafeMode && !customWidget && - wasProviderReady && !isProviderReady) { - c.markDeleted( - "Deleting widget that isn't installed anymore: " - + provider); - } else { - if (isProviderReady) { - appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, - provider.provider); - - // The provider is available. So the widget is either - // available or not available. We do not need to track - // any future restore updates. - int status = c.restoreFlag & - ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED & - ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY; - if (!wasProviderReady) { - // If provider was not previously ready, update the - // status and UI flag. - - // Id would be valid only if the widget restore broadcast was received. - if (isIdValid) { - status |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY; - } - } - appWidgetInfo.restoreStatus = status; - } else { - Log.v(TAG, "Widget restore pending id=" + c.id - + " appWidgetId=" + appWidgetId - + " status =" + c.restoreFlag); - appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, - component); - appWidgetInfo.restoreStatus = c.restoreFlag; - - tempPackageKey.update(component.getPackageName(), c.user); - SessionInfo si = - installingPkgs.get(tempPackageKey); - Integer installProgress = si == null - ? null - : (int) (si.getProgress() * 100); - - if (c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_RESTORE_STARTED)) { - // Restore has started once. - } else if (installProgress != null) { - // App restore has started. Update the flag - appWidgetInfo.restoreStatus |= - LauncherAppWidgetInfo.FLAG_RESTORE_STARTED; - } else if (!isSafeMode) { - c.markDeleted("Unrestored widget removed: " + component); - continue; - } - - appWidgetInfo.installProgress = - installProgress == null ? 0 : installProgress; - } - if (appWidgetInfo.hasRestoreFlag( - LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG)) { - appWidgetInfo.bindOptions = c.parseIntent(); - } - - c.applyCommonProperties(appWidgetInfo); - appWidgetInfo.spanX = c.getInt(spanXIndex); - appWidgetInfo.spanY = c.getInt(spanYIndex); - appWidgetInfo.options = c.getInt(optionsIndex); - appWidgetInfo.user = c.user; - appWidgetInfo.sourceContainer = c.getInt(sourceContainerIndex); - - if (appWidgetInfo.spanX <= 0 || appWidgetInfo.spanY <= 0) { - c.markDeleted("Widget has invalid size: " - + appWidgetInfo.spanX + "x" + appWidgetInfo.spanY); - continue; - } - widgetProviderInfo = - widgetHelper.getLauncherAppWidgetInfo(appWidgetId); - if (widgetProviderInfo != null - && (appWidgetInfo.spanX < widgetProviderInfo.minSpanX - || appWidgetInfo.spanY < widgetProviderInfo.minSpanY)) { - FileLog.d(TAG, "Widget " + widgetProviderInfo.getComponent() - + " minSizes not meet: span=" + appWidgetInfo.spanX - + "x" + appWidgetInfo.spanY + " minSpan=" - + widgetProviderInfo.minSpanX + "x" - + widgetProviderInfo.minSpanY); - logWidgetInfo(mApp.getInvariantDeviceProfile(), - widgetProviderInfo); - } - if (!c.isOnWorkspaceOrHotseat()) { - c.markDeleted("Widget found where container != " + - "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!"); - continue; - } - - if (!customWidget) { - String providerName = - appWidgetInfo.providerName.flattenToString(); - if (!providerName.equals(savedProvider) || - (appWidgetInfo.restoreStatus != c.restoreFlag)) { - c.updater() - .put(LauncherSettings.Favorites.APPWIDGET_PROVIDER, - providerName) - .put(LauncherSettings.Favorites.RESTORED, - appWidgetInfo.restoreStatus) - .commit(); - } - } - - if (appWidgetInfo.restoreStatus != - LauncherAppWidgetInfo.RESTORE_COMPLETED) { - appWidgetInfo.pendingItemInfo = WidgetsModel.newPendingItemInfo( - mApp.getContext(), - appWidgetInfo.providerName, - appWidgetInfo.user); - mIconCache.getTitleAndIconForApp( - appWidgetInfo.pendingItemInfo, false); - } - - c.checkAndAddItem(appWidgetInfo, mBgDataModel); - } - break; - } - } catch (Exception e) { - Log.e(TAG, "Desktop items loading interrupted", e); - } - } - if (FeatureFlags.ENABLE_BULK_WORKSPACE_ICON_LOADING.get()) { - Trace.beginSection("LoadWorkspaceIconsInBulk"); - try { - mIconCache.getTitlesAndIconsInBulk(iconRequestInfos); - for (IconRequestInfo iconRequestInfo : - iconRequestInfos) { - WorkspaceItemInfo wai = iconRequestInfo.itemInfo; - if (mIconCache.isDefaultIcon(wai.bitmap, wai.user)) { - iconRequestInfo.loadWorkspaceIcon(mApp.getContext()); - } - } - } finally { - Trace.endSection(); - } + processWorkspaceItem(c, memoryLogger, installingPkgs, isSdCardReady, + tempPackageKey, widgetHelper, pmHelper, shortcutKeyToPinnedShortcuts, + iconRequestInfos, unlockedUsers, isSafeMode, allDeepShortcuts); } + maybeLoadWorkspaceIconsInBulk(iconRequestInfos); } finally { IOUtils.closeSilently(c); } // Load delegate items - mModelDelegate.loadItems(mUserManagerState, shortcutKeyToPinnedShortcuts); + mModelDelegate.loadHotseatItems(mUserManagerState, shortcutKeyToPinnedShortcuts); + mModelDelegate.loadAllAppsItems(mUserManagerState, shortcutKeyToPinnedShortcuts); + mModelDelegate.loadWidgetsRecommendationItems(); + mModelDelegate.markActive(); // Load string cache mModelDelegate.loadStringCache(mBgDataModel.stringCache); @@ -885,7 +456,7 @@ public class LoaderTask implements Runnable { info.rank = rank; if (info.usingLowResIcon() - && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION + && info.itemType == Favorites.ITEM_TYPE_APPLICATION && verifier.isItemInPreview(info.rank)) { mIconCache.getTitleAndIcon(info, false); } @@ -896,6 +467,418 @@ public class LoaderTask implements Runnable { } } + private void processWorkspaceItem(LoaderCursor c, + LoaderMemoryLogger memoryLogger, + HashMap installingPkgs, + boolean isSdCardReady, + PackageUserKey tempPackageKey, + WidgetManagerHelper widgetHelper, + PackageManagerHelper pmHelper, + Map shortcutKeyToPinnedShortcuts, + List> iconRequestInfos, + LongSparseArray unlockedUsers, + boolean isSafeMode, + List allDeepShortcuts) { + + try { + if (c.user == null) { + // User has been deleted, remove the item. + c.markDeleted("User has been deleted"); + return; + } + + boolean allowMissingTarget = false; + switch (c.itemType) { + case Favorites.ITEM_TYPE_SHORTCUT: + case Favorites.ITEM_TYPE_APPLICATION: + case Favorites.ITEM_TYPE_DEEP_SHORTCUT: + Intent intent = c.parseIntent(); + if (intent == null) { + c.markDeleted("Invalid or null intent"); + return; + } + + int disabledState = mUserManagerState.isUserQuiet(c.serialNumber) + ? WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER : 0; + ComponentName cn = intent.getComponent(); + String targetPkg = cn == null ? intent.getPackage() : cn.getPackageName(); + + if (TextUtils.isEmpty(targetPkg) + && c.itemType != Favorites.ITEM_TYPE_SHORTCUT) { + c.markDeleted("Only legacy shortcuts can have null package"); + return; + } + + // If there is no target package, it's an implicit intent + // (legacy shortcut) which is always valid + boolean validTarget = TextUtils.isEmpty(targetPkg) + || mLauncherApps.isPackageEnabled(targetPkg, c.user); + + // If it's a deep shortcut, we'll use pinned shortcuts to restore it + if (cn != null && validTarget && c.itemType + != Favorites.ITEM_TYPE_DEEP_SHORTCUT) { + // If the apk is present and the shortcut points to a specific component. + + // If the component is already present + if (mLauncherApps.isActivityEnabled(cn, c.user)) { + // no special handling necessary for this item + c.markRestored(); + } else { + // Gracefully try to find a fallback activity. + intent = pmHelper.getAppLaunchIntent(targetPkg, c.user); + if (intent != null) { + c.restoreFlag = 0; + c.updater().put( + Favorites.INTENT, + intent.toUri(0)).commit(); + cn = intent.getComponent(); + } else { + c.markDeleted("Unable to find a launch target"); + return; + } + } + } + // else if cn == null => can't infer much, leave it + // else if !validPkg => could be restored icon or missing sd-card + + if (!TextUtils.isEmpty(targetPkg) && !validTarget) { + // Points to a valid app (superset of cn != null) but the apk + // is not available. + + if (c.restoreFlag != 0) { + // Package is not yet available but might be + // installed later. + FileLog.d(TAG, "package not yet restored: " + targetPkg); + + tempPackageKey.update(targetPkg, c.user); + if (c.hasRestoreFlag(WorkspaceItemInfo.FLAG_RESTORE_STARTED)) { + // Restore has started once. + } else if (installingPkgs.containsKey(tempPackageKey)) { + // App restore has started. Update the flag + c.restoreFlag |= WorkspaceItemInfo.FLAG_RESTORE_STARTED; + c.updater().put(Favorites.RESTORED, + c.restoreFlag).commit(); + } else { + c.markDeleted("Unrestored app removed: " + targetPkg); + return; + } + } else if (pmHelper.isAppOnSdcard(targetPkg, c.user)) { + // Package is present but not available. + disabledState |= WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE; + // Add the icon on the workspace anyway. + allowMissingTarget = true; + } else if (!isSdCardReady) { + // SdCard is not ready yet. Package might get available, + // once it is ready. + Log.d(TAG, "Missing pkg, will check later: " + targetPkg); + mPendingPackages.add(new PackageUserKey(targetPkg, c.user)); + // Add the icon on the workspace anyway. + allowMissingTarget = true; + } else { + // Do not wait for external media load anymore. + c.markDeleted("Invalid package removed: " + targetPkg); + return; + } + } + + if ((c.restoreFlag & WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI) != 0) { + validTarget = false; + } + + if (validTarget) { + // The shortcut points to a valid target (either no target + // or something which is ready to be used) + c.markRestored(); + } + + boolean useLowResIcon = !c.isOnWorkspaceOrHotseat(); + + WorkspaceItemInfo info; + if (c.restoreFlag != 0) { + // Already verified above that user is same as default user + info = c.getRestoredItemInfo(intent); + } else if (c.itemType == Favorites.ITEM_TYPE_APPLICATION) { + info = c.getAppShortcutInfo(intent, allowMissingTarget, useLowResIcon, + !FeatureFlags.ENABLE_BULK_WORKSPACE_ICON_LOADING.get()); + } else if (c.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) { + ShortcutKey key = ShortcutKey.fromIntent(intent, c.user); + if (unlockedUsers.get(c.serialNumber)) { + ShortcutInfo pinnedShortcut = shortcutKeyToPinnedShortcuts.get(key); + if (pinnedShortcut == null) { + // The shortcut is no longer valid. + c.markDeleted("Pinned shortcut not found"); + return; + } + info = new WorkspaceItemInfo(pinnedShortcut, mApp.getContext()); + // If the pinned deep shortcut is no longer published, + // use the last saved icon instead of the default. + mIconCache.getShortcutIcon(info, pinnedShortcut, c::loadIcon); + + if (pmHelper.isAppSuspended( + pinnedShortcut.getPackage(), info.user)) { + info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED; + } + intent = info.getIntent(); + allDeepShortcuts.add(pinnedShortcut); + } else { + // Create a shortcut info in disabled mode for now. + info = c.loadSimpleWorkspaceItem(); + info.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER; + } + } else { // item type == ITEM_TYPE_SHORTCUT + info = c.loadSimpleWorkspaceItem(); + + // Shortcuts are only available on the primary profile + if (!TextUtils.isEmpty(targetPkg) + && pmHelper.isAppSuspended(targetPkg, c.user)) { + disabledState |= FLAG_DISABLED_SUSPENDED; + } + info.options = c.getOptions(); + + // App shortcuts that used to be automatically added to Launcher + // didn't always have the correct intent flags set, so do that here + if (intent.getAction() != null + && intent.getCategories() != null + && intent.getAction().equals(Intent.ACTION_MAIN) + && intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) { + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + } + } + + if (info != null) { + if (info.itemType != Favorites.ITEM_TYPE_DEEP_SHORTCUT) { + // Skip deep shortcuts; their title and icons have already been + // loaded above. + iconRequestInfos.add(c.createIconRequestInfo(info, useLowResIcon)); + } + + c.applyCommonProperties(info); + + info.intent = intent; + info.rank = c.getRank(); + info.spanX = 1; + info.spanY = 1; + info.runtimeStatusFlags |= disabledState; + if (isSafeMode && !isSystemApp(mApp.getContext(), intent)) { + info.runtimeStatusFlags |= FLAG_DISABLED_SAFEMODE; + } + LauncherActivityInfo activityInfo = c.getLauncherActivityInfo(); + if (activityInfo != null) { + info.setProgressLevel( + PackageManagerHelper.getLoadingProgress(activityInfo), + PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING); + } + + if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) { + tempPackageKey.update(targetPkg, c.user); + SessionInfo si = installingPkgs.get(tempPackageKey); + 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); + } + } + + c.checkAndAddItem(info, mBgDataModel, memoryLogger); + } else { + throw new RuntimeException("Unexpected null WorkspaceItemInfo"); + } + break; + + case Favorites.ITEM_TYPE_FOLDER: + FolderInfo folderInfo = mBgDataModel.findOrMakeFolder(c.id); + c.applyCommonProperties(folderInfo); + + // Do not trim the folder label, as is was set by the user. + folderInfo.title = c.getString(c.mTitleIndex); + folderInfo.spanX = 1; + folderInfo.spanY = 1; + folderInfo.options = c.getOptions(); + + // no special handling required for restored folders + c.markRestored(); + + c.checkAndAddItem(folderInfo, mBgDataModel, memoryLogger); + break; + + case Favorites.ITEM_TYPE_APPWIDGET: + if (WidgetsModel.GO_DISABLE_WIDGETS) { + c.markDeleted("Only legacy shortcuts can have null package"); + return; + } + // Follow through + case Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: + // Read all Launcher-specific widget details + boolean customWidget = c.itemType + == Favorites.ITEM_TYPE_CUSTOM_APPWIDGET; + + int appWidgetId = c.getAppWidgetId(); + String savedProvider = c.getAppWidgetProvider(); + final ComponentName component; + + if ((c.getOptions() & LauncherAppWidgetInfo.OPTION_SEARCH_WIDGET) != 0) { + component = QsbContainerView.getSearchComponentName(mApp.getContext()); + if (component == null) { + c.markDeleted("Discarding SearchWidget without packagename "); + return; + } + } else { + component = ComponentName.unflattenFromString(savedProvider); + } + final boolean isIdValid = + !c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID); + final boolean wasProviderReady = + !c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY); + + ComponentKey providerKey = new ComponentKey(component, c.user); + if (!mWidgetProvidersMap.containsKey(providerKey)) { + mWidgetProvidersMap.put(providerKey, + widgetHelper.findProvider(component, c.user)); + } + final AppWidgetProviderInfo provider = mWidgetProvidersMap.get(providerKey); + + final boolean isProviderReady = isValidProvider(provider); + if (!isSafeMode && !customWidget && wasProviderReady && !isProviderReady) { + c.markDeleted("Deleting widget that isn't installed anymore: " + provider); + } else { + LauncherAppWidgetInfo appWidgetInfo; + if (isProviderReady) { + appWidgetInfo = + new LauncherAppWidgetInfo(appWidgetId, provider.provider); + + // The provider is available. So the widget is either + // available or not available. We do not need to track + // any future restore updates. + int status = c.restoreFlag + & ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED + & ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY; + if (!wasProviderReady) { + // If provider was not previously ready, update status and UI flag. + + // Id would be valid only if the widget restore broadcast received. + if (isIdValid) { + status |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY; + } + } + appWidgetInfo.restoreStatus = status; + } else { + Log.v(TAG, "Widget restore pending id=" + c.id + + " appWidgetId=" + appWidgetId + + " status =" + c.restoreFlag); + appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, component); + appWidgetInfo.restoreStatus = c.restoreFlag; + + tempPackageKey.update(component.getPackageName(), c.user); + SessionInfo si = installingPkgs.get(tempPackageKey); + Integer installProgress = si == null + ? null + : (int) (si.getProgress() * 100); + + if (c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_RESTORE_STARTED)) { + // Restore has started once. + } else if (installProgress != null) { + // App restore has started. Update the flag + appWidgetInfo.restoreStatus + |= LauncherAppWidgetInfo.FLAG_RESTORE_STARTED; + } else if (!isSafeMode) { + c.markDeleted("Unrestored widget removed: " + component); + return; + } + + appWidgetInfo.installProgress = + installProgress == null ? 0 : installProgress; + } + if (appWidgetInfo.hasRestoreFlag( + LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG)) { + appWidgetInfo.bindOptions = c.parseIntent(); + } + + c.applyCommonProperties(appWidgetInfo); + appWidgetInfo.spanX = c.getSpanX(); + appWidgetInfo.spanY = c.getSpanY(); + appWidgetInfo.options = c.getOptions(); + appWidgetInfo.user = c.user; + appWidgetInfo.sourceContainer = c.getAppWidgetSource(); + + if (appWidgetInfo.spanX <= 0 || appWidgetInfo.spanY <= 0) { + c.markDeleted("Widget has invalid size: " + + appWidgetInfo.spanX + "x" + appWidgetInfo.spanY); + return; + } + LauncherAppWidgetProviderInfo widgetProviderInfo = + widgetHelper.getLauncherAppWidgetInfo(appWidgetId); + if (widgetProviderInfo != null + && (appWidgetInfo.spanX < widgetProviderInfo.minSpanX + || appWidgetInfo.spanY < widgetProviderInfo.minSpanY)) { + FileLog.d(TAG, "Widget " + widgetProviderInfo.getComponent() + + " minSizes not meet: span=" + appWidgetInfo.spanX + + "x" + appWidgetInfo.spanY + " minSpan=" + + widgetProviderInfo.minSpanX + "x" + + widgetProviderInfo.minSpanY); + logWidgetInfo(mApp.getInvariantDeviceProfile(), + widgetProviderInfo); + } + if (!c.isOnWorkspaceOrHotseat()) { + c.markDeleted("Widget found where container != CONTAINER_DESKTOP" + + "nor CONTAINER_HOTSEAT - ignoring!"); + return; + } + + if (!customWidget) { + String providerName = appWidgetInfo.providerName.flattenToString(); + if (!providerName.equals(savedProvider) + || (appWidgetInfo.restoreStatus != c.restoreFlag)) { + c.updater() + .put(Favorites.APPWIDGET_PROVIDER, + providerName) + .put(Favorites.RESTORED, + appWidgetInfo.restoreStatus) + .commit(); + } + } + + if (appWidgetInfo.restoreStatus + != LauncherAppWidgetInfo.RESTORE_COMPLETED) { + appWidgetInfo.pendingItemInfo = WidgetsModel.newPendingItemInfo( + mApp.getContext(), + appWidgetInfo.providerName, + appWidgetInfo.user); + mIconCache.getTitleAndIconForApp( + appWidgetInfo.pendingItemInfo, false); + } + + c.checkAndAddItem(appWidgetInfo, mBgDataModel); + } + break; + } + } catch (Exception e) { + Log.e(TAG, "Desktop items loading interrupted", e); + } + } + + private void maybeLoadWorkspaceIconsInBulk( + List> iconRequestInfos) { + if (FeatureFlags.ENABLE_BULK_WORKSPACE_ICON_LOADING.get()) { + Trace.beginSection("LoadWorkspaceIconsInBulk"); + try { + mIconCache.getTitlesAndIconsInBulk(iconRequestInfos); + for (IconRequestInfo iconRequestInfo : iconRequestInfos) { + WorkspaceItemInfo wai = iconRequestInfo.itemInfo; + if (mIconCache.isDefaultIcon(wai.bitmap, wai.user)) { + iconRequestInfo.loadWorkspaceIcon(mApp.getContext()); + } + } + } finally { + Trace.endSection(); + } + } + } + private void setIgnorePackages(IconCacheUpdateHandler updateHandler) { // Ignore packages which have a promise icon. synchronized (mBgDataModel) { @@ -917,15 +900,12 @@ public class LoaderTask implements Runnable { } } - private void sanitizeData() { - Context context = mApp.getContext(); - ContentResolver contentResolver = context.getContentResolver(); - if (mItemsDeleted) { + private void sanitizeFolders(boolean itemsDeleted) { + if (itemsDeleted) { // Remove any empty folder - int[] deletedFolderIds = LauncherSettings.Settings - .call(contentResolver, - LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS) - .getIntArray(LauncherSettings.Settings.EXTRA_VALUE); + int[] deletedFolderIds = Settings.call(mApp.getContext().getContentResolver(), + Settings.METHOD_DELETE_EMPTY_FOLDERS) + .getIntArray(Settings.EXTRA_VALUE); synchronized (mBgDataModel) { for (int folderId : deletedFolderIds) { mBgDataModel.workspaceItems.remove(mBgDataModel.folders.get(folderId)); @@ -933,11 +913,16 @@ public class LoaderTask implements Runnable { mBgDataModel.itemsIdMap.remove(folderId); } } - } + } + + private void sanitizeWidgetsShortcutsAndPackages() { + Context context = mApp.getContext(); + ContentResolver contentResolver = context.getContentResolver(); + // Remove any ghost widgets - LauncherSettings.Settings.call(contentResolver, - LauncherSettings.Settings.METHOD_REMOVE_GHOST_WIDGETS); + Settings.call(contentResolver, + Settings.METHOD_REMOVE_GHOST_WIDGETS); // Update pinned state of model shortcuts mBgDataModel.updateShortcutPinnedState(context); @@ -1107,10 +1092,12 @@ public class LoaderTask implements Runnable { FileLog.d(TAG, widgetDimension.toString()); } - private static void logASplit(final TimingLogger logger, final String label) { - logger.addSplit(label); - if (DEBUG) { - Log.d(TAG, label); + private static void logASplit(@Nullable TimingLogger timingLogger, String label) { + if (timingLogger != null) { + timingLogger.addSplit(label); + if (DEBUG) { + Log.d(TAG, label); + } } } } diff --git a/src/com/android/launcher3/model/ModelDelegate.java b/src/com/android/launcher3/model/ModelDelegate.java index 3bd9470566..0639a6cbe2 100644 --- a/src/com/android/launcher3/model/ModelDelegate.java +++ b/src/com/android/launcher3/model/ModelDelegate.java @@ -80,10 +80,29 @@ public class ModelDelegate implements ResourceBasedOverride { } /** - * Load delegate items if any in the data model + * Load hot seat items if any in the data model */ @WorkerThread - public void loadItems(UserManagerState ums, Map pinnedShortcuts) { } + public void loadHotseatItems(UserManagerState ums, + Map pinnedShortcuts) { } + + /** + * Load all apps items if any in the data model + */ + @WorkerThread + public void loadAllAppsItems(UserManagerState ums, + Map pinnedShortcuts) { } + + /** + * Load widget recommendation items if any in the data model + */ + @WorkerThread + public void loadWidgetsRecommendationItems() { } + + /** + * Marks the ModelDelegate as active + */ + public void markActive() { } /** * Load String cache diff --git a/tests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/src/com/android/launcher3/model/LoaderCursorTest.java index 6444ef6927..7ab86ad4fa 100644 --- a/tests/src/com/android/launcher3/model/LoaderCursorTest.java +++ b/tests/src/com/android/launcher3/model/LoaderCursorTest.java @@ -18,6 +18,9 @@ package com.android.launcher3.model; import static androidx.test.InstrumentationRegistry.getContext; +import static com.android.launcher3.LauncherSettings.Favorites.APPWIDGET_ID; +import static com.android.launcher3.LauncherSettings.Favorites.APPWIDGET_PROVIDER; +import static com.android.launcher3.LauncherSettings.Favorites.APPWIDGET_SOURCE; import static com.android.launcher3.LauncherSettings.Favorites.CELLX; import static com.android.launcher3.LauncherSettings.Favorites.CELLY; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER; @@ -30,9 +33,13 @@ import static com.android.launcher3.LauncherSettings.Favorites.INTENT; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; +import static com.android.launcher3.LauncherSettings.Favorites.OPTIONS; import static com.android.launcher3.LauncherSettings.Favorites.PROFILE_ID; +import static com.android.launcher3.LauncherSettings.Favorites.RANK; import static com.android.launcher3.LauncherSettings.Favorites.RESTORED; import static com.android.launcher3.LauncherSettings.Favorites.SCREEN; +import static com.android.launcher3.LauncherSettings.Favorites.SPANX; +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.util.LauncherModelHelper.TEST_ACTIVITY; @@ -92,7 +99,9 @@ public class LoaderCursorTest { mCursor = new MatrixCursor(new String[] { ICON, ICON_PACKAGE, ICON_RESOURCE, TITLE, _ID, CONTAINER, ITEM_TYPE, PROFILE_ID, - SCREEN, CELLX, CELLY, RESTORED, INTENT + SCREEN, CELLX, CELLY, RESTORED, INTENT, + APPWIDGET_ID, APPWIDGET_PROVIDER, SPANX, + SPANY, RANK, OPTIONS, APPWIDGET_SOURCE }); UserManagerState ums = new UserManagerState();