| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.launcher3.model; |
| |
| import static com.android.launcher3.BuildConfig.WIDGET_ON_FIRST_SCREEN; |
| import static com.android.launcher3.Flags.enableLauncherBrMetricsFixed; |
| import static com.android.launcher3.LauncherPrefs.IS_FIRST_LOAD_AFTER_RESTORE; |
| import static com.android.launcher3.LauncherPrefs.SHOULD_SHOW_SMARTSPACE; |
| import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR; |
| import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME; |
| import static com.android.launcher3.config.FeatureFlags.ENABLE_SMARTSPACE_REMOVAL; |
| import static com.android.launcher3.config.FeatureFlags.SMARTSPACE_AS_A_WIDGET; |
| 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; |
| import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED; |
| import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_WORK_PROFILE_QUIET_MODE_ENABLED; |
| import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems; |
| import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE; |
| import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; |
| import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission; |
| |
| import android.appwidget.AppWidgetProviderInfo; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.content.pm.LauncherActivityInfo; |
| import android.content.pm.LauncherApps; |
| import android.content.pm.PackageInstaller; |
| import android.content.pm.PackageInstaller.SessionInfo; |
| import android.content.pm.PackageManager; |
| import android.content.pm.ShortcutInfo; |
| import android.os.Bundle; |
| import android.os.Trace; |
| import android.os.UserHandle; |
| import android.os.UserManager; |
| import android.util.ArrayMap; |
| import android.util.Log; |
| import android.util.LongSparseArray; |
| |
| import androidx.annotation.NonNull; |
| import androidx.annotation.Nullable; |
| import androidx.annotation.VisibleForTesting; |
| import androidx.annotation.WorkerThread; |
| |
| import com.android.launcher3.Flags; |
| import com.android.launcher3.LauncherAppState; |
| import com.android.launcher3.LauncherModel; |
| import com.android.launcher3.LauncherPrefs; |
| import com.android.launcher3.LauncherSettings.Favorites; |
| import com.android.launcher3.Utilities; |
| import com.android.launcher3.backuprestore.LauncherRestoreEventLogger; |
| import com.android.launcher3.config.FeatureFlags; |
| import com.android.launcher3.folder.Folder; |
| import com.android.launcher3.folder.FolderGridOrganizer; |
| import com.android.launcher3.folder.FolderNameInfos; |
| import com.android.launcher3.folder.FolderNameProvider; |
| import com.android.launcher3.icons.ComponentWithLabelAndIcon; |
| import com.android.launcher3.icons.ComponentWithLabelAndIcon.ComponentWithIconCachingLogic; |
| import com.android.launcher3.icons.IconCache; |
| import com.android.launcher3.icons.LauncherActivityCachingLogic; |
| import com.android.launcher3.icons.ShortcutCachingLogic; |
| import com.android.launcher3.icons.cache.IconCacheUpdateHandler; |
| import com.android.launcher3.logging.FileLog; |
| import com.android.launcher3.model.data.AppInfo; |
| import com.android.launcher3.model.data.FolderInfo; |
| import com.android.launcher3.model.data.IconRequestInfo; |
| import com.android.launcher3.model.data.ItemInfo; |
| import com.android.launcher3.model.data.LauncherAppWidgetInfo; |
| import com.android.launcher3.model.data.WorkspaceItemInfo; |
| import com.android.launcher3.pm.InstallSessionHelper; |
| import com.android.launcher3.pm.PackageInstallInfo; |
| import com.android.launcher3.pm.UserCache; |
| import com.android.launcher3.shortcuts.ShortcutKey; |
| import com.android.launcher3.shortcuts.ShortcutRequest; |
| import com.android.launcher3.shortcuts.ShortcutRequest.QueryResult; |
| import com.android.launcher3.util.ComponentKey; |
| import com.android.launcher3.util.IOUtils; |
| import com.android.launcher3.util.IntArray; |
| import com.android.launcher3.util.IntSet; |
| import com.android.launcher3.util.LooperIdleLock; |
| import com.android.launcher3.util.PackageManagerHelper; |
| import com.android.launcher3.util.PackageUserKey; |
| import com.android.launcher3.util.TraceHelper; |
| import com.android.launcher3.widget.WidgetInflater; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.Set; |
| import java.util.concurrent.CancellationException; |
| |
| /** |
| * Runnable for the thread that loads the contents of the launcher: |
| * - workspace icons |
| * - widgets |
| * - all apps icons |
| * - deep shortcuts within apps |
| */ |
| @SuppressWarnings("NewApi") |
| public class LoaderTask implements Runnable { |
| private static final String TAG = "LoaderTask"; |
| public static final String SMARTSPACE_ON_HOME_SCREEN = "pref_smartspace_home_screen"; |
| |
| private static final boolean DEBUG = true; |
| |
| @NonNull |
| protected final LauncherAppState mApp; |
| private final AllAppsList mBgAllAppsList; |
| protected final BgDataModel mBgDataModel; |
| private final ModelDelegate mModelDelegate; |
| private boolean mIsRestoreFromBackup; |
| |
| private FirstScreenBroadcast mFirstScreenBroadcast; |
| |
| @NonNull |
| private final LauncherBinder mLauncherBinder; |
| |
| private final LauncherApps mLauncherApps; |
| private final UserManager mUserManager; |
| private final UserCache mUserCache; |
| |
| private final InstallSessionHelper mSessionHelper; |
| private final IconCache mIconCache; |
| |
| private final UserManagerState mUserManagerState; |
| protected final Map<ComponentKey, AppWidgetProviderInfo> mWidgetProvidersMap = new ArrayMap<>(); |
| private Map<ShortcutKey, ShortcutInfo> mShortcutKeyToPinnedShortcuts; |
| private HashMap<PackageUserKey, SessionInfo> mInstallingPkgsCached; |
| |
| private boolean mStopped; |
| |
| private final Set<PackageUserKey> mPendingPackages = new HashSet<>(); |
| private boolean mItemsDeleted = false; |
| private String mDbName; |
| |
| public LoaderTask(@NonNull LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel bgModel, |
| ModelDelegate modelDelegate, @NonNull LauncherBinder launcherBinder) { |
| this(app, bgAllAppsList, bgModel, modelDelegate, launcherBinder, new UserManagerState()); |
| } |
| |
| @VisibleForTesting |
| LoaderTask(@NonNull LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel bgModel, |
| ModelDelegate modelDelegate, @NonNull LauncherBinder launcherBinder, |
| UserManagerState userManagerState) { |
| mApp = app; |
| mBgAllAppsList = bgAllAppsList; |
| mBgDataModel = bgModel; |
| mModelDelegate = modelDelegate; |
| mLauncherBinder = launcherBinder; |
| mLauncherApps = mApp.getContext().getSystemService(LauncherApps.class); |
| mUserManager = mApp.getContext().getSystemService(UserManager.class); |
| mUserCache = UserCache.INSTANCE.get(mApp.getContext()); |
| mSessionHelper = InstallSessionHelper.INSTANCE.get(mApp.getContext()); |
| mIconCache = mApp.getIconCache(); |
| mUserManagerState = userManagerState; |
| mInstallingPkgsCached = null; |
| } |
| |
| protected synchronized void waitForIdle() { |
| // Wait until the either we're stopped or the other threads are done. |
| // This way we don't start loading all apps until the workspace has settled |
| // down. |
| LooperIdleLock idleLock = mLauncherBinder.newIdleLock(this); |
| // Just in case mFlushingWorkerThread changes but we aren't woken up, |
| // wait no longer than 1sec at a time |
| while (!mStopped && idleLock.awaitLocked(1000)); |
| } |
| |
| private synchronized void verifyNotStopped() throws CancellationException { |
| if (mStopped) { |
| throw new CancellationException("Loader stopped"); |
| } |
| } |
| |
| private void sendFirstScreenActiveInstallsBroadcast() { |
| ArrayList<ItemInfo> firstScreenItems = new ArrayList<>(); |
| ArrayList<ItemInfo> allItems = mBgDataModel.getAllWorkspaceItems(); |
| |
| // Screen set is never empty |
| IntArray allScreens = mBgDataModel.collectWorkspaceScreens(); |
| final int firstScreen = allScreens.get(0); |
| IntSet firstScreens = IntSet.wrap(firstScreen); |
| |
| filterCurrentWorkspaceItems(firstScreens, allItems, firstScreenItems, |
| new ArrayList<>() /* otherScreenItems are ignored */); |
| mFirstScreenBroadcast.sendBroadcasts(mApp.getContext(), firstScreenItems); |
| } |
| |
| public void run() { |
| synchronized (this) { |
| // Skip fast if we are already stopped. |
| if (mStopped) { |
| return; |
| } |
| } |
| |
| TraceHelper.INSTANCE.beginSection(TAG); |
| LoaderMemoryLogger memoryLogger = new LoaderMemoryLogger(); |
| mIsRestoreFromBackup = |
| (Boolean) LauncherPrefs.get(mApp.getContext()).get(IS_FIRST_LOAD_AFTER_RESTORE); |
| LauncherRestoreEventLogger restoreEventLogger = null; |
| if (enableLauncherBrMetricsFixed()) { |
| restoreEventLogger = LauncherRestoreEventLogger.Companion |
| .newInstance(mApp.getContext()); |
| } |
| try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) { |
| |
| List<ShortcutInfo> allShortcuts = new ArrayList<>(); |
| loadWorkspace(allShortcuts, "", memoryLogger, restoreEventLogger); |
| |
| // 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 |
| // from the main db as defined in the invariant device profile. |
| // (e.g. both grid preview and minimal device mode uses a different db) |
| if (Objects.equals(mApp.getInvariantDeviceProfile().dbFile, mDbName)) { |
| verifyNotStopped(); |
| sanitizeFolders(mItemsDeleted); |
| sanitizeWidgetsShortcutsAndPackages(); |
| logASplit("sanitizeData"); |
| } |
| |
| verifyNotStopped(); |
| mLauncherBinder.bindWorkspace(true /* incrementBindId */, /* isBindSync= */ false); |
| logASplit("bindWorkspace"); |
| |
| mModelDelegate.workspaceLoadComplete(); |
| // Notify the installer packages of packages with active installs on the first screen. |
| sendFirstScreenActiveInstallsBroadcast(); |
| logASplit("sendFirstScreenActiveInstallsBroadcast"); |
| |
| // Take a break |
| waitForIdle(); |
| logASplit("step 1 complete"); |
| verifyNotStopped(); |
| |
| // second step |
| Trace.beginSection("LoadAllApps"); |
| List<LauncherActivityInfo> allActivityList; |
| try { |
| allActivityList = loadAllApps(); |
| } finally { |
| Trace.endSection(); |
| } |
| logASplit("loadAllApps"); |
| |
| if (FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) { |
| mModelDelegate.loadAndBindAllAppsItems(mUserManagerState, |
| mLauncherBinder.mCallbacksList, mShortcutKeyToPinnedShortcuts); |
| logASplit("allAppsDelegateItems"); |
| } |
| verifyNotStopped(); |
| mLauncherBinder.bindAllApps(); |
| logASplit("bindAllApps"); |
| |
| verifyNotStopped(); |
| IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler(); |
| setIgnorePackages(updateHandler); |
| updateHandler.updateIcons(allActivityList, |
| LauncherActivityCachingLogic.newInstance(mApp.getContext()), |
| mApp.getModel()::onPackageIconsUpdated); |
| logASplit("update icon cache"); |
| |
| verifyNotStopped(); |
| logASplit("save shortcuts in icon cache"); |
| updateHandler.updateIcons(allShortcuts, new ShortcutCachingLogic(), |
| mApp.getModel()::onPackageIconsUpdated); |
| |
| // Take a break |
| waitForIdle(); |
| logASplit("step 2 complete"); |
| verifyNotStopped(); |
| |
| // third step |
| List<ShortcutInfo> allDeepShortcuts = loadDeepShortcuts(); |
| logASplit("loadDeepShortcuts"); |
| |
| verifyNotStopped(); |
| mLauncherBinder.bindDeepShortcuts(); |
| logASplit("bindDeepShortcuts"); |
| |
| verifyNotStopped(); |
| logASplit("save deep shortcuts in icon cache"); |
| updateHandler.updateIcons(allDeepShortcuts, |
| new ShortcutCachingLogic(), (pkgs, user) -> { }); |
| |
| // Take a break |
| waitForIdle(); |
| logASplit("step 3 complete"); |
| verifyNotStopped(); |
| |
| // fourth step |
| List<ComponentWithLabelAndIcon> allWidgetsList = |
| mBgDataModel.widgetsModel.update(mApp, null); |
| logASplit("load widgets"); |
| |
| verifyNotStopped(); |
| mLauncherBinder.bindWidgets(); |
| logASplit("bindWidgets"); |
| verifyNotStopped(); |
| LauncherPrefs prefs = LauncherPrefs.get(mApp.getContext()); |
| |
| if (SMARTSPACE_AS_A_WIDGET.get() && prefs.get(SHOULD_SHOW_SMARTSPACE)) { |
| mLauncherBinder.bindSmartspaceWidget(); |
| // Turn off pref. |
| prefs.putSync(SHOULD_SHOW_SMARTSPACE.to(false)); |
| logASplit("bindSmartspaceWidget"); |
| verifyNotStopped(); |
| } else if (!SMARTSPACE_AS_A_WIDGET.get() && WIDGET_ON_FIRST_SCREEN |
| && !prefs.get(LauncherPrefs.SHOULD_SHOW_SMARTSPACE)) { |
| // Turn on pref. |
| prefs.putSync(SHOULD_SHOW_SMARTSPACE.to(true)); |
| } |
| |
| if (FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) { |
| mModelDelegate.loadAndBindOtherItems(mLauncherBinder.mCallbacksList); |
| logASplit("otherDelegateItems"); |
| verifyNotStopped(); |
| } |
| |
| updateHandler.updateIcons(allWidgetsList, |
| new ComponentWithIconCachingLogic(mApp.getContext(), true), |
| mApp.getModel()::onWidgetLabelsUpdated); |
| logASplit("save widgets in icon cache"); |
| |
| // fifth step |
| loadFolderNames(); |
| |
| verifyNotStopped(); |
| updateHandler.finish(); |
| logASplit("finish icon update"); |
| |
| mModelDelegate.modelLoadComplete(); |
| transaction.commit(); |
| memoryLogger.clearLogs(); |
| if (mIsRestoreFromBackup) { |
| mIsRestoreFromBackup = false; |
| LauncherPrefs.get(mApp.getContext()).putSync(IS_FIRST_LOAD_AFTER_RESTORE.to(false)); |
| if (restoreEventLogger != null) { |
| restoreEventLogger.reportLauncherRestoreResults(); |
| } |
| } |
| } catch (CancellationException e) { |
| // Loader stopped, ignore |
| logASplit("Cancelled"); |
| } catch (Exception e) { |
| memoryLogger.printLogs(); |
| throw e; |
| } |
| TraceHelper.INSTANCE.endSection(); |
| } |
| |
| public synchronized void stopLocked() { |
| mStopped = true; |
| this.notify(); |
| } |
| |
| protected void loadWorkspace( |
| List<ShortcutInfo> allDeepShortcuts, |
| String selection, |
| LoaderMemoryLogger memoryLogger, |
| @Nullable LauncherRestoreEventLogger restoreEventLogger |
| ) { |
| Trace.beginSection("LoadWorkspace"); |
| try { |
| loadWorkspaceImpl(allDeepShortcuts, selection, memoryLogger, restoreEventLogger); |
| } finally { |
| Trace.endSection(); |
| } |
| logASplit("loadWorkspace"); |
| |
| if (FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) { |
| verifyNotStopped(); |
| mModelDelegate.loadAndBindWorkspaceItems(mUserManagerState, |
| mLauncherBinder.mCallbacksList, mShortcutKeyToPinnedShortcuts); |
| mModelDelegate.markActive(); |
| logASplit("workspaceDelegateItems"); |
| } |
| mBgDataModel.isFirstPagePinnedItemEnabled = FeatureFlags.QSB_ON_FIRST_SCREEN |
| && (!ENABLE_SMARTSPACE_REMOVAL.get() || LauncherPrefs.getPrefs( |
| mApp.getContext()).getBoolean(SMARTSPACE_ON_HOME_SCREEN, true)); |
| } |
| |
| private void loadWorkspaceImpl( |
| List<ShortcutInfo> allDeepShortcuts, |
| String selection, |
| @Nullable LoaderMemoryLogger memoryLogger, |
| @Nullable LauncherRestoreEventLogger restoreEventLogger) { |
| final Context context = mApp.getContext(); |
| final PackageManagerHelper pmHelper = new PackageManagerHelper(context); |
| final boolean isSdCardReady = Utilities.isBootCompleted(); |
| final WidgetInflater widgetInflater = new WidgetInflater(context); |
| |
| ModelDbController dbController = mApp.getModel().getModelDbController(); |
| dbController.tryMigrateDB(restoreEventLogger); |
| Log.d(TAG, "loadWorkspace: loading default favorites"); |
| dbController.loadDefaultFavoritesIfNecessary(); |
| |
| synchronized (mBgDataModel) { |
| mBgDataModel.clear(); |
| mPendingPackages.clear(); |
| |
| final HashMap<PackageUserKey, SessionInfo> installingPkgs = |
| mSessionHelper.getActiveSessions(); |
| if (Utilities.enableSupportForArchiving()) { |
| mInstallingPkgsCached = installingPkgs; |
| } |
| installingPkgs.forEach(mApp.getIconCache()::updateSessionCache); |
| FileLog.d(TAG, "loadWorkspace: Packages with active install sessions: " |
| + installingPkgs.keySet().stream().map(info -> info.mPackageName).toList()); |
| |
| mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs); |
| |
| mShortcutKeyToPinnedShortcuts = new HashMap<>(); |
| final LoaderCursor c = new LoaderCursor( |
| dbController.query(TABLE_NAME, null, selection, null, null), |
| mApp, mUserManagerState, mIsRestoreFromBackup ? restoreEventLogger : null); |
| final Bundle extras = c.getExtras(); |
| mDbName = extras == null ? null : extras.getString(ModelDbController.EXTRA_DB_NAME); |
| try { |
| final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>(); |
| queryPinnedShortcutsForUnlockedUsers(context, unlockedUsers); |
| |
| List<IconRequestInfo<WorkspaceItemInfo>> iconRequestInfos = new ArrayList<>(); |
| |
| WorkspaceItemProcessor itemProcessor = new WorkspaceItemProcessor(c, memoryLogger, |
| mUserManagerState, mLauncherApps, mPendingPackages, |
| mShortcutKeyToPinnedShortcuts, mApp, mBgDataModel, |
| mWidgetProvidersMap, installingPkgs, isSdCardReady, |
| widgetInflater, pmHelper, iconRequestInfos, unlockedUsers, |
| allDeepShortcuts); |
| |
| while (!mStopped && c.moveToNext()) { |
| itemProcessor.processItem(); |
| } |
| tryLoadWorkspaceIconsInBulk(iconRequestInfos); |
| } finally { |
| IOUtils.closeSilently(c); |
| } |
| |
| if (!FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) { |
| mModelDelegate.loadAndBindWorkspaceItems(mUserManagerState, |
| mLauncherBinder.mCallbacksList, mShortcutKeyToPinnedShortcuts); |
| mModelDelegate.loadAndBindAllAppsItems(mUserManagerState, |
| mLauncherBinder.mCallbacksList, mShortcutKeyToPinnedShortcuts); |
| mModelDelegate.loadAndBindOtherItems(mLauncherBinder.mCallbacksList); |
| mModelDelegate.markActive(); |
| } |
| |
| // Break early if we've stopped loading |
| if (mStopped) { |
| mBgDataModel.clear(); |
| return; |
| } |
| |
| // Remove dead items |
| mItemsDeleted = c.commitDeleted(); |
| |
| processFolderItems(); |
| processAppPairItems(); |
| |
| c.commitRestoredItems(); |
| } |
| } |
| |
| /** |
| * After all items have been processed and added to the BgDataModel, this method requests |
| * high-res icons for the items that are part of an app pair |
| */ |
| private void processAppPairItems() { |
| mBgDataModel.workspaceItems.stream() |
| .filter((itemInfo -> itemInfo.itemType == ITEM_TYPE_APP_PAIR)) |
| .forEach(fi -> ((FolderInfo) fi).contents.forEach(item -> |
| mIconCache.getTitleAndIcon(item, false /*useLowResIcon*/))); |
| } |
| |
| /** |
| * Initialized the UserManagerState, and determines which users are unlocked. Additionally, if |
| * the user is unlocked, it queries LauncherAppsService for pinned shortcuts and stores the |
| * result in a class variable to be used in other methods while processing workspace items. |
| * |
| * @param context used to query LauncherAppsService |
| * @param unlockedUsers this param is changed, and the updated value is used outside this method |
| */ |
| @WorkerThread |
| private void queryPinnedShortcutsForUnlockedUsers(Context context, |
| LongSparseArray<Boolean> unlockedUsers) { |
| mUserManagerState.init(mUserCache, mUserManager); |
| |
| for (UserHandle user : mUserCache.getUserProfiles()) { |
| long serialNo = mUserCache.getSerialNumberForUser(user); |
| boolean userUnlocked = mUserManager.isUserUnlocked(user); |
| |
| // We can only query for shortcuts when the user is unlocked. |
| if (userUnlocked) { |
| QueryResult pinnedShortcuts = new ShortcutRequest(context, user) |
| .query(ShortcutRequest.PINNED); |
| if (pinnedShortcuts.wasSuccess()) { |
| for (ShortcutInfo shortcut : pinnedShortcuts) { |
| mShortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut), |
| shortcut); |
| } |
| if (pinnedShortcuts.isEmpty()) { |
| FileLog.d(TAG, "No pinned shortcuts found for user " + user); |
| } |
| } else { |
| // Shortcut manager can fail due to some race condition when the |
| // lock state changes too frequently. For the purpose of the loading |
| // shortcuts, consider the user is still locked. |
| FileLog.d(TAG, "Shortcut request failed for user " |
| + user + ", user may still be locked."); |
| userUnlocked = false; |
| } |
| } |
| unlockedUsers.put(serialNo, userUnlocked); |
| } |
| |
| } |
| |
| /** |
| * After all items have been processed and added to the BgDataModel, this method can correctly |
| * rank items inside folders and load the correct miniature preview icons to be shown when the |
| * folder is collapsed. |
| */ |
| @WorkerThread |
| private void processFolderItems() { |
| // Sort the folder items, update ranks, and make sure all preview items are high res. |
| List<FolderGridOrganizer> verifiers = mApp.getInvariantDeviceProfile().supportedProfiles |
| .stream().map(FolderGridOrganizer::new).toList(); |
| for (FolderInfo folder : mBgDataModel.folders) { |
| Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR); |
| verifiers.forEach(verifier -> verifier.setFolderInfo(folder)); |
| int size = folder.contents.size(); |
| |
| // Update ranks here to ensure there are no gaps caused by removed folder items. |
| // Ranks are the source of truth for folder items, so cellX and cellY can be |
| // ignored for now. Database will be updated once user manually modifies folder. |
| for (int rank = 0; rank < size; ++rank) { |
| WorkspaceItemInfo info = folder.contents.get(rank); |
| // rank is used differently in app pairs, so don't reset |
| if (folder.itemType != ITEM_TYPE_APP_PAIR) { |
| info.rank = rank; |
| } |
| |
| if (info.usingLowResIcon() && info.itemType == Favorites.ITEM_TYPE_APPLICATION |
| && verifiers.stream().anyMatch(it -> it.isItemInPreview(info.rank))) { |
| mIconCache.getTitleAndIcon(info, false); |
| } |
| } |
| } |
| } |
| |
| private void tryLoadWorkspaceIconsInBulk( |
| List<IconRequestInfo<WorkspaceItemInfo>> iconRequestInfos) { |
| Trace.beginSection("LoadWorkspaceIconsInBulk"); |
| try { |
| mIconCache.getTitlesAndIconsInBulk(iconRequestInfos); |
| for (IconRequestInfo<WorkspaceItemInfo> 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) { |
| for (ItemInfo info : mBgDataModel.itemsIdMap) { |
| if (info instanceof WorkspaceItemInfo) { |
| WorkspaceItemInfo si = (WorkspaceItemInfo) info; |
| if (si.isPromise() && si.getTargetComponent() != null) { |
| updateHandler.addPackagesToIgnore( |
| si.user, si.getTargetComponent().getPackageName()); |
| } |
| } else if (info instanceof LauncherAppWidgetInfo) { |
| LauncherAppWidgetInfo lawi = (LauncherAppWidgetInfo) info; |
| if (lawi.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) { |
| updateHandler.addPackagesToIgnore( |
| lawi.user, lawi.providerName.getPackageName()); |
| } |
| } |
| } |
| } |
| } |
| |
| private void sanitizeFolders(boolean itemsDeleted) { |
| if (itemsDeleted) { |
| // Remove any empty folder |
| IntArray deletedFolderIds = mApp.getModel().getModelDbController().deleteEmptyFolders(); |
| synchronized (mBgDataModel) { |
| for (int folderId : deletedFolderIds) { |
| mBgDataModel.workspaceItems.remove(mBgDataModel.folders.get(folderId)); |
| mBgDataModel.folders.remove(folderId); |
| mBgDataModel.itemsIdMap.remove(folderId); |
| } |
| } |
| } |
| } |
| |
| private void sanitizeWidgetsShortcutsAndPackages() { |
| Context context = mApp.getContext(); |
| |
| // Remove any ghost widgets |
| mApp.getModel().getModelDbController().removeGhostWidgets(); |
| |
| // Update pinned state of model shortcuts |
| mBgDataModel.updateShortcutPinnedState(context); |
| |
| if (!Utilities.isBootCompleted() && !mPendingPackages.isEmpty()) { |
| context.registerReceiver( |
| new SdCardAvailableReceiver(mApp, mPendingPackages), |
| new IntentFilter(Intent.ACTION_BOOT_COMPLETED), |
| null, |
| MODEL_EXECUTOR.getHandler()); |
| } |
| } |
| |
| private List<LauncherActivityInfo> loadAllApps() { |
| final List<UserHandle> profiles = mUserCache.getUserProfiles(); |
| List<LauncherActivityInfo> allActivityList = new ArrayList<>(); |
| // Clear the list of apps |
| mBgAllAppsList.clear(); |
| |
| List<IconRequestInfo<AppInfo>> iconRequestInfos = new ArrayList<>(); |
| boolean isWorkProfileQuiet = false; |
| boolean isPrivateProfileQuiet = false; |
| for (UserHandle user : profiles) { |
| // Query for the set of apps |
| final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user); |
| // Fail if we don't have any apps |
| // TODO: Fix this. Only fail for the current user. |
| if (apps == null || apps.isEmpty()) { |
| return allActivityList; |
| } |
| boolean quietMode = mUserManagerState.isUserQuiet(user); |
| |
| if (Flags.enablePrivateSpace()) { |
| if (mUserCache.getUserInfo(user).isWork()) { |
| isWorkProfileQuiet = quietMode; |
| } else if (mUserCache.getUserInfo(user).isPrivate()) { |
| isPrivateProfileQuiet = quietMode; |
| } |
| } |
| // Create the ApplicationInfos |
| for (int i = 0; i < apps.size(); i++) { |
| LauncherActivityInfo app = apps.get(i); |
| AppInfo appInfo = new AppInfo(app, mUserCache.getUserInfo(user), quietMode); |
| if (Utilities.enableSupportForArchiving() && app.getApplicationInfo().isArchived) { |
| // For archived apps, include progress info in case there is a pending |
| // install session post restart of device. |
| String appPackageName = app.getApplicationInfo().packageName; |
| SessionInfo si = mInstallingPkgsCached != null ? mInstallingPkgsCached.get( |
| new PackageUserKey(appPackageName, user)) |
| : mSessionHelper.getActiveSessionInfo(user, |
| appPackageName); |
| if (si != null) { |
| appInfo.runtimeStatusFlags |= FLAG_INSTALL_SESSION_ACTIVE; |
| appInfo.setProgressLevel((int) (si.getProgress() * 100), |
| PackageInstallInfo.STATUS_INSTALLING); |
| } |
| } |
| |
| iconRequestInfos.add(new IconRequestInfo<>( |
| appInfo, app, /* useLowResIcon= */ false)); |
| 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 : |
| mSessionHelper.getAllVerifiedSessions()) { |
| AppInfo promiseAppInfo = mBgAllAppsList.addPromiseApp( |
| mApp.getContext(), |
| PackageInstallInfo.fromInstallingState(info), |
| false); |
| |
| if (promiseAppInfo != null) { |
| iconRequestInfos.add(new IconRequestInfo<>( |
| promiseAppInfo, |
| /* launcherActivityInfo= */ null, |
| promiseAppInfo.usingLowResIcon())); |
| } |
| } |
| } |
| |
| Trace.beginSection("LoadAllAppsIconsInBulk"); |
| try { |
| mIconCache.getTitlesAndIconsInBulk(iconRequestInfos); |
| iconRequestInfos.forEach(iconRequestInfo -> |
| mBgAllAppsList.updateSectionName(iconRequestInfo.itemInfo)); |
| } finally { |
| Trace.endSection(); |
| } |
| |
| if (Flags.enablePrivateSpace()) { |
| mBgAllAppsList.setFlags(FLAG_WORK_PROFILE_QUIET_MODE_ENABLED, isWorkProfileQuiet); |
| mBgAllAppsList.setFlags(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED, isPrivateProfileQuiet); |
| } else { |
| mBgAllAppsList.setFlags(FLAG_QUIET_MODE_ENABLED, |
| mUserManagerState.isAnyProfileQuietModeEnabled()); |
| } |
| mBgAllAppsList.setFlags(FLAG_HAS_SHORTCUT_PERMISSION, |
| hasShortcutsPermission(mApp.getContext())); |
| mBgAllAppsList.setFlags(FLAG_QUIET_MODE_CHANGE_PERMISSION, |
| mApp.getContext().checkSelfPermission("android.permission.MODIFY_QUIET_MODE") |
| == PackageManager.PERMISSION_GRANTED); |
| |
| mBgAllAppsList.getAndResetChangeFlag(); |
| return allActivityList; |
| } |
| |
| private List<ShortcutInfo> loadDeepShortcuts() { |
| List<ShortcutInfo> allShortcuts = new ArrayList<>(); |
| mBgDataModel.deepShortcutMap.clear(); |
| |
| if (mBgAllAppsList.hasShortcutHostPermission()) { |
| for (UserHandle user : mUserCache.getUserProfiles()) { |
| if (mUserManager.isUserUnlocked(user)) { |
| List<ShortcutInfo> shortcuts = new ShortcutRequest(mApp.getContext(), user) |
| .query(ShortcutRequest.ALL); |
| allShortcuts.addAll(shortcuts); |
| mBgDataModel.updateDeepShortcutCounts(null, user, shortcuts); |
| } |
| } |
| } |
| return allShortcuts; |
| } |
| |
| private void loadFolderNames() { |
| FolderNameProvider provider = FolderNameProvider.newInstance(mApp.getContext(), |
| mBgAllAppsList.data, mBgDataModel.folders); |
| |
| synchronized (mBgDataModel) { |
| for (int i = 0; i < mBgDataModel.folders.size(); i++) { |
| FolderNameInfos suggestionInfos = new FolderNameInfos(); |
| FolderInfo info = mBgDataModel.folders.valueAt(i); |
| if (info.suggestedFolderNames == null) { |
| provider.getSuggestedFolderName(mApp.getContext(), info.contents, |
| suggestionInfos); |
| info.suggestedFolderNames = suggestionInfos; |
| } |
| } |
| } |
| } |
| |
| public static boolean isValidProvider(AppWidgetProviderInfo provider) { |
| return (provider != null) && (provider.provider != null) |
| && (provider.provider.getPackageName() != null); |
| } |
| |
| private static void logASplit(String label) { |
| if (DEBUG) { |
| Log.d(TAG, label); |
| } |
| } |
| } |