1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.launcher3.model; 18 19 import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER; 20 import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE; 21 import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED; 22 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW; 23 import static com.android.launcher3.model.LoaderResults.filterCurrentWorkspaceItems; 24 25 import android.appwidget.AppWidgetProviderInfo; 26 import android.content.ComponentName; 27 import android.content.ContentResolver; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.IntentFilter; 31 import android.content.pm.LauncherActivityInfo; 32 import android.content.pm.PackageInstaller; 33 import android.content.pm.PackageInstaller.SessionInfo; 34 import android.graphics.Bitmap; 35 import android.os.Handler; 36 import android.os.Process; 37 import android.os.UserHandle; 38 import android.text.TextUtils; 39 import android.util.Log; 40 import android.util.LongSparseArray; 41 import android.util.MutableInt; 42 43 import com.android.launcher3.AllAppsList; 44 import com.android.launcher3.AppInfo; 45 import com.android.launcher3.FolderInfo; 46 import com.android.launcher3.IconCache; 47 import com.android.launcher3.InstallShortcutReceiver; 48 import com.android.launcher3.ItemInfo; 49 import com.android.launcher3.LauncherAppState; 50 import com.android.launcher3.LauncherAppWidgetInfo; 51 import com.android.launcher3.LauncherModel; 52 import com.android.launcher3.LauncherSettings; 53 import com.android.launcher3.ShortcutInfo; 54 import com.android.launcher3.Utilities; 55 import com.android.launcher3.compat.AppWidgetManagerCompat; 56 import com.android.launcher3.compat.LauncherAppsCompat; 57 import com.android.launcher3.compat.PackageInstallerCompat; 58 import com.android.launcher3.compat.UserManagerCompat; 59 import com.android.launcher3.config.FeatureFlags; 60 import com.android.launcher3.folder.Folder; 61 import com.android.launcher3.folder.FolderIconPreviewVerifier; 62 import com.android.launcher3.graphics.LauncherIcons; 63 import com.android.launcher3.logging.FileLog; 64 import com.android.launcher3.provider.ImportDataTask; 65 import com.android.launcher3.shortcuts.DeepShortcutManager; 66 import com.android.launcher3.shortcuts.ShortcutInfoCompat; 67 import com.android.launcher3.shortcuts.ShortcutKey; 68 import com.android.launcher3.util.ComponentKey; 69 import com.android.launcher3.util.LooperIdleLock; 70 import com.android.launcher3.util.MultiHashMap; 71 import com.android.launcher3.util.PackageManagerHelper; 72 import com.android.launcher3.util.Provider; 73 import com.android.launcher3.util.TraceHelper; 74 75 import java.util.ArrayList; 76 import java.util.Collections; 77 import java.util.HashMap; 78 import java.util.HashSet; 79 import java.util.List; 80 import java.util.Map; 81 import java.util.concurrent.CancellationException; 82 83 /** 84 * Runnable for the thread that loads the contents of the launcher: 85 * - workspace icons 86 * - widgets 87 * - all apps icons 88 * - deep shortcuts within apps 89 */ 90 public class LoaderTask implements Runnable { 91 private static final String TAG = "LoaderTask"; 92 93 private final LauncherAppState mApp; 94 private final AllAppsList mBgAllAppsList; 95 private final BgDataModel mBgDataModel; 96 97 private FirstScreenBroadcast mFirstScreenBroadcast; 98 99 private final LoaderResults mResults; 100 101 private final LauncherAppsCompat mLauncherApps; 102 private final UserManagerCompat mUserManager; 103 private final DeepShortcutManager mShortcutManager; 104 private final PackageInstallerCompat mPackageInstaller; 105 private final AppWidgetManagerCompat mAppWidgetManager; 106 private final IconCache mIconCache; 107 108 private boolean mStopped; 109 LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel, LoaderResults results)110 public LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel, 111 LoaderResults results) { 112 mApp = app; 113 mBgAllAppsList = bgAllAppsList; 114 mBgDataModel = dataModel; 115 mResults = results; 116 117 mLauncherApps = LauncherAppsCompat.getInstance(mApp.getContext()); 118 mUserManager = UserManagerCompat.getInstance(mApp.getContext()); 119 mShortcutManager = DeepShortcutManager.getInstance(mApp.getContext()); 120 mPackageInstaller = PackageInstallerCompat.getInstance(mApp.getContext()); 121 mAppWidgetManager = AppWidgetManagerCompat.getInstance(mApp.getContext()); 122 mIconCache = mApp.getIconCache(); 123 } 124 waitForIdle()125 protected synchronized void waitForIdle() { 126 // Wait until the either we're stopped or the other threads are done. 127 // This way we don't start loading all apps until the workspace has settled 128 // down. 129 LooperIdleLock idleLock = mResults.newIdleLock(this); 130 // Just in case mFlushingWorkerThread changes but we aren't woken up, 131 // wait no longer than 1sec at a time 132 while (!mStopped && idleLock.awaitLocked(1000)); 133 } 134 verifyNotStopped()135 private synchronized void verifyNotStopped() throws CancellationException { 136 if (mStopped) { 137 throw new CancellationException("Loader stopped"); 138 } 139 } 140 sendFirstScreenActiveInstallsBroadcast()141 private void sendFirstScreenActiveInstallsBroadcast() { 142 ArrayList<ItemInfo> firstScreenItems = new ArrayList<>(); 143 144 ArrayList<ItemInfo> allItems = new ArrayList<>(); 145 synchronized (mBgDataModel) { 146 allItems.addAll(mBgDataModel.workspaceItems); 147 allItems.addAll(mBgDataModel.appWidgets); 148 } 149 long firstScreen = mBgDataModel.workspaceScreens.isEmpty() 150 ? -1 // In this case, we can still look at the items in the hotseat. 151 : mBgDataModel.workspaceScreens.get(0); 152 filterCurrentWorkspaceItems(firstScreen, allItems, firstScreenItems, 153 new ArrayList<>() /* otherScreenItems are ignored */); 154 mFirstScreenBroadcast.sendBroadcasts(mApp.getContext(), firstScreenItems); 155 } 156 run()157 public void run() { 158 synchronized (this) { 159 // Skip fast if we are already stopped. 160 if (mStopped) { 161 return; 162 } 163 } 164 165 TraceHelper.beginSection(TAG); 166 try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) { 167 TraceHelper.partitionSection(TAG, "step 1.1: loading workspace"); 168 loadWorkspace(); 169 170 verifyNotStopped(); 171 TraceHelper.partitionSection(TAG, "step 1.2: bind workspace workspace"); 172 mResults.bindWorkspace(); 173 174 // Notify the installer packages of packages with active installs on the first screen. 175 TraceHelper.partitionSection(TAG, "step 1.3: send first screen broadcast"); 176 sendFirstScreenActiveInstallsBroadcast(); 177 178 // Take a break 179 TraceHelper.partitionSection(TAG, "step 1 completed, wait for idle"); 180 waitForIdle(); 181 verifyNotStopped(); 182 183 // second step 184 TraceHelper.partitionSection(TAG, "step 2.1: loading all apps"); 185 loadAllApps(); 186 187 TraceHelper.partitionSection(TAG, "step 2.2: Binding all apps"); 188 verifyNotStopped(); 189 mResults.bindAllApps(); 190 191 verifyNotStopped(); 192 TraceHelper.partitionSection(TAG, "step 2.3: Update icon cache"); 193 updateIconCache(); 194 195 // Take a break 196 TraceHelper.partitionSection(TAG, "step 2 completed, wait for idle"); 197 waitForIdle(); 198 verifyNotStopped(); 199 200 // third step 201 TraceHelper.partitionSection(TAG, "step 3.1: loading deep shortcuts"); 202 loadDeepShortcuts(); 203 204 verifyNotStopped(); 205 TraceHelper.partitionSection(TAG, "step 3.2: bind deep shortcuts"); 206 mResults.bindDeepShortcuts(); 207 208 // Take a break 209 TraceHelper.partitionSection(TAG, "step 3 completed, wait for idle"); 210 waitForIdle(); 211 verifyNotStopped(); 212 213 // fourth step 214 TraceHelper.partitionSection(TAG, "step 4.1: loading widgets"); 215 mBgDataModel.widgetsModel.update(mApp, null); 216 217 verifyNotStopped(); 218 TraceHelper.partitionSection(TAG, "step 4.2: Binding widgets"); 219 mResults.bindWidgets(); 220 221 transaction.commit(); 222 } catch (CancellationException e) { 223 // Loader stopped, ignore 224 TraceHelper.partitionSection(TAG, "Cancelled"); 225 } 226 TraceHelper.endSection(TAG); 227 } 228 stopLocked()229 public synchronized void stopLocked() { 230 mStopped = true; 231 this.notify(); 232 } 233 loadWorkspace()234 private void loadWorkspace() { 235 final Context context = mApp.getContext(); 236 final ContentResolver contentResolver = context.getContentResolver(); 237 final PackageManagerHelper pmHelper = new PackageManagerHelper(context); 238 final boolean isSafeMode = pmHelper.isSafeMode(); 239 final boolean isSdCardReady = Utilities.isBootCompleted(); 240 final MultiHashMap<UserHandle, String> pendingPackages = new MultiHashMap<>(); 241 242 boolean clearDb = false; 243 try { 244 ImportDataTask.performImportIfPossible(context); 245 } catch (Exception e) { 246 // Migration failed. Clear workspace. 247 clearDb = true; 248 } 249 250 if (!clearDb && GridSizeMigrationTask.ENABLED && 251 !GridSizeMigrationTask.migrateGridIfNeeded(context)) { 252 // Migration failed. Clear workspace. 253 clearDb = true; 254 } 255 256 if (clearDb) { 257 Log.d(TAG, "loadWorkspace: resetting launcher database"); 258 LauncherSettings.Settings.call(contentResolver, 259 LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB); 260 } 261 262 Log.d(TAG, "loadWorkspace: loading default favorites"); 263 LauncherSettings.Settings.call(contentResolver, 264 LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES); 265 266 synchronized (mBgDataModel) { 267 mBgDataModel.clear(); 268 269 final HashMap<String, SessionInfo> installingPkgs = 270 mPackageInstaller.updateAndGetActiveSessionCache(); 271 mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs); 272 mBgDataModel.workspaceScreens.addAll(LauncherModel.loadWorkspaceScreensDb(context)); 273 274 Map<ShortcutKey, ShortcutInfoCompat> shortcutKeyToPinnedShortcuts = new HashMap<>(); 275 final LoaderCursor c = new LoaderCursor(contentResolver.query( 276 LauncherSettings.Favorites.CONTENT_URI, null, null, null, null), mApp); 277 278 HashMap<ComponentKey, AppWidgetProviderInfo> widgetProvidersMap = null; 279 280 try { 281 final int appWidgetIdIndex = c.getColumnIndexOrThrow( 282 LauncherSettings.Favorites.APPWIDGET_ID); 283 final int appWidgetProviderIndex = c.getColumnIndexOrThrow( 284 LauncherSettings.Favorites.APPWIDGET_PROVIDER); 285 final int spanXIndex = c.getColumnIndexOrThrow 286 (LauncherSettings.Favorites.SPANX); 287 final int spanYIndex = c.getColumnIndexOrThrow( 288 LauncherSettings.Favorites.SPANY); 289 final int rankIndex = c.getColumnIndexOrThrow( 290 LauncherSettings.Favorites.RANK); 291 final int optionsIndex = c.getColumnIndexOrThrow( 292 LauncherSettings.Favorites.OPTIONS); 293 294 final LongSparseArray<UserHandle> allUsers = c.allUsers; 295 final LongSparseArray<Boolean> quietMode = new LongSparseArray<>(); 296 final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>(); 297 for (UserHandle user : mUserManager.getUserProfiles()) { 298 long serialNo = mUserManager.getSerialNumberForUser(user); 299 allUsers.put(serialNo, user); 300 quietMode.put(serialNo, mUserManager.isQuietModeEnabled(user)); 301 302 boolean userUnlocked = mUserManager.isUserUnlocked(user); 303 304 // We can only query for shortcuts when the user is unlocked. 305 if (userUnlocked) { 306 List<ShortcutInfoCompat> pinnedShortcuts = 307 mShortcutManager.queryForPinnedShortcuts(null, user); 308 if (mShortcutManager.wasLastCallSuccess()) { 309 for (ShortcutInfoCompat shortcut : pinnedShortcuts) { 310 shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut), 311 shortcut); 312 } 313 } else { 314 // Shortcut manager can fail due to some race condition when the 315 // lock state changes too frequently. For the purpose of the loading 316 // shortcuts, consider the user is still locked. 317 userUnlocked = false; 318 } 319 } 320 unlockedUsers.put(serialNo, userUnlocked); 321 } 322 323 ShortcutInfo info; 324 LauncherAppWidgetInfo appWidgetInfo; 325 Intent intent; 326 String targetPkg; 327 328 FolderIconPreviewVerifier verifier = 329 new FolderIconPreviewVerifier(mApp.getInvariantDeviceProfile()); 330 while (!mStopped && c.moveToNext()) { 331 try { 332 if (c.user == null) { 333 // User has been deleted, remove the item. 334 c.markDeleted("User has been deleted"); 335 continue; 336 } 337 338 boolean allowMissingTarget = false; 339 switch (c.itemType) { 340 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 341 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: 342 case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: 343 intent = c.parseIntent(); 344 if (intent == null) { 345 c.markDeleted("Invalid or null intent"); 346 continue; 347 } 348 349 int disabledState = quietMode.get(c.serialNumber) ? 350 ShortcutInfo.FLAG_DISABLED_QUIET_USER : 0; 351 ComponentName cn = intent.getComponent(); 352 targetPkg = cn == null ? intent.getPackage() : cn.getPackageName(); 353 354 if (!Process.myUserHandle().equals(c.user)) { 355 if (c.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) { 356 c.markDeleted("Legacy shortcuts are only allowed for default user"); 357 continue; 358 } else if (c.restoreFlag != 0) { 359 // Don't restore items for other profiles. 360 c.markDeleted("Restore from managed profile not supported"); 361 continue; 362 } 363 } 364 if (TextUtils.isEmpty(targetPkg) && 365 c.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) { 366 c.markDeleted("Only legacy shortcuts can have null package"); 367 continue; 368 } 369 370 // If there is no target package, its an implicit intent 371 // (legacy shortcut) which is always valid 372 boolean validTarget = TextUtils.isEmpty(targetPkg) || 373 mLauncherApps.isPackageEnabledForProfile(targetPkg, c.user); 374 375 if (cn != null && validTarget) { 376 // If the apk is present and the shortcut points to a specific 377 // component. 378 379 // If the component is already present 380 if (mLauncherApps.isActivityEnabledForProfile(cn, c.user)) { 381 // no special handling necessary for this item 382 c.markRestored(); 383 } else { 384 if (c.hasRestoreFlag(ShortcutInfo.FLAG_AUTOINSTALL_ICON)) { 385 // We allow auto install apps to have their intent 386 // updated after an install. 387 intent = pmHelper.getAppLaunchIntent(targetPkg, c.user); 388 if (intent != null) { 389 c.restoreFlag = 0; 390 c.updater().put( 391 LauncherSettings.Favorites.INTENT, 392 intent.toUri(0)).commit(); 393 cn = intent.getComponent(); 394 } else { 395 c.markDeleted("Unable to find a launch target"); 396 continue; 397 } 398 } else { 399 // The app is installed but the component is no 400 // longer available. 401 c.markDeleted("Invalid component removed: " + cn); 402 continue; 403 } 404 } 405 } 406 // else if cn == null => can't infer much, leave it 407 // else if !validPkg => could be restored icon or missing sd-card 408 409 if (!TextUtils.isEmpty(targetPkg) && !validTarget) { 410 // Points to a valid app (superset of cn != null) but the apk 411 // is not available. 412 413 if (c.restoreFlag != 0) { 414 // Package is not yet available but might be 415 // installed later. 416 FileLog.d(TAG, "package not yet restored: " + targetPkg); 417 418 if (c.hasRestoreFlag(ShortcutInfo.FLAG_RESTORE_STARTED)) { 419 // Restore has started once. 420 } else if (installingPkgs.containsKey(targetPkg)) { 421 // App restore has started. Update the flag 422 c.restoreFlag |= ShortcutInfo.FLAG_RESTORE_STARTED; 423 c.updater().commit(); 424 } else { 425 c.markDeleted("Unrestored app removed: " + targetPkg); 426 continue; 427 } 428 } else if (pmHelper.isAppOnSdcard(targetPkg, c.user)) { 429 // Package is present but not available. 430 disabledState |= ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE; 431 // Add the icon on the workspace anyway. 432 allowMissingTarget = true; 433 } else if (!isSdCardReady) { 434 // SdCard is not ready yet. Package might get available, 435 // once it is ready. 436 Log.d(TAG, "Missing pkg, will check later: " + targetPkg); 437 pendingPackages.addToList(c.user, targetPkg); 438 // Add the icon on the workspace anyway. 439 allowMissingTarget = true; 440 } else { 441 // Do not wait for external media load anymore. 442 c.markDeleted("Invalid package removed: " + targetPkg); 443 continue; 444 } 445 } 446 447 if ((c.restoreFlag & ShortcutInfo.FLAG_SUPPORTS_WEB_UI) != 0) { 448 validTarget = false; 449 } 450 451 if (validTarget) { 452 // The shortcut points to a valid target (either no target 453 // or something which is ready to be used) 454 c.markRestored(); 455 } 456 457 boolean useLowResIcon = !c.isOnWorkspaceOrHotseat() && 458 !verifier.isItemInPreview(c.getInt(rankIndex)); 459 460 if (c.restoreFlag != 0) { 461 // Already verified above that user is same as default user 462 info = c.getRestoredItemInfo(intent); 463 } else if (c.itemType == 464 LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { 465 info = c.getAppShortcutInfo( 466 intent, allowMissingTarget, useLowResIcon); 467 } else if (c.itemType == 468 LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { 469 470 ShortcutKey key = ShortcutKey.fromIntent(intent, c.user); 471 if (unlockedUsers.get(c.serialNumber)) { 472 ShortcutInfoCompat pinnedShortcut = 473 shortcutKeyToPinnedShortcuts.get(key); 474 if (pinnedShortcut == null) { 475 // The shortcut is no longer valid. 476 c.markDeleted("Pinned shortcut not found"); 477 continue; 478 } 479 info = new ShortcutInfo(pinnedShortcut, context); 480 final ShortcutInfo finalInfo = info; 481 Provider<Bitmap> fallbackIconProvider = new Provider<Bitmap>() { 482 @Override 483 public Bitmap get() { 484 // If the pinned deep shortcut is no longer published, 485 // use the last saved icon instead of the default. 486 return c.loadIcon(finalInfo) 487 ? finalInfo.iconBitmap : null; 488 } 489 }; 490 LauncherIcons li = LauncherIcons.obtain(context); 491 li.createShortcutIcon(pinnedShortcut, 492 true /* badged */, fallbackIconProvider).applyTo(info); 493 li.recycle(); 494 if (pmHelper.isAppSuspended( 495 pinnedShortcut.getPackage(), info.user)) { 496 info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED; 497 } 498 intent = info.intent; 499 } else { 500 // Create a shortcut info in disabled mode for now. 501 info = c.loadSimpleShortcut(); 502 info.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER; 503 } 504 } else { // item type == ITEM_TYPE_SHORTCUT 505 info = c.loadSimpleShortcut(); 506 507 // Shortcuts are only available on the primary profile 508 if (!TextUtils.isEmpty(targetPkg) 509 && pmHelper.isAppSuspended(targetPkg, c.user)) { 510 disabledState |= FLAG_DISABLED_SUSPENDED; 511 } 512 513 // App shortcuts that used to be automatically added to Launcher 514 // didn't always have the correct intent flags set, so do that 515 // here 516 if (intent.getAction() != null && 517 intent.getCategories() != null && 518 intent.getAction().equals(Intent.ACTION_MAIN) && 519 intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) { 520 intent.addFlags( 521 Intent.FLAG_ACTIVITY_NEW_TASK | 522 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 523 } 524 } 525 526 if (info != null) { 527 c.applyCommonProperties(info); 528 529 info.intent = intent; 530 info.rank = c.getInt(rankIndex); 531 info.spanX = 1; 532 info.spanY = 1; 533 info.runtimeStatusFlags |= disabledState; 534 if (isSafeMode && !Utilities.isSystemApp(context, intent)) { 535 info.runtimeStatusFlags |= FLAG_DISABLED_SAFEMODE; 536 } 537 538 if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) { 539 SessionInfo si = installingPkgs.get(targetPkg); 540 if (si == null) { 541 info.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE; 542 } else { 543 info.setInstallProgress((int) (si.getProgress() * 100)); 544 } 545 } 546 547 c.checkAndAddItem(info, mBgDataModel); 548 } else { 549 throw new RuntimeException("Unexpected null ShortcutInfo"); 550 } 551 break; 552 553 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: 554 FolderInfo folderInfo = mBgDataModel.findOrMakeFolder(c.id); 555 c.applyCommonProperties(folderInfo); 556 557 // Do not trim the folder label, as is was set by the user. 558 folderInfo.title = c.getString(c.titleIndex); 559 folderInfo.spanX = 1; 560 folderInfo.spanY = 1; 561 folderInfo.options = c.getInt(optionsIndex); 562 563 // no special handling required for restored folders 564 c.markRestored(); 565 566 c.checkAndAddItem(folderInfo, mBgDataModel); 567 break; 568 569 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: 570 if (FeatureFlags.GO_DISABLE_WIDGETS) { 571 c.markDeleted("Only legacy shortcuts can have null package"); 572 continue; 573 } 574 // Follow through 575 case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: 576 // Read all Launcher-specific widget details 577 boolean customWidget = c.itemType == 578 LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET; 579 580 int appWidgetId = c.getInt(appWidgetIdIndex); 581 String savedProvider = c.getString(appWidgetProviderIndex); 582 583 final ComponentName component = 584 ComponentName.unflattenFromString(savedProvider); 585 586 final boolean isIdValid = !c.hasRestoreFlag( 587 LauncherAppWidgetInfo.FLAG_ID_NOT_VALID); 588 final boolean wasProviderReady = !c.hasRestoreFlag( 589 LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY); 590 591 if (widgetProvidersMap == null) { 592 widgetProvidersMap = mAppWidgetManager.getAllProvidersMap(); 593 } 594 final AppWidgetProviderInfo provider = widgetProvidersMap.get( 595 new ComponentKey( 596 ComponentName.unflattenFromString(savedProvider), 597 c.user)); 598 599 final boolean isProviderReady = isValidProvider(provider); 600 if (!isSafeMode && !customWidget && 601 wasProviderReady && !isProviderReady) { 602 c.markDeleted( 603 "Deleting widget that isn't installed anymore: " 604 + provider); 605 } else { 606 if (isProviderReady) { 607 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, 608 provider.provider); 609 610 // The provider is available. So the widget is either 611 // available or not available. We do not need to track 612 // any future restore updates. 613 int status = c.restoreFlag & 614 ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED & 615 ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY; 616 if (!wasProviderReady) { 617 // If provider was not previously ready, update the 618 // status and UI flag. 619 620 // Id would be valid only if the widget restore broadcast was received. 621 if (isIdValid) { 622 status |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY; 623 } 624 } 625 appWidgetInfo.restoreStatus = status; 626 } else { 627 Log.v(TAG, "Widget restore pending id=" + c.id 628 + " appWidgetId=" + appWidgetId 629 + " status =" + c.restoreFlag); 630 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, 631 component); 632 appWidgetInfo.restoreStatus = c.restoreFlag; 633 SessionInfo si = 634 installingPkgs.get(component.getPackageName()); 635 Integer installProgress = si == null 636 ? null 637 : (int) (si.getProgress() * 100); 638 639 if (c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_RESTORE_STARTED)) { 640 // Restore has started once. 641 } else if (installProgress != null) { 642 // App restore has started. Update the flag 643 appWidgetInfo.restoreStatus |= 644 LauncherAppWidgetInfo.FLAG_RESTORE_STARTED; 645 } else if (!isSafeMode) { 646 c.markDeleted("Unrestored widget removed: " + component); 647 continue; 648 } 649 650 appWidgetInfo.installProgress = 651 installProgress == null ? 0 : installProgress; 652 } 653 if (appWidgetInfo.hasRestoreFlag( 654 LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG)) { 655 appWidgetInfo.bindOptions = c.parseIntent(); 656 } 657 658 c.applyCommonProperties(appWidgetInfo); 659 appWidgetInfo.spanX = c.getInt(spanXIndex); 660 appWidgetInfo.spanY = c.getInt(spanYIndex); 661 appWidgetInfo.user = c.user; 662 663 if (!c.isOnWorkspaceOrHotseat()) { 664 c.markDeleted("Widget found where container != " + 665 "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!"); 666 continue; 667 } 668 669 if (!customWidget) { 670 String providerName = 671 appWidgetInfo.providerName.flattenToString(); 672 if (!providerName.equals(savedProvider) || 673 (appWidgetInfo.restoreStatus != c.restoreFlag)) { 674 c.updater() 675 .put(LauncherSettings.Favorites.APPWIDGET_PROVIDER, 676 providerName) 677 .put(LauncherSettings.Favorites.RESTORED, 678 appWidgetInfo.restoreStatus) 679 .commit(); 680 } 681 } 682 683 if (appWidgetInfo.restoreStatus != 684 LauncherAppWidgetInfo.RESTORE_COMPLETED) { 685 String pkg = appWidgetInfo.providerName.getPackageName(); 686 appWidgetInfo.pendingItemInfo = new PackageItemInfo(pkg); 687 appWidgetInfo.pendingItemInfo.user = appWidgetInfo.user; 688 mIconCache.getTitleAndIconForApp( 689 appWidgetInfo.pendingItemInfo, false); 690 } 691 692 c.checkAndAddItem(appWidgetInfo, mBgDataModel); 693 } 694 break; 695 } 696 } catch (Exception e) { 697 Log.e(TAG, "Desktop items loading interrupted", e); 698 } 699 } 700 } finally { 701 Utilities.closeSilently(c); 702 } 703 704 // Break early if we've stopped loading 705 if (mStopped) { 706 mBgDataModel.clear(); 707 return; 708 } 709 710 // Remove dead items 711 if (c.commitDeleted()) { 712 // Remove any empty folder 713 ArrayList<Long> deletedFolderIds = (ArrayList<Long>) LauncherSettings.Settings 714 .call(contentResolver, 715 LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS) 716 .getSerializable(LauncherSettings.Settings.EXTRA_VALUE); 717 for (long folderId : deletedFolderIds) { 718 mBgDataModel.workspaceItems.remove(mBgDataModel.folders.get(folderId)); 719 mBgDataModel.folders.remove(folderId); 720 mBgDataModel.itemsIdMap.remove(folderId); 721 } 722 723 // Remove any ghost widgets 724 LauncherSettings.Settings.call(contentResolver, 725 LauncherSettings.Settings.METHOD_REMOVE_GHOST_WIDGETS); 726 } 727 728 // Unpin shortcuts that don't exist on the workspace. 729 HashSet<ShortcutKey> pendingShortcuts = 730 InstallShortcutReceiver.getPendingShortcuts(context); 731 for (ShortcutKey key : shortcutKeyToPinnedShortcuts.keySet()) { 732 MutableInt numTimesPinned = mBgDataModel.pinnedShortcutCounts.get(key); 733 if ((numTimesPinned == null || numTimesPinned.value == 0) 734 && !pendingShortcuts.contains(key)) { 735 // Shortcut is pinned but doesn't exist on the workspace; unpin it. 736 mShortcutManager.unpinShortcut(key); 737 } 738 } 739 740 FolderIconPreviewVerifier verifier = 741 new FolderIconPreviewVerifier(mApp.getInvariantDeviceProfile()); 742 // Sort the folder items and make sure all items in the preview are high resolution. 743 for (FolderInfo folder : mBgDataModel.folders) { 744 Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR); 745 verifier.setFolderInfo(folder); 746 747 int numItemsInPreview = 0; 748 for (ShortcutInfo info : folder.contents) { 749 if (info.usingLowResIcon 750 && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION 751 && verifier.isItemInPreview(info.rank)) { 752 mIconCache.getTitleAndIcon(info, false); 753 numItemsInPreview++; 754 } 755 756 if (numItemsInPreview >= MAX_NUM_ITEMS_IN_PREVIEW) { 757 break; 758 } 759 } 760 } 761 762 c.commitRestoredItems(); 763 if (!isSdCardReady && !pendingPackages.isEmpty()) { 764 context.registerReceiver( 765 new SdCardAvailableReceiver(mApp, pendingPackages), 766 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), 767 null, 768 new Handler(LauncherModel.getWorkerLooper())); 769 } 770 771 // Remove any empty screens 772 ArrayList<Long> unusedScreens = new ArrayList<>(mBgDataModel.workspaceScreens); 773 for (ItemInfo item: mBgDataModel.itemsIdMap) { 774 long screenId = item.screenId; 775 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP && 776 unusedScreens.contains(screenId)) { 777 unusedScreens.remove(screenId); 778 } 779 } 780 781 // If there are any empty screens remove them, and update. 782 if (unusedScreens.size() != 0) { 783 mBgDataModel.workspaceScreens.removeAll(unusedScreens); 784 LauncherModel.updateWorkspaceScreenOrder(context, mBgDataModel.workspaceScreens); 785 } 786 } 787 } 788 updateIconCache()789 private void updateIconCache() { 790 // Ignore packages which have a promise icon. 791 HashSet<String> packagesToIgnore = new HashSet<>(); 792 synchronized (mBgDataModel) { 793 for (ItemInfo info : mBgDataModel.itemsIdMap) { 794 if (info instanceof ShortcutInfo) { 795 ShortcutInfo si = (ShortcutInfo) info; 796 if (si.isPromise() && si.getTargetComponent() != null) { 797 packagesToIgnore.add(si.getTargetComponent().getPackageName()); 798 } 799 } else if (info instanceof LauncherAppWidgetInfo) { 800 LauncherAppWidgetInfo lawi = (LauncherAppWidgetInfo) info; 801 if (lawi.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) { 802 packagesToIgnore.add(lawi.providerName.getPackageName()); 803 } 804 } 805 } 806 } 807 mIconCache.updateDbIcons(packagesToIgnore); 808 } 809 loadAllApps()810 private void loadAllApps() { 811 final List<UserHandle> profiles = mUserManager.getUserProfiles(); 812 813 // Clear the list of apps 814 mBgAllAppsList.clear(); 815 for (UserHandle user : profiles) { 816 // Query for the set of apps 817 final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user); 818 // Fail if we don't have any apps 819 // TODO: Fix this. Only fail for the current user. 820 if (apps == null || apps.isEmpty()) { 821 return; 822 } 823 boolean quietMode = mUserManager.isQuietModeEnabled(user); 824 // Create the ApplicationInfos 825 for (int i = 0; i < apps.size(); i++) { 826 LauncherActivityInfo app = apps.get(i); 827 // This builds the icon bitmaps. 828 mBgAllAppsList.add(new AppInfo(app, user, quietMode), app); 829 } 830 } 831 832 if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) { 833 // get all active sessions and add them to the all apps list 834 for (PackageInstaller.SessionInfo info : 835 mPackageInstaller.getAllVerifiedSessions()) { 836 mBgAllAppsList.addPromiseApp(mApp.getContext(), 837 PackageInstallerCompat.PackageInstallInfo.fromInstallingState(info)); 838 } 839 } 840 841 mBgAllAppsList.added = new ArrayList<>(); 842 } 843 loadDeepShortcuts()844 private void loadDeepShortcuts() { 845 mBgDataModel.deepShortcutMap.clear(); 846 mBgDataModel.hasShortcutHostPermission = mShortcutManager.hasHostPermission(); 847 if (mBgDataModel.hasShortcutHostPermission) { 848 for (UserHandle user : mUserManager.getUserProfiles()) { 849 if (mUserManager.isUserUnlocked(user)) { 850 List<ShortcutInfoCompat> shortcuts = 851 mShortcutManager.queryForAllShortcuts(user); 852 mBgDataModel.updateDeepShortcutMap(null, user, shortcuts); 853 } 854 } 855 } 856 } 857 isValidProvider(AppWidgetProviderInfo provider)858 public static boolean isValidProvider(AppWidgetProviderInfo provider) { 859 return (provider != null) && (provider.provider != null) 860 && (provider.provider.getPackageName() != null); 861 } 862 } 863