summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher3/model/LoaderTask.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/launcher3/model/LoaderTask.java')
-rw-r--r--src/com/android/launcher3/model/LoaderTask.java839
1 files changed, 839 insertions, 0 deletions
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
new file mode 100644
index 000000000..bcf516eed
--- /dev/null
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -0,0 +1,839 @@
+/*
+ * 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 android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.LauncherActivityInfo;
+import android.content.pm.PackageInstaller;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Process;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.LongSparseArray;
+import android.util.MutableInt;
+
+import com.android.launcher3.AllAppsList;
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.FolderInfo;
+import com.android.launcher3.IconCache;
+import com.android.launcher3.InstallShortcutReceiver;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.PackageInstallerCompat;
+import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.folder.Folder;
+import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.folder.FolderIconPreviewVerifier;
+import com.android.launcher3.graphics.LauncherIcons;
+import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.provider.ImportDataTask;
+import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.shortcuts.ShortcutInfoCompat;
+import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.LooperIdleLock;
+import com.android.launcher3.util.ManagedProfileHeuristic;
+import com.android.launcher3.util.MultiHashMap;
+import com.android.launcher3.util.PackageManagerHelper;
+
+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.concurrent.CancellationException;
+
+/**
+ * Runnable for the thread that loads the contents of the launcher:
+ * - workspace icons
+ * - widgets
+ * - all apps icons
+ * - deep shortcuts within apps
+ */
+public class LoaderTask implements Runnable {
+ private static final boolean DEBUG_LOADERS = false;
+ private static final String TAG = "LoaderTask";
+
+ private final LauncherAppState mApp;
+ private final AllAppsList mBgAllAppsList;
+ private final BgDataModel mBgDataModel;
+
+ private final LoaderResults mResults;
+
+ private final LauncherAppsCompat mLauncherApps;
+ private final UserManagerCompat mUserManager;
+ private final DeepShortcutManager mShortcutManager;
+ private final PackageInstallerCompat mPackageInstaller;
+ private final AppWidgetManagerCompat mAppWidgetManager;
+ private final IconCache mIconCache;
+
+ private boolean mStopped;
+
+ public LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel,
+ LoaderResults results) {
+ mApp = app;
+ mBgAllAppsList = bgAllAppsList;
+ mBgDataModel = dataModel;
+ mResults = results;
+
+ mLauncherApps = LauncherAppsCompat.getInstance(mApp.getContext());
+ mUserManager = UserManagerCompat.getInstance(mApp.getContext());
+ mShortcutManager = DeepShortcutManager.getInstance(mApp.getContext());
+ mPackageInstaller = PackageInstallerCompat.getInstance(mApp.getContext());
+ mAppWidgetManager = AppWidgetManagerCompat.getInstance(mApp.getContext());
+ mIconCache = mApp.getIconCache();
+ }
+
+ private 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 = new LooperIdleLock(this, Looper.getMainLooper());
+ // 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");
+ }
+ }
+
+ public void run() {
+ synchronized (this) {
+ // Skip fast if we are already stopped.
+ if (mStopped) {
+ return;
+ }
+ }
+
+ try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
+ long now = 0;
+ if (DEBUG_LOADERS) Log.d(TAG, "step 1.1: loading workspace");
+ loadWorkspace();
+
+ verifyNotStopped();
+ if (DEBUG_LOADERS) Log.d(TAG, "step 1.2: bind workspace workspace");
+ mResults.bindWorkspace();
+
+ // Take a break
+ if (DEBUG_LOADERS) {
+ Log.d(TAG, "step 1 completed, wait for idle");
+ now = SystemClock.uptimeMillis();
+ }
+ waitForIdle();
+ if (DEBUG_LOADERS) Log.d(TAG, "Waited " + (SystemClock.uptimeMillis() - now) + "ms");
+ verifyNotStopped();
+
+ // second step
+ if (DEBUG_LOADERS) Log.d(TAG, "step 2.1: loading all apps");
+ loadAllApps();
+
+ if (DEBUG_LOADERS) Log.d(TAG, "step 2.2: Binding all apps");
+ verifyNotStopped();
+ mResults.bindAllApps();
+
+ verifyNotStopped();
+ if (DEBUG_LOADERS) Log.d(TAG, "step 2.3: Update icon cache");
+ updateIconCache();
+
+ // Take a break
+ if (DEBUG_LOADERS) {
+ Log.d(TAG, "step 2 completed, wait for idle");
+ now = SystemClock.uptimeMillis();
+ }
+ waitForIdle();
+ if (DEBUG_LOADERS) Log.d(TAG, "Waited " + (SystemClock.uptimeMillis() - now) + "ms");
+ verifyNotStopped();
+
+ // third step
+ if (DEBUG_LOADERS) Log.d(TAG, "step 3.1: loading deep shortcuts");
+ loadDeepShortcuts();
+
+ verifyNotStopped();
+ if (DEBUG_LOADERS) Log.d(TAG, "step 3.2: bind deep shortcuts");
+ mResults.bindDeepShortcuts();
+
+ // Take a break
+ if (DEBUG_LOADERS) Log.d(TAG, "step 3 completed, wait for idle");
+ waitForIdle();
+ verifyNotStopped();
+
+ // fourth step
+ if (DEBUG_LOADERS) Log.d(TAG, "step 4.1: loading widgets");
+ mBgDataModel.widgetsModel.update(mApp, null);
+
+ verifyNotStopped();
+ if (DEBUG_LOADERS) Log.d(TAG, "step 4.2: Binding widgets");
+ mResults.bindWidgets();
+
+ transaction.commit();
+ } catch (CancellationException e) {
+ // Loader stopped, ignore
+ }
+ }
+
+ public synchronized void stopLocked() {
+ mStopped = true;
+ this.notify();
+ }
+
+ private void loadWorkspace() {
+ if (LauncherAppState.PROFILE_STARTUP) {
+ Trace.beginSection("Loading Workspace");
+ }
+
+ final Context context = mApp.getContext();
+ final ContentResolver contentResolver = context.getContentResolver();
+ final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
+ final boolean isSafeMode = pmHelper.isSafeMode();
+ final boolean isSdCardReady = Utilities.isBootCompleted();
+ final MultiHashMap<UserHandle, String> pendingPackages = new MultiHashMap<>();
+
+ boolean clearDb = false;
+ try {
+ ImportDataTask.performImportIfPossible(context);
+ } catch (Exception e) {
+ // Migration failed. Clear workspace.
+ clearDb = true;
+ }
+
+ if (!clearDb && GridSizeMigrationTask.ENABLED &&
+ !GridSizeMigrationTask.migrateGridIfNeeded(context)) {
+ // Migration failed. Clear workspace.
+ clearDb = true;
+ }
+
+ if (clearDb) {
+ Log.d(TAG, "loadWorkspace: resetting launcher database");
+ LauncherSettings.Settings.call(contentResolver,
+ LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
+ }
+
+ Log.d(TAG, "loadWorkspace: loading default favorites");
+ LauncherSettings.Settings.call(contentResolver,
+ LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES);
+
+ synchronized (mBgDataModel) {
+ mBgDataModel.clear();
+
+ final HashMap<String, Integer> installingPkgs =
+ mPackageInstaller.updateAndGetActiveSessionCache();
+ mBgDataModel.workspaceScreens.addAll(LauncherModel.loadWorkspaceScreensDb(context));
+
+ Map<ShortcutKey, ShortcutInfoCompat> shortcutKeyToPinnedShortcuts = new HashMap<>();
+ final LoaderCursor c = new LoaderCursor(contentResolver.query(
+ LauncherSettings.Favorites.CONTENT_URI, null, null, null, null), mApp);
+
+ HashMap<ComponentKey, AppWidgetProviderInfo> widgetProvidersMap = null;
+
+ 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 LongSparseArray<UserHandle> allUsers = c.allUsers;
+ final LongSparseArray<Boolean> quietMode = new LongSparseArray<>();
+ final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>();
+ for (UserHandle user : mUserManager.getUserProfiles()) {
+ long serialNo = mUserManager.getSerialNumberForUser(user);
+ allUsers.put(serialNo, user);
+ quietMode.put(serialNo, mUserManager.isQuietModeEnabled(user));
+
+ boolean userUnlocked = mUserManager.isUserUnlocked(user);
+
+ // We can only query for shortcuts when the user is unlocked.
+ if (userUnlocked) {
+ List<ShortcutInfoCompat> pinnedShortcuts =
+ mShortcutManager.queryForPinnedShortcuts(null, user);
+ if (mShortcutManager.wasLastCallSuccess()) {
+ for (ShortcutInfoCompat shortcut : pinnedShortcuts) {
+ shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut),
+ shortcut);
+ }
+ } 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.
+ userUnlocked = false;
+ }
+ }
+ unlockedUsers.put(serialNo, userUnlocked);
+ }
+
+ ShortcutInfo info;
+ LauncherAppWidgetInfo appWidgetInfo;
+ Intent intent;
+ String targetPkg;
+
+ FolderIconPreviewVerifier verifier =
+ new FolderIconPreviewVerifier(mApp.getInvariantDeviceProfile());
+ 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 = quietMode.get(c.serialNumber) ?
+ ShortcutInfo.FLAG_DISABLED_QUIET_USER : 0;
+ ComponentName cn = intent.getComponent();
+ targetPkg = cn == null ? intent.getPackage() : cn.getPackageName();
+
+ if (!Process.myUserHandle().equals(c.user)) {
+ if (c.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
+ c.markDeleted("Legacy shortcuts are only allowed for default user");
+ continue;
+ } else if (c.restoreFlag != 0) {
+ // Don't restore items for other profiles.
+ c.markDeleted("Restore from managed profile not supported");
+ continue;
+ }
+ }
+ 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.isPackageEnabledForProfile(targetPkg, c.user);
+
+ if (cn != null && validTarget) {
+ // If the apk is present and the shortcut points to a specific
+ // component.
+
+ // If the component is already present
+ if (mLauncherApps.isActivityEnabledForProfile(cn, c.user)) {
+ // no special handling necessary for this item
+ c.markRestored();
+ } else {
+ if (c.hasRestoreFlag(ShortcutInfo.FLAG_AUTOINSTALL_ICON)) {
+ // We allow auto install apps to have their intent
+ // updated after an install.
+ 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 {
+ // The app is installed but the component is no
+ // longer available.
+ c.markDeleted("Invalid component removed: " + cn);
+ 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);
+
+ if (c.hasRestoreFlag(ShortcutInfo.FLAG_RESTORE_STARTED)) {
+ // Restore has started once.
+ } else if (installingPkgs.containsKey(targetPkg)) {
+ // App restore has started. Update the flag
+ c.restoreFlag |= ShortcutInfo.FLAG_RESTORE_STARTED;
+ c.updater().commit();
+ } else {
+ c.markDeleted("Unrestored app removed: " + targetPkg);
+ continue;
+ }
+ } else if (pmHelper.isAppOnSdcard(targetPkg, c.user)) {
+ // Package is present but not available.
+ disabledState |= ShortcutInfo.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);
+ pendingPackages.addToList(c.user, targetPkg);
+ // 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 (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() &&
+ !verifier.isItemInPreview(c.getInt(rankIndex));
+
+ 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);
+ } else if (c.itemType ==
+ LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+
+ ShortcutKey key = ShortcutKey.fromIntent(intent, c.user);
+ if (unlockedUsers.get(c.serialNumber)) {
+ ShortcutInfoCompat pinnedShortcut =
+ shortcutKeyToPinnedShortcuts.get(key);
+ if (pinnedShortcut == null) {
+ // The shortcut is no longer valid.
+ c.markDeleted("Pinned shortcut not found");
+ continue;
+ }
+ info = new ShortcutInfo(pinnedShortcut, context);
+ info.iconBitmap = LauncherIcons
+ .createShortcutIcon(pinnedShortcut, context);
+ if (pmHelper.isAppSuspended(
+ pinnedShortcut.getPackage(), info.user)) {
+ info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
+ }
+ intent = info.intent;
+ } else {
+ // Create a shortcut info in disabled mode for now.
+ info = c.loadSimpleShortcut();
+ info.isDisabled |= ShortcutInfo.FLAG_DISABLED_LOCKED_USER;
+ }
+ } else { // item type == ITEM_TYPE_SHORTCUT
+ info = c.loadSimpleShortcut();
+
+ // Shortcuts are only available on the primary profile
+ if (!TextUtils.isEmpty(targetPkg)
+ && pmHelper.isAppSuspended(targetPkg, c.user)) {
+ disabledState |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
+ }
+
+ // 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) {
+ c.applyCommonProperties(info);
+
+ info.intent = intent;
+ info.rank = c.getInt(rankIndex);
+ info.spanX = 1;
+ info.spanY = 1;
+ info.isDisabled |= disabledState;
+ if (isSafeMode && !Utilities.isSystemApp(context, intent)) {
+ info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SAFEMODE;
+ }
+
+ if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) {
+ Integer progress = installingPkgs.get(targetPkg);
+ if (progress != null) {
+ info.setInstallProgress(progress);
+ } else {
+ info.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
+ }
+ }
+
+ c.checkAndAddItem(info, mBgDataModel);
+ } else {
+ throw new RuntimeException("Unexpected null ShortcutInfo");
+ }
+ 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);
+ break;
+
+ case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
+ 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 =
+ ComponentName.unflattenFromString(savedProvider);
+
+ final boolean isIdValid = !c.hasRestoreFlag(
+ LauncherAppWidgetInfo.FLAG_ID_NOT_VALID);
+ final boolean wasProviderReady = !c.hasRestoreFlag(
+ LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY);
+
+ if (widgetProvidersMap == null) {
+ widgetProvidersMap = mAppWidgetManager.getAllProvidersMap();
+ }
+ final AppWidgetProviderInfo provider = widgetProvidersMap.get(
+ new ComponentKey(
+ ComponentName.unflattenFromString(savedProvider),
+ c.user));
+
+ 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;
+ 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;
+ } else {
+ status &= ~LauncherAppWidgetInfo
+ .FLAG_PROVIDER_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;
+ Integer installProgress = installingPkgs.get(component.getPackageName());
+
+ 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.user = c.user;
+
+ 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) {
+ String pkg = appWidgetInfo.providerName.getPackageName();
+ appWidgetInfo.pendingItemInfo = new PackageItemInfo(pkg);
+ appWidgetInfo.pendingItemInfo.user = appWidgetInfo.user;
+ mIconCache.getTitleAndIconForApp(
+ appWidgetInfo.pendingItemInfo, false);
+ }
+
+ c.checkAndAddItem(appWidgetInfo, mBgDataModel);
+ }
+ break;
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Desktop items loading interrupted", e);
+ }
+ }
+ } finally {
+ Utilities.closeSilently(c);
+ }
+
+ // Break early if we've stopped loading
+ if (mStopped) {
+ mBgDataModel.clear();
+ return;
+ }
+
+ // Remove dead items
+ if (c.commitDeleted()) {
+ // Remove any empty folder
+ ArrayList<Long> deletedFolderIds = (ArrayList<Long>) LauncherSettings.Settings
+ .call(contentResolver,
+ LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS)
+ .getSerializable(LauncherSettings.Settings.EXTRA_VALUE);
+ for (long folderId : deletedFolderIds) {
+ mBgDataModel.workspaceItems.remove(mBgDataModel.folders.get(folderId));
+ mBgDataModel.folders.remove(folderId);
+ mBgDataModel.itemsIdMap.remove(folderId);
+ }
+
+ // Remove any ghost widgets
+ LauncherSettings.Settings.call(contentResolver,
+ LauncherSettings.Settings.METHOD_REMOVE_GHOST_WIDGETS);
+ }
+
+ // Unpin shortcuts that don't exist on the workspace.
+ HashSet<ShortcutKey> pendingShortcuts =
+ InstallShortcutReceiver.getPendingShortcuts(context);
+ for (ShortcutKey key : shortcutKeyToPinnedShortcuts.keySet()) {
+ MutableInt numTimesPinned = mBgDataModel.pinnedShortcutCounts.get(key);
+ if ((numTimesPinned == null || numTimesPinned.value == 0)
+ && !pendingShortcuts.contains(key)) {
+ // Shortcut is pinned but doesn't exist on the workspace; unpin it.
+ mShortcutManager.unpinShortcut(key);
+ }
+ }
+
+ FolderIconPreviewVerifier verifier =
+ new FolderIconPreviewVerifier(mApp.getInvariantDeviceProfile());
+ // Sort the folder items and make sure all items in the preview are high resolution.
+ for (FolderInfo folder : mBgDataModel.folders) {
+ Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
+ verifier.setFolderInfo(folder);
+
+ int numItemsInPreview = 0;
+ for (ShortcutInfo info : folder.contents) {
+ if (info.usingLowResIcon
+ && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
+ && verifier.isItemInPreview(info.rank)) {
+ mIconCache.getTitleAndIcon(info, false);
+ numItemsInPreview++;
+ }
+
+ if (numItemsInPreview >= FolderIcon.NUM_ITEMS_IN_PREVIEW) {
+ break;
+ }
+ }
+ }
+
+ c.commitRestoredItems();
+ if (!isSdCardReady && !pendingPackages.isEmpty()) {
+ context.registerReceiver(
+ new SdCardAvailableReceiver(mApp, pendingPackages),
+ new IntentFilter(Intent.ACTION_BOOT_COMPLETED),
+ null,
+ new Handler(LauncherModel.getWorkerLooper()));
+ }
+
+ // Remove any empty screens
+ ArrayList<Long> unusedScreens = new ArrayList<>(mBgDataModel.workspaceScreens);
+ for (ItemInfo item: mBgDataModel.itemsIdMap) {
+ long screenId = item.screenId;
+ if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
+ unusedScreens.contains(screenId)) {
+ unusedScreens.remove(screenId);
+ }
+ }
+
+ // If there are any empty screens remove them, and update.
+ if (unusedScreens.size() != 0) {
+ mBgDataModel.workspaceScreens.removeAll(unusedScreens);
+ LauncherModel.updateWorkspaceScreenOrder(context, mBgDataModel.workspaceScreens);
+ }
+ }
+ if (LauncherAppState.PROFILE_STARTUP) {
+ Trace.endSection();
+ }
+ }
+
+ private void updateIconCache() {
+ // Ignore packages which have a promise icon.
+ HashSet<String> packagesToIgnore = new HashSet<>();
+ synchronized (mBgDataModel) {
+ for (ItemInfo info : mBgDataModel.itemsIdMap) {
+ if (info instanceof ShortcutInfo) {
+ ShortcutInfo si = (ShortcutInfo) info;
+ if (si.isPromise() && si.getTargetComponent() != null) {
+ packagesToIgnore.add(si.getTargetComponent().getPackageName());
+ }
+ } else if (info instanceof LauncherAppWidgetInfo) {
+ LauncherAppWidgetInfo lawi = (LauncherAppWidgetInfo) info;
+ if (lawi.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) {
+ packagesToIgnore.add(lawi.providerName.getPackageName());
+ }
+ }
+ }
+ }
+ mIconCache.updateDbIcons(packagesToIgnore);
+ }
+
+ private void loadAllApps() {
+ final long loadTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
+
+ final List<UserHandle> profiles = mUserManager.getUserProfiles();
+
+ // Clear the list of apps
+ mBgAllAppsList.clear();
+ for (UserHandle user : profiles) {
+ // Query for the set of apps
+ final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
+ final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user);
+ if (DEBUG_LOADERS) {
+ Log.d(TAG, "getActivityList took "
+ + (SystemClock.uptimeMillis()-qiaTime) + "ms for user " + user);
+ Log.d(TAG, "getActivityList got " + apps.size() + " apps for user " + user);
+ }
+ // Fail if we don't have any apps
+ // TODO: Fix this. Only fail for the current user.
+ if (apps == null || apps.isEmpty()) {
+ return;
+ }
+ boolean quietMode = mUserManager.isQuietModeEnabled(user);
+ // Create the ApplicationInfos
+ for (int i = 0; i < apps.size(); i++) {
+ LauncherActivityInfo app = apps.get(i);
+ // This builds the icon bitmaps.
+ mBgAllAppsList.add(new AppInfo(app, user, quietMode), app);
+ }
+
+ ManagedProfileHeuristic.onAllAppsLoaded(mApp.getContext(), apps, user);
+ }
+
+ if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) {
+ // get all active sessions and add them to the all apps list
+ for (PackageInstaller.SessionInfo info :
+ mPackageInstaller.getAllVerifiedSessions()) {
+ mBgAllAppsList.addPromiseApp(mApp.getContext(),
+ PackageInstallerCompat.PackageInstallInfo.fromInstallingState(info));
+ }
+ }
+
+ mBgAllAppsList.added = new ArrayList<>();
+ if (DEBUG_LOADERS) {
+ Log.d(TAG, "All apps loaded in in "
+ + (SystemClock.uptimeMillis() - loadTime) + "ms");
+ }
+ }
+
+ private void loadDeepShortcuts() {
+ mBgDataModel.deepShortcutMap.clear();
+ mBgDataModel.hasShortcutHostPermission = mShortcutManager.hasHostPermission();
+ if (mBgDataModel.hasShortcutHostPermission) {
+ for (UserHandle user : mUserManager.getUserProfiles()) {
+ if (mUserManager.isUserUnlocked(user)) {
+ List<ShortcutInfoCompat> shortcuts =
+ mShortcutManager.queryForAllShortcuts(user);
+ mBgDataModel.updateDeepShortcutMap(null, user, shortcuts);
+ }
+ }
+ }
+ }
+
+ public static boolean isValidProvider(AppWidgetProviderInfo provider) {
+ return (provider != null) && (provider.provider != null)
+ && (provider.provider.getPackageName() != null);
+ }
+}