1 /* 2 * Copyright (C) 2008 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; 18 19 import android.appwidget.AppWidgetProviderInfo; 20 import android.content.BroadcastReceiver; 21 import android.content.ComponentName; 22 import android.content.ContentProviderOperation; 23 import android.content.ContentResolver; 24 import android.content.ContentValues; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.content.pm.LauncherActivityInfo; 29 import android.net.Uri; 30 import android.os.Handler; 31 import android.os.HandlerThread; 32 import android.os.Looper; 33 import android.os.Process; 34 import android.os.SystemClock; 35 import android.os.Trace; 36 import android.os.UserHandle; 37 import android.support.annotation.Nullable; 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.compat.AppWidgetManagerCompat; 44 import com.android.launcher3.compat.LauncherAppsCompat; 45 import com.android.launcher3.compat.PackageInstallerCompat; 46 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; 47 import com.android.launcher3.compat.UserManagerCompat; 48 import com.android.launcher3.config.ProviderConfig; 49 import com.android.launcher3.dynamicui.ExtractionUtils; 50 import com.android.launcher3.folder.Folder; 51 import com.android.launcher3.folder.FolderIcon; 52 import com.android.launcher3.graphics.LauncherIcons; 53 import com.android.launcher3.logging.FileLog; 54 import com.android.launcher3.model.AddWorkspaceItemsTask; 55 import com.android.launcher3.model.BgDataModel; 56 import com.android.launcher3.model.CacheDataUpdatedTask; 57 import com.android.launcher3.model.ExtendedModelTask; 58 import com.android.launcher3.model.GridSizeMigrationTask; 59 import com.android.launcher3.model.LoaderCursor; 60 import com.android.launcher3.model.ModelWriter; 61 import com.android.launcher3.model.PackageInstallStateChangedTask; 62 import com.android.launcher3.model.PackageItemInfo; 63 import com.android.launcher3.model.PackageUpdatedTask; 64 import com.android.launcher3.model.SdCardAvailableReceiver; 65 import com.android.launcher3.model.ShortcutsChangedTask; 66 import com.android.launcher3.model.UserLockStateChangedTask; 67 import com.android.launcher3.model.WidgetItem; 68 import com.android.launcher3.model.WidgetsModel; 69 import com.android.launcher3.provider.ImportDataTask; 70 import com.android.launcher3.provider.LauncherDbUtils; 71 import com.android.launcher3.shortcuts.DeepShortcutManager; 72 import com.android.launcher3.shortcuts.ShortcutInfoCompat; 73 import com.android.launcher3.shortcuts.ShortcutKey; 74 import com.android.launcher3.util.ComponentKey; 75 import com.android.launcher3.util.ManagedProfileHeuristic; 76 import com.android.launcher3.util.MultiHashMap; 77 import com.android.launcher3.util.PackageManagerHelper; 78 import com.android.launcher3.util.PackageUserKey; 79 import com.android.launcher3.util.Preconditions; 80 import com.android.launcher3.util.Provider; 81 import com.android.launcher3.util.Thunk; 82 import com.android.launcher3.util.ViewOnDrawExecutor; 83 84 import java.io.FileDescriptor; 85 import java.io.PrintWriter; 86 import java.lang.ref.WeakReference; 87 import java.util.ArrayList; 88 import java.util.Collections; 89 import java.util.Comparator; 90 import java.util.HashMap; 91 import java.util.HashSet; 92 import java.util.Iterator; 93 import java.util.List; 94 import java.util.Map; 95 import java.util.Set; 96 import java.util.concurrent.CancellationException; 97 import java.util.concurrent.Executor; 98 99 /** 100 * Maintains in-memory state of the Launcher. It is expected that there should be only one 101 * LauncherModel object held in a static. Also provide APIs for updating the database state 102 * for the Launcher. 103 */ 104 public class LauncherModel extends BroadcastReceiver 105 implements LauncherAppsCompat.OnAppsChangedCallbackCompat { 106 static final boolean DEBUG_LOADERS = false; 107 private static final boolean DEBUG_RECEIVER = false; 108 109 static final String TAG = "Launcher.Model"; 110 111 private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons 112 private static final long INVALID_SCREEN_ID = -1L; 113 114 @Thunk final LauncherAppState mApp; 115 @Thunk final Object mLock = new Object(); 116 @Thunk DeferredHandler mHandler = new DeferredHandler(); 117 @Thunk LoaderTask mLoaderTask; 118 @Thunk boolean mIsLoaderTaskRunning; 119 @Thunk boolean mHasLoaderCompletedOnce; 120 @Thunk boolean mIsManagedHeuristicAppsUpdated; 121 122 @Thunk static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader"); 123 static { sWorkerThread.start()124 sWorkerThread.start(); 125 } 126 @Thunk static final Handler sWorker = new Handler(sWorkerThread.getLooper()); 127 128 // Indicates whether the current model data is valid or not. 129 // We start off with everything not loaded. After that, we assume that 130 // our monitoring of the package manager provides all updates and we never 131 // need to do a requery. This is only ever touched from the loader thread. 132 private boolean mModelLoaded; isModelLoaded()133 public boolean isModelLoaded() { 134 synchronized (mLock) { 135 return mModelLoaded && mLoaderTask == null; 136 } 137 } 138 139 /** 140 * Set of runnables to be called on the background thread after the workspace binding 141 * is complete. 142 */ 143 static final ArrayList<Runnable> mBindCompleteRunnables = new ArrayList<Runnable>(); 144 145 @Thunk WeakReference<Callbacks> mCallbacks; 146 147 // < only access in worker thread > 148 private final AllAppsList mBgAllAppsList; 149 // Entire list of widgets. 150 private final WidgetsModel mBgWidgetsModel; 151 152 private boolean mHasShortcutHostPermission; 153 // Runnable to check if the shortcuts permission has changed. 154 private final Runnable mShortcutPermissionCheckRunnable = new Runnable() { 155 @Override 156 public void run() { 157 if (mModelLoaded) { 158 boolean hasShortcutHostPermission = 159 DeepShortcutManager.getInstance(mApp.getContext()).hasHostPermission(); 160 if (hasShortcutHostPermission != mHasShortcutHostPermission) { 161 forceReload(); 162 } 163 } 164 } 165 }; 166 167 /** 168 * All the static data should be accessed on the background thread, A lock should be acquired 169 * on this object when accessing any data from this model. 170 */ 171 static final BgDataModel sBgDataModel = new BgDataModel(); 172 173 // </ only access in worker thread > 174 175 private final IconCache mIconCache; 176 177 private final LauncherAppsCompat mLauncherApps; 178 private final UserManagerCompat mUserManager; 179 180 public interface Callbacks { setLoadOnResume()181 public boolean setLoadOnResume(); getCurrentWorkspaceScreen()182 public int getCurrentWorkspaceScreen(); clearPendingBinds()183 public void clearPendingBinds(); startBinding()184 public void startBinding(); bindItems(ArrayList<ItemInfo> shortcuts, int start, int end, boolean forceAnimateIcons)185 public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end, 186 boolean forceAnimateIcons); bindScreens(ArrayList<Long> orderedScreenIds)187 public void bindScreens(ArrayList<Long> orderedScreenIds); finishFirstPageBind(ViewOnDrawExecutor executor)188 public void finishFirstPageBind(ViewOnDrawExecutor executor); finishBindingItems()189 public void finishBindingItems(); bindAppWidget(LauncherAppWidgetInfo info)190 public void bindAppWidget(LauncherAppWidgetInfo info); bindAllApplications(ArrayList<AppInfo> apps)191 public void bindAllApplications(ArrayList<AppInfo> apps); bindAppsAdded(ArrayList<Long> newScreens, ArrayList<ItemInfo> addNotAnimated, ArrayList<ItemInfo> addAnimated, ArrayList<AppInfo> addedApps)192 public void bindAppsAdded(ArrayList<Long> newScreens, 193 ArrayList<ItemInfo> addNotAnimated, 194 ArrayList<ItemInfo> addAnimated, 195 ArrayList<AppInfo> addedApps); bindAppsUpdated(ArrayList<AppInfo> apps)196 public void bindAppsUpdated(ArrayList<AppInfo> apps); bindShortcutsChanged(ArrayList<ShortcutInfo> updated, ArrayList<ShortcutInfo> removed, UserHandle user)197 public void bindShortcutsChanged(ArrayList<ShortcutInfo> updated, 198 ArrayList<ShortcutInfo> removed, UserHandle user); bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets)199 public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets); bindRestoreItemsChange(HashSet<ItemInfo> updates)200 public void bindRestoreItemsChange(HashSet<ItemInfo> updates); bindWorkspaceComponentsRemoved( HashSet<String> packageNames, HashSet<ComponentName> components, UserHandle user)201 public void bindWorkspaceComponentsRemoved( 202 HashSet<String> packageNames, HashSet<ComponentName> components, 203 UserHandle user); bindAppInfosRemoved(ArrayList<AppInfo> appInfos)204 public void bindAppInfosRemoved(ArrayList<AppInfo> appInfos); notifyWidgetProvidersChanged()205 public void notifyWidgetProvidersChanged(); bindAllWidgets(MultiHashMap<PackageItemInfo, WidgetItem> widgets)206 public void bindAllWidgets(MultiHashMap<PackageItemInfo, WidgetItem> widgets); onPageBoundSynchronously(int page)207 public void onPageBoundSynchronously(int page); executeOnNextDraw(ViewOnDrawExecutor executor)208 public void executeOnNextDraw(ViewOnDrawExecutor executor); bindDeepShortcutMap(MultiHashMap<ComponentKey, String> deepShortcutMap)209 public void bindDeepShortcutMap(MultiHashMap<ComponentKey, String> deepShortcutMap); 210 } 211 LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter)212 LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) { 213 Context context = app.getContext(); 214 mApp = app; 215 mBgAllAppsList = new AllAppsList(iconCache, appFilter); 216 mBgWidgetsModel = new WidgetsModel(iconCache, appFilter); 217 mIconCache = iconCache; 218 219 mLauncherApps = LauncherAppsCompat.getInstance(context); 220 mUserManager = UserManagerCompat.getInstance(context); 221 } 222 223 /** Runs the specified runnable immediately if called from the main thread, otherwise it is 224 * posted on the main thread handler. */ runOnMainThread(Runnable r)225 private void runOnMainThread(Runnable r) { 226 if (sWorkerThread.getThreadId() == Process.myTid()) { 227 // If we are on the worker thread, post onto the main handler 228 mHandler.post(r); 229 } else { 230 r.run(); 231 } 232 } 233 234 /** Runs the specified runnable immediately if called from the worker thread, otherwise it is 235 * posted on the worker thread handler. */ runOnWorkerThread(Runnable r)236 private static void runOnWorkerThread(Runnable r) { 237 if (sWorkerThread.getThreadId() == Process.myTid()) { 238 r.run(); 239 } else { 240 // If we are not on the worker thread, then post to the worker handler 241 sWorker.post(r); 242 } 243 } 244 setPackageState(PackageInstallInfo installInfo)245 public void setPackageState(PackageInstallInfo installInfo) { 246 enqueueModelUpdateTask(new PackageInstallStateChangedTask(installInfo)); 247 } 248 249 /** 250 * Updates the icons and label of all pending icons for the provided package name. 251 */ updateSessionDisplayInfo(final String packageName)252 public void updateSessionDisplayInfo(final String packageName) { 253 HashSet<String> packages = new HashSet<>(); 254 packages.add(packageName); 255 enqueueModelUpdateTask(new CacheDataUpdatedTask( 256 CacheDataUpdatedTask.OP_SESSION_UPDATE, Process.myUserHandle(), packages)); 257 } 258 259 /** 260 * Adds the provided items to the workspace. 261 */ addAndBindAddedWorkspaceItems(List<ItemInfo> workspaceApps)262 public void addAndBindAddedWorkspaceItems(List<ItemInfo> workspaceApps) { 263 addAndBindAddedWorkspaceItems(Provider.of(workspaceApps)); 264 } 265 266 /** 267 * Adds the provided items to the workspace. 268 */ addAndBindAddedWorkspaceItems( Provider<List<ItemInfo>> appsProvider)269 public void addAndBindAddedWorkspaceItems( 270 Provider<List<ItemInfo>> appsProvider) { 271 enqueueModelUpdateTask(new AddWorkspaceItemsTask(appsProvider)); 272 } 273 getWriter(boolean hasVerticalHotseat)274 public ModelWriter getWriter(boolean hasVerticalHotseat) { 275 return new ModelWriter(mApp.getContext(), sBgDataModel, hasVerticalHotseat); 276 } 277 checkItemInfoLocked( final long itemId, final ItemInfo item, StackTraceElement[] stackTrace)278 static void checkItemInfoLocked( 279 final long itemId, final ItemInfo item, StackTraceElement[] stackTrace) { 280 ItemInfo modelItem = sBgDataModel.itemsIdMap.get(itemId); 281 if (modelItem != null && item != modelItem) { 282 // check all the data is consistent 283 if (modelItem instanceof ShortcutInfo && item instanceof ShortcutInfo) { 284 ShortcutInfo modelShortcut = (ShortcutInfo) modelItem; 285 ShortcutInfo shortcut = (ShortcutInfo) item; 286 if (modelShortcut.title.toString().equals(shortcut.title.toString()) && 287 modelShortcut.intent.filterEquals(shortcut.intent) && 288 modelShortcut.id == shortcut.id && 289 modelShortcut.itemType == shortcut.itemType && 290 modelShortcut.container == shortcut.container && 291 modelShortcut.screenId == shortcut.screenId && 292 modelShortcut.cellX == shortcut.cellX && 293 modelShortcut.cellY == shortcut.cellY && 294 modelShortcut.spanX == shortcut.spanX && 295 modelShortcut.spanY == shortcut.spanY) { 296 // For all intents and purposes, this is the same object 297 return; 298 } 299 } 300 301 // the modelItem needs to match up perfectly with item if our model is 302 // to be consistent with the database-- for now, just require 303 // modelItem == item or the equality check above 304 String msg = "item: " + ((item != null) ? item.toString() : "null") + 305 "modelItem: " + 306 ((modelItem != null) ? modelItem.toString() : "null") + 307 "Error: ItemInfo passed to checkItemInfo doesn't match original"; 308 RuntimeException e = new RuntimeException(msg); 309 if (stackTrace != null) { 310 e.setStackTrace(stackTrace); 311 } 312 throw e; 313 } 314 } 315 checkItemInfo(final ItemInfo item)316 static void checkItemInfo(final ItemInfo item) { 317 final StackTraceElement[] stackTrace = new Throwable().getStackTrace(); 318 final long itemId = item.id; 319 Runnable r = new Runnable() { 320 public void run() { 321 synchronized (sBgDataModel) { 322 checkItemInfoLocked(itemId, item, stackTrace); 323 } 324 } 325 }; 326 runOnWorkerThread(r); 327 } 328 329 /** 330 * Update the order of the workspace screens in the database. The array list contains 331 * a list of screen ids in the order that they should appear. 332 */ updateWorkspaceScreenOrder(Context context, final ArrayList<Long> screens)333 public static void updateWorkspaceScreenOrder(Context context, final ArrayList<Long> screens) { 334 final ArrayList<Long> screensCopy = new ArrayList<Long>(screens); 335 final ContentResolver cr = context.getContentResolver(); 336 final Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI; 337 338 // Remove any negative screen ids -- these aren't persisted 339 Iterator<Long> iter = screensCopy.iterator(); 340 while (iter.hasNext()) { 341 long id = iter.next(); 342 if (id < 0) { 343 iter.remove(); 344 } 345 } 346 347 Runnable r = new Runnable() { 348 @Override 349 public void run() { 350 ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); 351 // Clear the table 352 ops.add(ContentProviderOperation.newDelete(uri).build()); 353 int count = screensCopy.size(); 354 for (int i = 0; i < count; i++) { 355 ContentValues v = new ContentValues(); 356 long screenId = screensCopy.get(i); 357 v.put(LauncherSettings.WorkspaceScreens._ID, screenId); 358 v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i); 359 ops.add(ContentProviderOperation.newInsert(uri).withValues(v).build()); 360 } 361 362 try { 363 cr.applyBatch(LauncherProvider.AUTHORITY, ops); 364 } catch (Exception ex) { 365 throw new RuntimeException(ex); 366 } 367 368 synchronized (sBgDataModel) { 369 sBgDataModel.workspaceScreens.clear(); 370 sBgDataModel.workspaceScreens.addAll(screensCopy); 371 } 372 } 373 }; 374 runOnWorkerThread(r); 375 } 376 377 /** 378 * Set this as the current Launcher activity object for the loader. 379 */ initialize(Callbacks callbacks)380 public void initialize(Callbacks callbacks) { 381 synchronized (mLock) { 382 Preconditions.assertUIThread(); 383 // Remove any queued UI runnables 384 mHandler.cancelAll(); 385 mCallbacks = new WeakReference<>(callbacks); 386 } 387 } 388 389 @Override onPackageChanged(String packageName, UserHandle user)390 public void onPackageChanged(String packageName, UserHandle user) { 391 int op = PackageUpdatedTask.OP_UPDATE; 392 enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packageName)); 393 } 394 395 @Override onPackageRemoved(String packageName, UserHandle user)396 public void onPackageRemoved(String packageName, UserHandle user) { 397 onPackagesRemoved(user, packageName); 398 } 399 onPackagesRemoved(UserHandle user, String... packages)400 public void onPackagesRemoved(UserHandle user, String... packages) { 401 int op = PackageUpdatedTask.OP_REMOVE; 402 enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packages)); 403 } 404 405 @Override onPackageAdded(String packageName, UserHandle user)406 public void onPackageAdded(String packageName, UserHandle user) { 407 int op = PackageUpdatedTask.OP_ADD; 408 enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packageName)); 409 } 410 411 @Override onPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing)412 public void onPackagesAvailable(String[] packageNames, UserHandle user, 413 boolean replacing) { 414 enqueueModelUpdateTask( 415 new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE, user, packageNames)); 416 } 417 418 @Override onPackagesUnavailable(String[] packageNames, UserHandle user, boolean replacing)419 public void onPackagesUnavailable(String[] packageNames, UserHandle user, 420 boolean replacing) { 421 if (!replacing) { 422 enqueueModelUpdateTask(new PackageUpdatedTask( 423 PackageUpdatedTask.OP_UNAVAILABLE, user, packageNames)); 424 } 425 } 426 427 @Override onPackagesSuspended(String[] packageNames, UserHandle user)428 public void onPackagesSuspended(String[] packageNames, UserHandle user) { 429 enqueueModelUpdateTask(new PackageUpdatedTask( 430 PackageUpdatedTask.OP_SUSPEND, user, packageNames)); 431 } 432 433 @Override onPackagesUnsuspended(String[] packageNames, UserHandle user)434 public void onPackagesUnsuspended(String[] packageNames, UserHandle user) { 435 enqueueModelUpdateTask(new PackageUpdatedTask( 436 PackageUpdatedTask.OP_UNSUSPEND, user, packageNames)); 437 } 438 439 @Override onShortcutsChanged(String packageName, List<ShortcutInfoCompat> shortcuts, UserHandle user)440 public void onShortcutsChanged(String packageName, List<ShortcutInfoCompat> shortcuts, 441 UserHandle user) { 442 enqueueModelUpdateTask(new ShortcutsChangedTask(packageName, shortcuts, user, true)); 443 } 444 updatePinnedShortcuts(String packageName, List<ShortcutInfoCompat> shortcuts, UserHandle user)445 public void updatePinnedShortcuts(String packageName, List<ShortcutInfoCompat> shortcuts, 446 UserHandle user) { 447 enqueueModelUpdateTask(new ShortcutsChangedTask(packageName, shortcuts, user, false)); 448 } 449 450 /** 451 * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and 452 * ACTION_PACKAGE_CHANGED. 453 */ 454 @Override onReceive(Context context, Intent intent)455 public void onReceive(Context context, Intent intent) { 456 if (DEBUG_RECEIVER) Log.d(TAG, "onReceive intent=" + intent); 457 458 final String action = intent.getAction(); 459 if (Intent.ACTION_LOCALE_CHANGED.equals(action)) { 460 // If we have changed locale we need to clear out the labels in all apps/workspace. 461 forceReload(); 462 } else if (Intent.ACTION_MANAGED_PROFILE_ADDED.equals(action) 463 || Intent.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) { 464 UserManagerCompat.getInstance(context).enableAndResetCache(); 465 forceReload(); 466 } else if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) || 467 Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action) || 468 Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) { 469 UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER); 470 if (user != null) { 471 if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) || 472 Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) { 473 enqueueModelUpdateTask(new PackageUpdatedTask( 474 PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE, user)); 475 } 476 477 // ACTION_MANAGED_PROFILE_UNAVAILABLE sends the profile back to locked mode, so 478 // we need to run the state change task again. 479 if (Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action) || 480 Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) { 481 enqueueModelUpdateTask(new UserLockStateChangedTask(user)); 482 } 483 } 484 } else if (Intent.ACTION_WALLPAPER_CHANGED.equals(action)) { 485 ExtractionUtils.startColorExtractionServiceIfNecessary(context); 486 } 487 } 488 489 /** 490 * Reloads the workspace items from the DB and re-binds the workspace. This should generally 491 * not be called as DB updates are automatically followed by UI update 492 */ forceReload()493 public void forceReload() { 494 synchronized (mLock) { 495 // Stop any existing loaders first, so they don't set mModelLoaded to true later 496 stopLoaderLocked(); 497 mModelLoaded = false; 498 } 499 500 // Do this here because if the launcher activity is running it will be restarted. 501 // If it's not running startLoaderFromBackground will merely tell it that it needs 502 // to reload. 503 startLoaderFromBackground(); 504 } 505 506 /** 507 * When the launcher is in the background, it's possible for it to miss paired 508 * configuration changes. So whenever we trigger the loader from the background 509 * tell the launcher that it needs to re-run the loader when it comes back instead 510 * of doing it now. 511 */ startLoaderFromBackground()512 public void startLoaderFromBackground() { 513 Callbacks callbacks = getCallback(); 514 if (callbacks != null) { 515 // Only actually run the loader if they're not paused. 516 if (!callbacks.setLoadOnResume()) { 517 startLoader(callbacks.getCurrentWorkspaceScreen()); 518 } 519 } 520 } 521 522 /** 523 * If there is already a loader task running, tell it to stop. 524 */ stopLoaderLocked()525 private void stopLoaderLocked() { 526 LoaderTask oldTask = mLoaderTask; 527 if (oldTask != null) { 528 oldTask.stopLocked(); 529 } 530 } 531 isCurrentCallbacks(Callbacks callbacks)532 public boolean isCurrentCallbacks(Callbacks callbacks) { 533 return (mCallbacks != null && mCallbacks.get() == callbacks); 534 } 535 536 /** 537 * Starts the loader. Tries to bind {@params synchronousBindPage} synchronously if possible. 538 * @return true if the page could be bound synchronously. 539 */ startLoader(int synchronousBindPage)540 public boolean startLoader(int synchronousBindPage) { 541 // Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems 542 InstallShortcutReceiver.enableInstallQueue(); 543 synchronized (mLock) { 544 // Don't bother to start the thread if we know it's not going to do anything 545 if (mCallbacks != null && mCallbacks.get() != null) { 546 final Callbacks oldCallbacks = mCallbacks.get(); 547 // Clear any pending bind-runnables from the synchronized load process. 548 runOnMainThread(new Runnable() { 549 public void run() { 550 oldCallbacks.clearPendingBinds(); 551 } 552 }); 553 554 // If there is already one running, tell it to stop. 555 stopLoaderLocked(); 556 mLoaderTask = new LoaderTask(mApp.getContext(), synchronousBindPage); 557 if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE 558 && mModelLoaded && !mIsLoaderTaskRunning) { 559 mLoaderTask.runBindSynchronousPage(synchronousBindPage); 560 return true; 561 } else { 562 sWorkerThread.setPriority(Thread.NORM_PRIORITY); 563 sWorker.post(mLoaderTask); 564 } 565 } 566 } 567 return false; 568 } 569 stopLoader()570 public void stopLoader() { 571 synchronized (mLock) { 572 if (mLoaderTask != null) { 573 mLoaderTask.stopLocked(); 574 } 575 } 576 } 577 578 /** 579 * Loads the workspace screen ids in an ordered list. 580 */ loadWorkspaceScreensDb(Context context)581 public static ArrayList<Long> loadWorkspaceScreensDb(Context context) { 582 final ContentResolver contentResolver = context.getContentResolver(); 583 final Uri screensUri = LauncherSettings.WorkspaceScreens.CONTENT_URI; 584 585 // Get screens ordered by rank. 586 return LauncherDbUtils.getScreenIdsFromCursor(contentResolver.query( 587 screensUri, null, null, null, LauncherSettings.WorkspaceScreens.SCREEN_RANK)); 588 } 589 590 /** 591 * Runnable for the thread that loads the contents of the launcher: 592 * - workspace icons 593 * - widgets 594 * - all apps icons 595 * - deep shortcuts within apps 596 */ 597 private class LoaderTask implements Runnable { 598 private Context mContext; 599 private int mPageToBindFirst; 600 601 @Thunk boolean mIsLoadingAndBindingWorkspace; 602 private boolean mStopped; 603 @Thunk boolean mLoadAndBindStepFinished; 604 LoaderTask(Context context, int pageToBindFirst)605 LoaderTask(Context context, int pageToBindFirst) { 606 mContext = context; 607 mPageToBindFirst = pageToBindFirst; 608 } 609 waitForIdle()610 private void waitForIdle() { 611 // Wait until the either we're stopped or the other threads are done. 612 // This way we don't start loading all apps until the workspace has settled 613 // down. 614 synchronized (LoaderTask.this) { 615 final long workspaceWaitTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; 616 617 mHandler.postIdle(new Runnable() { 618 public void run() { 619 synchronized (LoaderTask.this) { 620 mLoadAndBindStepFinished = true; 621 if (DEBUG_LOADERS) { 622 Log.d(TAG, "done with previous binding step"); 623 } 624 LoaderTask.this.notify(); 625 } 626 } 627 }); 628 629 while (!mStopped && !mLoadAndBindStepFinished) { 630 try { 631 // Just in case mFlushingWorkerThread changes but we aren't woken up, 632 // wait no longer than 1sec at a time 633 this.wait(1000); 634 } catch (InterruptedException ex) { 635 // Ignore 636 } 637 } 638 if (DEBUG_LOADERS) { 639 Log.d(TAG, "waited " 640 + (SystemClock.uptimeMillis()-workspaceWaitTime) 641 + "ms for previous step to finish binding"); 642 } 643 } 644 } 645 runBindSynchronousPage(int synchronousBindPage)646 void runBindSynchronousPage(int synchronousBindPage) { 647 if (synchronousBindPage == PagedView.INVALID_RESTORE_PAGE) { 648 // Ensure that we have a valid page index to load synchronously 649 throw new RuntimeException("Should not call runBindSynchronousPage() without " + 650 "valid page index"); 651 } 652 if (!mModelLoaded) { 653 // Ensure that we don't try and bind a specified page when the pages have not been 654 // loaded already (we should load everything asynchronously in that case) 655 throw new RuntimeException("Expecting AllApps and Workspace to be loaded"); 656 } 657 synchronized (mLock) { 658 if (mIsLoaderTaskRunning) { 659 // Ensure that we are never running the background loading at this point since 660 // we also touch the background collections 661 throw new RuntimeException("Error! Background loading is already running"); 662 } 663 } 664 665 // XXX: Throw an exception if we are already loading (since we touch the worker thread 666 // data structures, we can't allow any other thread to touch that data, but because 667 // this call is synchronous, we can get away with not locking). 668 669 // The LauncherModel is static in the LauncherAppState and mHandler may have queued 670 // operations from the previous activity. We need to ensure that all queued operations 671 // are executed before any synchronous binding work is done. 672 mHandler.flush(); 673 674 // Divide the set of loaded items into those that we are binding synchronously, and 675 // everything else that is to be bound normally (asynchronously). 676 bindWorkspace(synchronousBindPage); 677 // XXX: For now, continue posting the binding of AllApps as there are other issues that 678 // arise from that. 679 onlyBindAllApps(); 680 681 bindDeepShortcuts(); 682 } 683 verifyNotStopped()684 private void verifyNotStopped() throws CancellationException { 685 synchronized (LoaderTask.this) { 686 if (mStopped) { 687 throw new CancellationException("Loader stopped"); 688 } 689 } 690 } 691 run()692 public void run() { 693 synchronized (mLock) { 694 if (mStopped) { 695 return; 696 } 697 mIsLoaderTaskRunning = true; 698 } 699 700 try { 701 if (DEBUG_LOADERS) Log.d(TAG, "step 1.1: loading workspace"); 702 // Set to false in bindWorkspace() 703 mIsLoadingAndBindingWorkspace = true; 704 loadWorkspace(); 705 706 verifyNotStopped(); 707 if (DEBUG_LOADERS) Log.d(TAG, "step 1.2: bind workspace workspace"); 708 bindWorkspace(mPageToBindFirst); 709 710 // Take a break 711 if (DEBUG_LOADERS) Log.d(TAG, "step 1 completed, wait for idle"); 712 waitForIdle(); 713 verifyNotStopped(); 714 715 // second step 716 if (DEBUG_LOADERS) Log.d(TAG, "step 2.1: loading all apps"); 717 loadAllApps(); 718 719 verifyNotStopped(); 720 if (DEBUG_LOADERS) Log.d(TAG, "step 2.2: Update icon cache"); 721 updateIconCache(); 722 723 // Take a break 724 if (DEBUG_LOADERS) Log.d(TAG, "step 2 completed, wait for idle"); 725 waitForIdle(); 726 verifyNotStopped(); 727 728 // third step 729 if (DEBUG_LOADERS) Log.d(TAG, "step 3.1: loading deep shortcuts"); 730 loadDeepShortcuts(); 731 732 verifyNotStopped(); 733 if (DEBUG_LOADERS) Log.d(TAG, "step 3.2: bind deep shortcuts"); 734 bindDeepShortcuts(); 735 736 // Take a break 737 if (DEBUG_LOADERS) Log.d(TAG, "step 3 completed, wait for idle"); 738 waitForIdle(); 739 verifyNotStopped(); 740 741 // fourth step 742 if (DEBUG_LOADERS) Log.d(TAG, "step 4.1: loading widgets"); 743 refreshAndBindWidgetsAndShortcuts(getCallback(), false /* bindFirst */, 744 null /* packageUser */); 745 746 synchronized (mLock) { 747 // Everything loaded bind the data. 748 mModelLoaded = true; 749 mHasLoaderCompletedOnce = true; 750 } 751 } catch (CancellationException e) { 752 // Loader stopped, ignore 753 } finally { 754 // Clear out this reference, otherwise we end up holding it until all of the 755 // callback runnables are done. 756 mContext = null; 757 758 synchronized (mLock) { 759 // If we are still the last one to be scheduled, remove ourselves. 760 if (mLoaderTask == this) { 761 mLoaderTask = null; 762 } 763 mIsLoaderTaskRunning = false; 764 } 765 } 766 } 767 stopLocked()768 public void stopLocked() { 769 synchronized (LoaderTask.this) { 770 mStopped = true; 771 this.notify(); 772 } 773 } 774 775 /** 776 * Gets the callbacks object. If we've been stopped, or if the launcher object 777 * has somehow been garbage collected, return null instead. Pass in the Callbacks 778 * object that was around when the deferred message was scheduled, and if there's 779 * a new Callbacks object around then also return null. This will save us from 780 * calling onto it with data that will be ignored. 781 */ tryGetCallbacks(Callbacks oldCallbacks)782 Callbacks tryGetCallbacks(Callbacks oldCallbacks) { 783 synchronized (mLock) { 784 if (mStopped) { 785 return null; 786 } 787 788 if (mCallbacks == null) { 789 return null; 790 } 791 792 final Callbacks callbacks = mCallbacks.get(); 793 if (callbacks != oldCallbacks) { 794 return null; 795 } 796 if (callbacks == null) { 797 Log.w(TAG, "no mCallbacks"); 798 return null; 799 } 800 801 return callbacks; 802 } 803 } 804 loadWorkspace()805 private void loadWorkspace() { 806 if (LauncherAppState.PROFILE_STARTUP) { 807 Trace.beginSection("Loading Workspace"); 808 } 809 810 final Context context = mContext; 811 final ContentResolver contentResolver = context.getContentResolver(); 812 final PackageManagerHelper pmHelper = new PackageManagerHelper(context); 813 final boolean isSafeMode = pmHelper.isSafeMode(); 814 final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context); 815 final DeepShortcutManager shortcutManager = DeepShortcutManager.getInstance(context); 816 final boolean isSdCardReady = Utilities.isBootCompleted(); 817 final MultiHashMap<UserHandle, String> pendingPackages = new MultiHashMap<>(); 818 819 boolean clearDb = false; 820 try { 821 ImportDataTask.performImportIfPossible(context); 822 } catch (Exception e) { 823 // Migration failed. Clear workspace. 824 clearDb = true; 825 } 826 827 if (!clearDb && GridSizeMigrationTask.ENABLED && 828 !GridSizeMigrationTask.migrateGridIfNeeded(mContext)) { 829 // Migration failed. Clear workspace. 830 clearDb = true; 831 } 832 833 if (clearDb) { 834 Log.d(TAG, "loadWorkspace: resetting launcher database"); 835 LauncherSettings.Settings.call(contentResolver, 836 LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB); 837 } 838 839 Log.d(TAG, "loadWorkspace: loading default favorites"); 840 LauncherSettings.Settings.call(contentResolver, 841 LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES); 842 843 synchronized (sBgDataModel) { 844 sBgDataModel.clear(); 845 846 final HashMap<String, Integer> installingPkgs = PackageInstallerCompat 847 .getInstance(mContext).updateAndGetActiveSessionCache(); 848 sBgDataModel.workspaceScreens.addAll(loadWorkspaceScreensDb(mContext)); 849 850 Map<ShortcutKey, ShortcutInfoCompat> shortcutKeyToPinnedShortcuts = new HashMap<>(); 851 final LoaderCursor c = new LoaderCursor(contentResolver.query( 852 LauncherSettings.Favorites.CONTENT_URI, null, null, null, null), mApp); 853 854 HashMap<ComponentKey, AppWidgetProviderInfo> widgetProvidersMap = null; 855 856 try { 857 final int appWidgetIdIndex = c.getColumnIndexOrThrow( 858 LauncherSettings.Favorites.APPWIDGET_ID); 859 final int appWidgetProviderIndex = c.getColumnIndexOrThrow( 860 LauncherSettings.Favorites.APPWIDGET_PROVIDER); 861 final int spanXIndex = c.getColumnIndexOrThrow 862 (LauncherSettings.Favorites.SPANX); 863 final int spanYIndex = c.getColumnIndexOrThrow( 864 LauncherSettings.Favorites.SPANY); 865 final int rankIndex = c.getColumnIndexOrThrow( 866 LauncherSettings.Favorites.RANK); 867 final int optionsIndex = c.getColumnIndexOrThrow( 868 LauncherSettings.Favorites.OPTIONS); 869 870 final LongSparseArray<UserHandle> allUsers = c.allUsers; 871 final LongSparseArray<Boolean> quietMode = new LongSparseArray<>(); 872 final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>(); 873 for (UserHandle user : mUserManager.getUserProfiles()) { 874 long serialNo = mUserManager.getSerialNumberForUser(user); 875 allUsers.put(serialNo, user); 876 quietMode.put(serialNo, mUserManager.isQuietModeEnabled(user)); 877 878 boolean userUnlocked = mUserManager.isUserUnlocked(user); 879 880 // We can only query for shortcuts when the user is unlocked. 881 if (userUnlocked) { 882 List<ShortcutInfoCompat> pinnedShortcuts = 883 shortcutManager.queryForPinnedShortcuts(null, user); 884 if (shortcutManager.wasLastCallSuccess()) { 885 for (ShortcutInfoCompat shortcut : pinnedShortcuts) { 886 shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut), 887 shortcut); 888 } 889 } else { 890 // Shortcut manager can fail due to some race condition when the 891 // lock state changes too frequently. For the purpose of the loading 892 // shortcuts, consider the user is still locked. 893 userUnlocked = false; 894 } 895 } 896 unlockedUsers.put(serialNo, userUnlocked); 897 } 898 899 ShortcutInfo info; 900 LauncherAppWidgetInfo appWidgetInfo; 901 Intent intent; 902 String targetPkg; 903 904 while (!mStopped && c.moveToNext()) { 905 try { 906 if (c.user == null) { 907 // User has been deleted, remove the item. 908 c.markDeleted("User has been deleted"); 909 continue; 910 } 911 912 boolean allowMissingTarget = false; 913 switch (c.itemType) { 914 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 915 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: 916 case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: 917 intent = c.parseIntent(); 918 if (intent == null) { 919 c.markDeleted("Invalid or null intent"); 920 continue; 921 } 922 923 int disabledState = quietMode.get(c.serialNumber) ? 924 ShortcutInfo.FLAG_DISABLED_QUIET_USER : 0; 925 ComponentName cn = intent.getComponent(); 926 targetPkg = cn == null ? intent.getPackage() : cn.getPackageName(); 927 928 if (!Process.myUserHandle().equals(c.user)) { 929 if (c.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) { 930 c.markDeleted("Legacy shortcuts are only allowed for default user"); 931 continue; 932 } else if (c.restoreFlag != 0) { 933 // Don't restore items for other profiles. 934 c.markDeleted("Restore from managed profile not supported"); 935 continue; 936 } 937 } 938 if (TextUtils.isEmpty(targetPkg) && 939 c.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) { 940 c.markDeleted("Only legacy shortcuts can have null package"); 941 continue; 942 } 943 944 // If there is no target package, its an implicit intent 945 // (legacy shortcut) which is always valid 946 boolean validTarget = TextUtils.isEmpty(targetPkg) || 947 launcherApps.isPackageEnabledForProfile(targetPkg, c.user); 948 949 if (cn != null && validTarget) { 950 // If the apk is present and the shortcut points to a specific 951 // component. 952 953 // If the component is already present 954 if (launcherApps.isActivityEnabledForProfile(cn, c.user)) { 955 // no special handling necessary for this item 956 c.markRestored(); 957 } else { 958 if (c.hasRestoreFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) { 959 // We allow auto install apps to have their intent 960 // updated after an install. 961 intent = pmHelper.getAppLaunchIntent(targetPkg, c.user); 962 if (intent != null) { 963 c.restoreFlag = 0; 964 c.updater().put( 965 LauncherSettings.Favorites.INTENT, 966 intent.toUri(0)).commit(); 967 cn = intent.getComponent(); 968 } else { 969 c.markDeleted("Unable to find a launch target"); 970 continue; 971 } 972 } else { 973 // The app is installed but the component is no 974 // longer available. 975 c.markDeleted("Invalid component removed: " + cn); 976 continue; 977 } 978 } 979 } 980 // else if cn == null => can't infer much, leave it 981 // else if !validPkg => could be restored icon or missing sd-card 982 983 if (!TextUtils.isEmpty(targetPkg) && !validTarget) { 984 // Points to a valid app (superset of cn != null) but the apk 985 // is not available. 986 987 if (c.restoreFlag != 0) { 988 // Package is not yet available but might be 989 // installed later. 990 FileLog.d(TAG, "package not yet restored: " + targetPkg); 991 992 if (c.hasRestoreFlag(ShortcutInfo.FLAG_RESTORE_STARTED)) { 993 // Restore has started once. 994 } else if (installingPkgs.containsKey(targetPkg)) { 995 // App restore has started. Update the flag 996 c.restoreFlag |= ShortcutInfo.FLAG_RESTORE_STARTED; 997 c.updater().commit(); 998 } else { 999 c.markDeleted("Unrestored app removed: " + targetPkg); 1000 continue; 1001 } 1002 } else if (pmHelper.isAppOnSdcard(targetPkg, c.user)) { 1003 // Package is present but not available. 1004 disabledState |= ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE; 1005 // Add the icon on the workspace anyway. 1006 allowMissingTarget = true; 1007 } else if (!isSdCardReady) { 1008 // SdCard is not ready yet. Package might get available, 1009 // once it is ready. 1010 Log.d(TAG, "Missing pkg, will check later: " + targetPkg); 1011 pendingPackages.addToList(c.user, targetPkg); 1012 // Add the icon on the workspace anyway. 1013 allowMissingTarget = true; 1014 } else { 1015 // Do not wait for external media load anymore. 1016 c.markDeleted("Invalid package removed: " + targetPkg); 1017 continue; 1018 } 1019 } 1020 1021 if (validTarget) { 1022 // The shortcut points to a valid target (either no target 1023 // or something which is ready to be used) 1024 c.markRestored(); 1025 } 1026 1027 boolean useLowResIcon = !c.isOnWorkspaceOrHotseat() && 1028 c.getInt(rankIndex) >= FolderIcon.NUM_ITEMS_IN_PREVIEW; 1029 1030 if (c.restoreFlag != 0) { 1031 // Already verified above that user is same as default user 1032 info = c.getRestoredItemInfo(intent); 1033 } else if (c.itemType == 1034 LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { 1035 info = c.getAppShortcutInfo( 1036 intent, allowMissingTarget, useLowResIcon); 1037 } else if (c.itemType == 1038 LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { 1039 1040 ShortcutKey key = ShortcutKey.fromIntent(intent, c.user); 1041 if (unlockedUsers.get(c.serialNumber)) { 1042 ShortcutInfoCompat pinnedShortcut = 1043 shortcutKeyToPinnedShortcuts.get(key); 1044 if (pinnedShortcut == null) { 1045 // The shortcut is no longer valid. 1046 c.markDeleted("Pinned shortcut not found"); 1047 continue; 1048 } 1049 info = new ShortcutInfo(pinnedShortcut, context); 1050 info.iconBitmap = LauncherIcons 1051 .createShortcutIcon(pinnedShortcut, context); 1052 if (pmHelper.isAppSuspended( 1053 pinnedShortcut.getPackage(), info.user)) { 1054 info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SUSPENDED; 1055 } 1056 intent = info.intent; 1057 } else { 1058 // Create a shortcut info in disabled mode for now. 1059 info = c.loadSimpleShortcut(); 1060 info.isDisabled |= ShortcutInfo.FLAG_DISABLED_LOCKED_USER; 1061 } 1062 } else { // item type == ITEM_TYPE_SHORTCUT 1063 info = c.loadSimpleShortcut(); 1064 1065 // Shortcuts are only available on the primary profile 1066 if (!TextUtils.isEmpty(targetPkg) 1067 && pmHelper.isAppSuspended(targetPkg, c.user)) { 1068 disabledState |= ShortcutInfo.FLAG_DISABLED_SUSPENDED; 1069 } 1070 1071 // App shortcuts that used to be automatically added to Launcher 1072 // didn't always have the correct intent flags set, so do that 1073 // here 1074 if (intent.getAction() != null && 1075 intent.getCategories() != null && 1076 intent.getAction().equals(Intent.ACTION_MAIN) && 1077 intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) { 1078 intent.addFlags( 1079 Intent.FLAG_ACTIVITY_NEW_TASK | 1080 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 1081 } 1082 } 1083 1084 if (info != null) { 1085 c.applyCommonProperties(info); 1086 1087 info.intent = intent; 1088 info.rank = c.getInt(rankIndex); 1089 info.spanX = 1; 1090 info.spanY = 1; 1091 info.isDisabled |= disabledState; 1092 if (isSafeMode && !Utilities.isSystemApp(context, intent)) { 1093 info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SAFEMODE; 1094 } 1095 1096 if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) { 1097 Integer progress = installingPkgs.get(targetPkg); 1098 if (progress != null) { 1099 info.setInstallProgress(progress); 1100 } else { 1101 info.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE; 1102 } 1103 } 1104 1105 c.checkAndAddItem(info, sBgDataModel); 1106 } else { 1107 throw new RuntimeException("Unexpected null ShortcutInfo"); 1108 } 1109 break; 1110 1111 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: 1112 FolderInfo folderInfo = sBgDataModel.findOrMakeFolder(c.id); 1113 c.applyCommonProperties(folderInfo); 1114 1115 // Do not trim the folder label, as is was set by the user. 1116 folderInfo.title = c.getString(c.titleIndex); 1117 folderInfo.spanX = 1; 1118 folderInfo.spanY = 1; 1119 folderInfo.options = c.getInt(optionsIndex); 1120 1121 // no special handling required for restored folders 1122 c.markRestored(); 1123 1124 c.checkAndAddItem(folderInfo, sBgDataModel); 1125 break; 1126 1127 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: 1128 case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: 1129 // Read all Launcher-specific widget details 1130 boolean customWidget = c.itemType == 1131 LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET; 1132 1133 int appWidgetId = c.getInt(appWidgetIdIndex); 1134 String savedProvider = c.getString(appWidgetProviderIndex); 1135 1136 final ComponentName component = 1137 ComponentName.unflattenFromString(savedProvider); 1138 1139 final boolean isIdValid = !c.hasRestoreFlag( 1140 LauncherAppWidgetInfo.FLAG_ID_NOT_VALID); 1141 final boolean wasProviderReady = !c.hasRestoreFlag( 1142 LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY); 1143 1144 if (widgetProvidersMap == null) { 1145 widgetProvidersMap = AppWidgetManagerCompat 1146 .getInstance(mContext).getAllProvidersMap(); 1147 } 1148 final AppWidgetProviderInfo provider = widgetProvidersMap.get( 1149 new ComponentKey( 1150 ComponentName.unflattenFromString(savedProvider), 1151 c.user)); 1152 1153 final boolean isProviderReady = isValidProvider(provider); 1154 if (!isSafeMode && !customWidget && 1155 wasProviderReady && !isProviderReady) { 1156 c.markDeleted( 1157 "Deleting widget that isn't installed anymore: " 1158 + provider); 1159 } else { 1160 if (isProviderReady) { 1161 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, 1162 provider.provider); 1163 1164 // The provider is available. So the widget is either 1165 // available or not available. We do not need to track 1166 // any future restore updates. 1167 int status = c.restoreFlag & 1168 ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED; 1169 if (!wasProviderReady) { 1170 // If provider was not previously ready, update the 1171 // status and UI flag. 1172 1173 // Id would be valid only if the widget restore broadcast was received. 1174 if (isIdValid) { 1175 status |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY; 1176 } else { 1177 status &= ~LauncherAppWidgetInfo 1178 .FLAG_PROVIDER_NOT_READY; 1179 } 1180 } 1181 appWidgetInfo.restoreStatus = status; 1182 } else { 1183 Log.v(TAG, "Widget restore pending id=" + c.id 1184 + " appWidgetId=" + appWidgetId 1185 + " status =" + c.restoreFlag); 1186 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, 1187 component); 1188 appWidgetInfo.restoreStatus = c.restoreFlag; 1189 Integer installProgress = installingPkgs.get(component.getPackageName()); 1190 1191 if (c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_RESTORE_STARTED)) { 1192 // Restore has started once. 1193 } else if (installProgress != null) { 1194 // App restore has started. Update the flag 1195 appWidgetInfo.restoreStatus |= 1196 LauncherAppWidgetInfo.FLAG_RESTORE_STARTED; 1197 } else if (!isSafeMode) { 1198 c.markDeleted("Unrestored widget removed: " + component); 1199 continue; 1200 } 1201 1202 appWidgetInfo.installProgress = 1203 installProgress == null ? 0 : installProgress; 1204 } 1205 if (appWidgetInfo.hasRestoreFlag( 1206 LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG)) { 1207 appWidgetInfo.bindOptions = c.parseIntent(); 1208 } 1209 1210 c.applyCommonProperties(appWidgetInfo); 1211 appWidgetInfo.spanX = c.getInt(spanXIndex); 1212 appWidgetInfo.spanY = c.getInt(spanYIndex); 1213 appWidgetInfo.user = c.user; 1214 1215 if (!c.isOnWorkspaceOrHotseat()) { 1216 c.markDeleted("Widget found where container != " + 1217 "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!"); 1218 continue; 1219 } 1220 1221 if (!customWidget) { 1222 String providerName = 1223 appWidgetInfo.providerName.flattenToString(); 1224 if (!providerName.equals(savedProvider) || 1225 (appWidgetInfo.restoreStatus != c.restoreFlag)) { 1226 c.updater() 1227 .put(LauncherSettings.Favorites.APPWIDGET_PROVIDER, 1228 providerName) 1229 .put(LauncherSettings.Favorites.RESTORED, 1230 appWidgetInfo.restoreStatus) 1231 .commit(); 1232 } 1233 } 1234 c.checkAndAddItem(appWidgetInfo, sBgDataModel); 1235 } 1236 break; 1237 } 1238 } catch (Exception e) { 1239 Log.e(TAG, "Desktop items loading interrupted", e); 1240 } 1241 } 1242 } finally { 1243 Utilities.closeSilently(c); 1244 } 1245 1246 // Break early if we've stopped loading 1247 if (mStopped) { 1248 sBgDataModel.clear(); 1249 return; 1250 } 1251 1252 // Remove dead items 1253 if (c.commitDeleted()) { 1254 // Remove any empty folder 1255 ArrayList<Long> deletedFolderIds = (ArrayList<Long>) LauncherSettings.Settings 1256 .call(contentResolver, 1257 LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS) 1258 .getSerializable(LauncherSettings.Settings.EXTRA_VALUE); 1259 for (long folderId : deletedFolderIds) { 1260 sBgDataModel.workspaceItems.remove(sBgDataModel.folders.get(folderId)); 1261 sBgDataModel.folders.remove(folderId); 1262 sBgDataModel.itemsIdMap.remove(folderId); 1263 } 1264 1265 // Remove any ghost widgets 1266 LauncherSettings.Settings.call(contentResolver, 1267 LauncherSettings.Settings.METHOD_REMOVE_GHOST_WIDGETS); 1268 } 1269 1270 // Unpin shortcuts that don't exist on the workspace. 1271 HashSet<ShortcutKey> pendingShortcuts = 1272 InstallShortcutReceiver.getPendingShortcuts(context); 1273 for (ShortcutKey key : shortcutKeyToPinnedShortcuts.keySet()) { 1274 MutableInt numTimesPinned = sBgDataModel.pinnedShortcutCounts.get(key); 1275 if ((numTimesPinned == null || numTimesPinned.value == 0) 1276 && !pendingShortcuts.contains(key)) { 1277 // Shortcut is pinned but doesn't exist on the workspace; unpin it. 1278 shortcutManager.unpinShortcut(key); 1279 } 1280 } 1281 1282 // Sort all the folder items and make sure the first 3 items are high resolution. 1283 for (FolderInfo folder : sBgDataModel.folders) { 1284 Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR); 1285 int pos = 0; 1286 for (ShortcutInfo info : folder.contents) { 1287 if (info.usingLowResIcon && 1288 info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { 1289 mIconCache.getTitleAndIcon(info, false); 1290 } 1291 pos ++; 1292 if (pos >= FolderIcon.NUM_ITEMS_IN_PREVIEW) { 1293 break; 1294 } 1295 } 1296 } 1297 1298 c.commitRestoredItems(); 1299 if (!isSdCardReady && !pendingPackages.isEmpty()) { 1300 context.registerReceiver( 1301 new SdCardAvailableReceiver( 1302 LauncherModel.this, mContext, pendingPackages), 1303 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), 1304 null, 1305 sWorker); 1306 } 1307 1308 // Remove any empty screens 1309 ArrayList<Long> unusedScreens = new ArrayList<>(sBgDataModel.workspaceScreens); 1310 for (ItemInfo item: sBgDataModel.itemsIdMap) { 1311 long screenId = item.screenId; 1312 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP && 1313 unusedScreens.contains(screenId)) { 1314 unusedScreens.remove(screenId); 1315 } 1316 } 1317 1318 // If there are any empty screens remove them, and update. 1319 if (unusedScreens.size() != 0) { 1320 sBgDataModel.workspaceScreens.removeAll(unusedScreens); 1321 updateWorkspaceScreenOrder(context, sBgDataModel.workspaceScreens); 1322 } 1323 } 1324 if (LauncherAppState.PROFILE_STARTUP) { 1325 Trace.endSection(); 1326 } 1327 } 1328 1329 /** Filters the set of items who are directly or indirectly (via another container) on the 1330 * specified screen. */ filterCurrentWorkspaceItems(long currentScreenId, ArrayList<ItemInfo> allWorkspaceItems, ArrayList<ItemInfo> currentScreenItems, ArrayList<ItemInfo> otherScreenItems)1331 private void filterCurrentWorkspaceItems(long currentScreenId, 1332 ArrayList<ItemInfo> allWorkspaceItems, 1333 ArrayList<ItemInfo> currentScreenItems, 1334 ArrayList<ItemInfo> otherScreenItems) { 1335 // Purge any null ItemInfos 1336 Iterator<ItemInfo> iter = allWorkspaceItems.iterator(); 1337 while (iter.hasNext()) { 1338 ItemInfo i = iter.next(); 1339 if (i == null) { 1340 iter.remove(); 1341 } 1342 } 1343 1344 // Order the set of items by their containers first, this allows use to walk through the 1345 // list sequentially, build up a list of containers that are in the specified screen, 1346 // as well as all items in those containers. 1347 Set<Long> itemsOnScreen = new HashSet<Long>(); 1348 Collections.sort(allWorkspaceItems, new Comparator<ItemInfo>() { 1349 @Override 1350 public int compare(ItemInfo lhs, ItemInfo rhs) { 1351 return Utilities.longCompare(lhs.container, rhs.container); 1352 } 1353 }); 1354 for (ItemInfo info : allWorkspaceItems) { 1355 if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { 1356 if (info.screenId == currentScreenId) { 1357 currentScreenItems.add(info); 1358 itemsOnScreen.add(info.id); 1359 } else { 1360 otherScreenItems.add(info); 1361 } 1362 } else if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { 1363 currentScreenItems.add(info); 1364 itemsOnScreen.add(info.id); 1365 } else { 1366 if (itemsOnScreen.contains(info.container)) { 1367 currentScreenItems.add(info); 1368 itemsOnScreen.add(info.id); 1369 } else { 1370 otherScreenItems.add(info); 1371 } 1372 } 1373 } 1374 } 1375 1376 /** Filters the set of widgets which are on the specified screen. */ filterCurrentAppWidgets(long currentScreenId, ArrayList<LauncherAppWidgetInfo> appWidgets, ArrayList<LauncherAppWidgetInfo> currentScreenWidgets, ArrayList<LauncherAppWidgetInfo> otherScreenWidgets)1377 private void filterCurrentAppWidgets(long currentScreenId, 1378 ArrayList<LauncherAppWidgetInfo> appWidgets, 1379 ArrayList<LauncherAppWidgetInfo> currentScreenWidgets, 1380 ArrayList<LauncherAppWidgetInfo> otherScreenWidgets) { 1381 1382 for (LauncherAppWidgetInfo widget : appWidgets) { 1383 if (widget == null) continue; 1384 if (widget.container == LauncherSettings.Favorites.CONTAINER_DESKTOP && 1385 widget.screenId == currentScreenId) { 1386 currentScreenWidgets.add(widget); 1387 } else { 1388 otherScreenWidgets.add(widget); 1389 } 1390 } 1391 } 1392 1393 /** Sorts the set of items by hotseat, workspace (spatially from top to bottom, left to 1394 * right) */ sortWorkspaceItemsSpatially(ArrayList<ItemInfo> workspaceItems)1395 private void sortWorkspaceItemsSpatially(ArrayList<ItemInfo> workspaceItems) { 1396 final InvariantDeviceProfile profile = mApp.getInvariantDeviceProfile(); 1397 final int screenCols = profile.numColumns; 1398 final int screenCellCount = profile.numColumns * profile.numRows; 1399 Collections.sort(workspaceItems, new Comparator<ItemInfo>() { 1400 @Override 1401 public int compare(ItemInfo lhs, ItemInfo rhs) { 1402 if (lhs.container == rhs.container) { 1403 // Within containers, order by their spatial position in that container 1404 switch ((int) lhs.container) { 1405 case LauncherSettings.Favorites.CONTAINER_DESKTOP: { 1406 long lr = (lhs.screenId * screenCellCount + 1407 lhs.cellY * screenCols + lhs.cellX); 1408 long rr = (rhs.screenId * screenCellCount + 1409 rhs.cellY * screenCols + rhs.cellX); 1410 return Utilities.longCompare(lr, rr); 1411 } 1412 case LauncherSettings.Favorites.CONTAINER_HOTSEAT: { 1413 // We currently use the screen id as the rank 1414 return Utilities.longCompare(lhs.screenId, rhs.screenId); 1415 } 1416 default: 1417 if (ProviderConfig.IS_DOGFOOD_BUILD) { 1418 throw new RuntimeException("Unexpected container type when " + 1419 "sorting workspace items."); 1420 } 1421 return 0; 1422 } 1423 } else { 1424 // Between containers, order by hotseat, desktop 1425 return Utilities.longCompare(lhs.container, rhs.container); 1426 } 1427 } 1428 }); 1429 } 1430 bindWorkspaceScreens(final Callbacks oldCallbacks, final ArrayList<Long> orderedScreens)1431 private void bindWorkspaceScreens(final Callbacks oldCallbacks, 1432 final ArrayList<Long> orderedScreens) { 1433 final Runnable r = new Runnable() { 1434 @Override 1435 public void run() { 1436 Callbacks callbacks = tryGetCallbacks(oldCallbacks); 1437 if (callbacks != null) { 1438 callbacks.bindScreens(orderedScreens); 1439 } 1440 } 1441 }; 1442 runOnMainThread(r); 1443 } 1444 bindWorkspaceItems(final Callbacks oldCallbacks, final ArrayList<ItemInfo> workspaceItems, final ArrayList<LauncherAppWidgetInfo> appWidgets, final Executor executor)1445 private void bindWorkspaceItems(final Callbacks oldCallbacks, 1446 final ArrayList<ItemInfo> workspaceItems, 1447 final ArrayList<LauncherAppWidgetInfo> appWidgets, 1448 final Executor executor) { 1449 1450 // Bind the workspace items 1451 int N = workspaceItems.size(); 1452 for (int i = 0; i < N; i += ITEMS_CHUNK) { 1453 final int start = i; 1454 final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i); 1455 final Runnable r = new Runnable() { 1456 @Override 1457 public void run() { 1458 Callbacks callbacks = tryGetCallbacks(oldCallbacks); 1459 if (callbacks != null) { 1460 callbacks.bindItems(workspaceItems, start, start+chunkSize, 1461 false); 1462 } 1463 } 1464 }; 1465 executor.execute(r); 1466 } 1467 1468 // Bind the widgets, one at a time 1469 N = appWidgets.size(); 1470 for (int i = 0; i < N; i++) { 1471 final LauncherAppWidgetInfo widget = appWidgets.get(i); 1472 final Runnable r = new Runnable() { 1473 public void run() { 1474 Callbacks callbacks = tryGetCallbacks(oldCallbacks); 1475 if (callbacks != null) { 1476 callbacks.bindAppWidget(widget); 1477 } 1478 } 1479 }; 1480 executor.execute(r); 1481 } 1482 } 1483 1484 /** 1485 * Binds all loaded data to actual views on the main thread. 1486 */ bindWorkspace(int synchronizeBindPage)1487 private void bindWorkspace(int synchronizeBindPage) { 1488 final long t = SystemClock.uptimeMillis(); 1489 Runnable r; 1490 1491 // Don't use these two variables in any of the callback runnables. 1492 // Otherwise we hold a reference to them. 1493 final Callbacks oldCallbacks = mCallbacks.get(); 1494 if (oldCallbacks == null) { 1495 // This launcher has exited and nobody bothered to tell us. Just bail. 1496 Log.w(TAG, "LoaderTask running with no launcher"); 1497 return; 1498 } 1499 1500 // Save a copy of all the bg-thread collections 1501 ArrayList<ItemInfo> workspaceItems = new ArrayList<>(); 1502 ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<>(); 1503 ArrayList<Long> orderedScreenIds = new ArrayList<>(); 1504 1505 synchronized (sBgDataModel) { 1506 workspaceItems.addAll(sBgDataModel.workspaceItems); 1507 appWidgets.addAll(sBgDataModel.appWidgets); 1508 orderedScreenIds.addAll(sBgDataModel.workspaceScreens); 1509 } 1510 1511 final int currentScreen; 1512 { 1513 int currScreen = synchronizeBindPage != PagedView.INVALID_RESTORE_PAGE 1514 ? synchronizeBindPage : oldCallbacks.getCurrentWorkspaceScreen(); 1515 if (currScreen >= orderedScreenIds.size()) { 1516 // There may be no workspace screens (just hotseat items and an empty page). 1517 currScreen = PagedView.INVALID_RESTORE_PAGE; 1518 } 1519 currentScreen = currScreen; 1520 } 1521 final boolean validFirstPage = currentScreen >= 0; 1522 final long currentScreenId = 1523 validFirstPage ? orderedScreenIds.get(currentScreen) : INVALID_SCREEN_ID; 1524 1525 // Separate the items that are on the current screen, and all the other remaining items 1526 ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<>(); 1527 ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<>(); 1528 ArrayList<LauncherAppWidgetInfo> currentAppWidgets = new ArrayList<>(); 1529 ArrayList<LauncherAppWidgetInfo> otherAppWidgets = new ArrayList<>(); 1530 1531 filterCurrentWorkspaceItems(currentScreenId, workspaceItems, currentWorkspaceItems, 1532 otherWorkspaceItems); 1533 filterCurrentAppWidgets(currentScreenId, appWidgets, currentAppWidgets, 1534 otherAppWidgets); 1535 sortWorkspaceItemsSpatially(currentWorkspaceItems); 1536 sortWorkspaceItemsSpatially(otherWorkspaceItems); 1537 1538 // Tell the workspace that we're about to start binding items 1539 r = new Runnable() { 1540 public void run() { 1541 Callbacks callbacks = tryGetCallbacks(oldCallbacks); 1542 if (callbacks != null) { 1543 callbacks.clearPendingBinds(); 1544 callbacks.startBinding(); 1545 } 1546 } 1547 }; 1548 runOnMainThread(r); 1549 1550 bindWorkspaceScreens(oldCallbacks, orderedScreenIds); 1551 1552 Executor mainExecutor = new DeferredMainThreadExecutor(); 1553 // Load items on the current page. 1554 bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets, mainExecutor); 1555 1556 // In case of validFirstPage, only bind the first screen, and defer binding the 1557 // remaining screens after first onDraw (and an optional the fade animation whichever 1558 // happens later). 1559 // This ensures that the first screen is immediately visible (eg. during rotation) 1560 // In case of !validFirstPage, bind all pages one after other. 1561 final Executor deferredExecutor = 1562 validFirstPage ? new ViewOnDrawExecutor(mHandler) : mainExecutor; 1563 1564 mainExecutor.execute(new Runnable() { 1565 @Override 1566 public void run() { 1567 Callbacks callbacks = tryGetCallbacks(oldCallbacks); 1568 if (callbacks != null) { 1569 callbacks.finishFirstPageBind( 1570 validFirstPage ? (ViewOnDrawExecutor) deferredExecutor : null); 1571 } 1572 } 1573 }); 1574 1575 bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, deferredExecutor); 1576 1577 // Tell the workspace that we're done binding items 1578 r = new Runnable() { 1579 public void run() { 1580 Callbacks callbacks = tryGetCallbacks(oldCallbacks); 1581 if (callbacks != null) { 1582 callbacks.finishBindingItems(); 1583 } 1584 1585 mIsLoadingAndBindingWorkspace = false; 1586 1587 // Run all the bind complete runnables after workspace is bound. 1588 if (!mBindCompleteRunnables.isEmpty()) { 1589 synchronized (mBindCompleteRunnables) { 1590 for (final Runnable r : mBindCompleteRunnables) { 1591 runOnWorkerThread(r); 1592 } 1593 mBindCompleteRunnables.clear(); 1594 } 1595 } 1596 1597 // If we're profiling, ensure this is the last thing in the queue. 1598 if (DEBUG_LOADERS) { 1599 Log.d(TAG, "bound workspace in " 1600 + (SystemClock.uptimeMillis()-t) + "ms"); 1601 } 1602 1603 } 1604 }; 1605 deferredExecutor.execute(r); 1606 1607 if (validFirstPage) { 1608 r = new Runnable() { 1609 public void run() { 1610 Callbacks callbacks = tryGetCallbacks(oldCallbacks); 1611 if (callbacks != null) { 1612 // We are loading synchronously, which means, some of the pages will be 1613 // bound after first draw. Inform the callbacks that page binding is 1614 // not complete, and schedule the remaining pages. 1615 if (currentScreen != PagedView.INVALID_RESTORE_PAGE) { 1616 callbacks.onPageBoundSynchronously(currentScreen); 1617 } 1618 callbacks.executeOnNextDraw((ViewOnDrawExecutor) deferredExecutor); 1619 } 1620 } 1621 }; 1622 runOnMainThread(r); 1623 } 1624 } 1625 updateIconCache()1626 private void updateIconCache() { 1627 // Ignore packages which have a promise icon. 1628 HashSet<String> packagesToIgnore = new HashSet<>(); 1629 synchronized (sBgDataModel) { 1630 for (ItemInfo info : sBgDataModel.itemsIdMap) { 1631 if (info instanceof ShortcutInfo) { 1632 ShortcutInfo si = (ShortcutInfo) info; 1633 if (si.isPromise() && si.getTargetComponent() != null) { 1634 packagesToIgnore.add(si.getTargetComponent().getPackageName()); 1635 } 1636 } else if (info instanceof LauncherAppWidgetInfo) { 1637 LauncherAppWidgetInfo lawi = (LauncherAppWidgetInfo) info; 1638 if (lawi.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) { 1639 packagesToIgnore.add(lawi.providerName.getPackageName()); 1640 } 1641 } 1642 } 1643 } 1644 mIconCache.updateDbIcons(packagesToIgnore); 1645 } 1646 onlyBindAllApps()1647 private void onlyBindAllApps() { 1648 final Callbacks oldCallbacks = mCallbacks.get(); 1649 if (oldCallbacks == null) { 1650 // This launcher has exited and nobody bothered to tell us. Just bail. 1651 Log.w(TAG, "LoaderTask running with no launcher (onlyBindAllApps)"); 1652 return; 1653 } 1654 1655 // shallow copy 1656 @SuppressWarnings("unchecked") 1657 final ArrayList<AppInfo> list 1658 = (ArrayList<AppInfo>) mBgAllAppsList.data.clone(); 1659 Runnable r = new Runnable() { 1660 public void run() { 1661 final long t = SystemClock.uptimeMillis(); 1662 final Callbacks callbacks = tryGetCallbacks(oldCallbacks); 1663 if (callbacks != null) { 1664 callbacks.bindAllApplications(list); 1665 } 1666 if (DEBUG_LOADERS) { 1667 Log.d(TAG, "bound all " + list.size() + " apps from cache in " 1668 + (SystemClock.uptimeMillis() - t) + "ms"); 1669 } 1670 } 1671 }; 1672 runOnMainThread(r); 1673 } 1674 scheduleManagedHeuristicRunnable(final ManagedProfileHeuristic heuristic, final UserHandle user, final List<LauncherActivityInfo> apps)1675 private void scheduleManagedHeuristicRunnable(final ManagedProfileHeuristic heuristic, 1676 final UserHandle user, final List<LauncherActivityInfo> apps) { 1677 if (heuristic != null) { 1678 // Assume the app lists now is updated. 1679 mIsManagedHeuristicAppsUpdated = false; 1680 final Runnable managedHeuristicRunnable = new Runnable() { 1681 @Override 1682 public void run() { 1683 if (mIsManagedHeuristicAppsUpdated) { 1684 // If app list is updated, we need to reschedule it otherwise old app 1685 // list will override everything in processUserApps(). 1686 sWorker.post(new Runnable() { 1687 public void run() { 1688 final List<LauncherActivityInfo> updatedApps = 1689 mLauncherApps.getActivityList(null, user); 1690 scheduleManagedHeuristicRunnable(heuristic, user, 1691 updatedApps); 1692 } 1693 }); 1694 } else { 1695 heuristic.processUserApps(apps); 1696 } 1697 } 1698 }; 1699 runOnMainThread(new Runnable() { 1700 @Override 1701 public void run() { 1702 // Check isLoadingWorkspace on the UI thread, as it is updated on the UI 1703 // thread. 1704 if (mIsLoadingAndBindingWorkspace) { 1705 synchronized (mBindCompleteRunnables) { 1706 mBindCompleteRunnables.add(managedHeuristicRunnable); 1707 } 1708 } else { 1709 runOnWorkerThread(managedHeuristicRunnable); 1710 } 1711 } 1712 }); 1713 } 1714 } 1715 loadAllApps()1716 private void loadAllApps() { 1717 final long loadTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; 1718 1719 final Callbacks oldCallbacks = mCallbacks.get(); 1720 if (oldCallbacks == null) { 1721 // This launcher has exited and nobody bothered to tell us. Just bail. 1722 Log.w(TAG, "LoaderTask running with no launcher (loadAllApps)"); 1723 return; 1724 } 1725 1726 final List<UserHandle> profiles = mUserManager.getUserProfiles(); 1727 1728 // Clear the list of apps 1729 mBgAllAppsList.clear(); 1730 for (UserHandle user : profiles) { 1731 // Query for the set of apps 1732 final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; 1733 final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user); 1734 if (DEBUG_LOADERS) { 1735 Log.d(TAG, "getActivityList took " 1736 + (SystemClock.uptimeMillis()-qiaTime) + "ms for user " + user); 1737 Log.d(TAG, "getActivityList got " + apps.size() + " apps for user " + user); 1738 } 1739 // Fail if we don't have any apps 1740 // TODO: Fix this. Only fail for the current user. 1741 if (apps == null || apps.isEmpty()) { 1742 return; 1743 } 1744 boolean quietMode = mUserManager.isQuietModeEnabled(user); 1745 // Create the ApplicationInfos 1746 for (int i = 0; i < apps.size(); i++) { 1747 LauncherActivityInfo app = apps.get(i); 1748 // This builds the icon bitmaps. 1749 mBgAllAppsList.add(new AppInfo(app, user, quietMode), app); 1750 } 1751 final ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(mContext, user); 1752 if (heuristic != null) { 1753 scheduleManagedHeuristicRunnable(heuristic, user, apps); 1754 } 1755 } 1756 // Huh? Shouldn't this be inside the Runnable below? 1757 final ArrayList<AppInfo> added = mBgAllAppsList.added; 1758 mBgAllAppsList.added = new ArrayList<AppInfo>(); 1759 1760 // Post callback on main thread 1761 mHandler.post(new Runnable() { 1762 public void run() { 1763 1764 final long bindTime = SystemClock.uptimeMillis(); 1765 final Callbacks callbacks = tryGetCallbacks(oldCallbacks); 1766 if (callbacks != null) { 1767 callbacks.bindAllApplications(added); 1768 if (DEBUG_LOADERS) { 1769 Log.d(TAG, "bound " + added.size() + " apps in " 1770 + (SystemClock.uptimeMillis() - bindTime) + "ms"); 1771 } 1772 } else { 1773 Log.i(TAG, "not binding apps: no Launcher activity"); 1774 } 1775 } 1776 }); 1777 // Cleanup any data stored for a deleted user. 1778 ManagedProfileHeuristic.processAllUsers(profiles, mContext); 1779 if (DEBUG_LOADERS) { 1780 Log.d(TAG, "Icons processed in " 1781 + (SystemClock.uptimeMillis() - loadTime) + "ms"); 1782 } 1783 } 1784 loadDeepShortcuts()1785 private void loadDeepShortcuts() { 1786 sBgDataModel.deepShortcutMap.clear(); 1787 DeepShortcutManager shortcutManager = DeepShortcutManager.getInstance(mContext); 1788 mHasShortcutHostPermission = shortcutManager.hasHostPermission(); 1789 if (mHasShortcutHostPermission) { 1790 for (UserHandle user : mUserManager.getUserProfiles()) { 1791 if (mUserManager.isUserUnlocked(user)) { 1792 List<ShortcutInfoCompat> shortcuts = 1793 shortcutManager.queryForAllShortcuts(user); 1794 sBgDataModel.updateDeepShortcutMap(null, user, shortcuts); 1795 } 1796 } 1797 } 1798 } 1799 } 1800 bindDeepShortcuts()1801 public void bindDeepShortcuts() { 1802 final MultiHashMap<ComponentKey, String> shortcutMapCopy = 1803 sBgDataModel.deepShortcutMap.clone(); 1804 Runnable r = new Runnable() { 1805 @Override 1806 public void run() { 1807 Callbacks callbacks = getCallback(); 1808 if (callbacks != null) { 1809 callbacks.bindDeepShortcutMap(shortcutMapCopy); 1810 } 1811 } 1812 }; 1813 runOnMainThread(r); 1814 } 1815 1816 /** 1817 * Refreshes the cached shortcuts if the shortcut permission has changed. 1818 * Current implementation simply reloads the workspace, but it can be optimized to 1819 * use partial updates similar to {@link UserManagerCompat} 1820 */ refreshShortcutsIfRequired()1821 public void refreshShortcutsIfRequired() { 1822 if (Utilities.ATLEAST_NOUGAT_MR1) { 1823 sWorker.removeCallbacks(mShortcutPermissionCheckRunnable); 1824 sWorker.post(mShortcutPermissionCheckRunnable); 1825 } 1826 } 1827 1828 /** 1829 * Called when the icons for packages have been updated in the icon cache. 1830 */ onPackageIconsUpdated(HashSet<String> updatedPackages, UserHandle user)1831 public void onPackageIconsUpdated(HashSet<String> updatedPackages, UserHandle user) { 1832 // If any package icon has changed (app was updated while launcher was dead), 1833 // update the corresponding shortcuts. 1834 enqueueModelUpdateTask(new CacheDataUpdatedTask( 1835 CacheDataUpdatedTask.OP_CACHE_UPDATE, user, updatedPackages)); 1836 } 1837 enqueueModelUpdateTask(BaseModelUpdateTask task)1838 void enqueueModelUpdateTask(BaseModelUpdateTask task) { 1839 if (!mModelLoaded && mLoaderTask == null) { 1840 if (DEBUG_LOADERS) { 1841 Log.d(TAG, "enqueueModelUpdateTask Ignoring task since loader is pending=" + task); 1842 } 1843 return; 1844 } 1845 task.init(this); 1846 runOnWorkerThread(task); 1847 } 1848 1849 /** 1850 * A task to be executed on the current callbacks on the UI thread. 1851 * If there is no current callbacks, the task is ignored. 1852 */ 1853 public interface CallbackTask { 1854 execute(Callbacks callbacks)1855 void execute(Callbacks callbacks); 1856 } 1857 1858 /** 1859 * A runnable which changes/updates the data model of the launcher based on certain events. 1860 */ 1861 public static abstract class BaseModelUpdateTask implements Runnable { 1862 1863 private LauncherModel mModel; 1864 private DeferredHandler mUiHandler; 1865 1866 /* package private */ init(LauncherModel model)1867 void init(LauncherModel model) { 1868 mModel = model; 1869 mUiHandler = mModel.mHandler; 1870 } 1871 1872 @Override run()1873 public void run() { 1874 if (!mModel.mHasLoaderCompletedOnce) { 1875 // Loader has not yet run. 1876 return; 1877 } 1878 execute(mModel.mApp, sBgDataModel, mModel.mBgAllAppsList); 1879 } 1880 1881 /** 1882 * Execute the actual task. Called on the worker thread. 1883 */ execute( LauncherAppState app, BgDataModel dataModel, AllAppsList apps)1884 public abstract void execute( 1885 LauncherAppState app, BgDataModel dataModel, AllAppsList apps); 1886 1887 /** 1888 * Schedules a {@param task} to be executed on the current callbacks. 1889 */ scheduleCallbackTask(final CallbackTask task)1890 public final void scheduleCallbackTask(final CallbackTask task) { 1891 final Callbacks callbacks = mModel.getCallback(); 1892 mUiHandler.post(new Runnable() { 1893 public void run() { 1894 Callbacks cb = mModel.getCallback(); 1895 if (callbacks == cb && cb != null) { 1896 task.execute(callbacks); 1897 } 1898 } 1899 }); 1900 } 1901 getModelWriter()1902 public ModelWriter getModelWriter() { 1903 // Updates from model task, do not deal with icon position in hotseat. 1904 return mModel.getWriter(false /* hasVerticalHotseat */); 1905 } 1906 } 1907 updateAndBindShortcutInfo(final ShortcutInfo si, final ShortcutInfoCompat info)1908 public void updateAndBindShortcutInfo(final ShortcutInfo si, final ShortcutInfoCompat info) { 1909 updateAndBindShortcutInfo(new Provider<ShortcutInfo>() { 1910 @Override 1911 public ShortcutInfo get() { 1912 si.updateFromDeepShortcutInfo(info, mApp.getContext()); 1913 si.iconBitmap = LauncherIcons.createShortcutIcon(info, mApp.getContext()); 1914 return si; 1915 } 1916 }); 1917 } 1918 1919 /** 1920 * Utility method to update a shortcut on the background thread. 1921 */ updateAndBindShortcutInfo(final Provider<ShortcutInfo> shortcutProvider)1922 public void updateAndBindShortcutInfo(final Provider<ShortcutInfo> shortcutProvider) { 1923 enqueueModelUpdateTask(new ExtendedModelTask() { 1924 @Override 1925 public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) { 1926 ShortcutInfo info = shortcutProvider.get(); 1927 ArrayList<ShortcutInfo> update = new ArrayList<>(); 1928 update.add(info); 1929 bindUpdatedShortcuts(update, info.user); 1930 } 1931 }); 1932 } 1933 bindWidgetsModel(final Callbacks callbacks)1934 private void bindWidgetsModel(final Callbacks callbacks) { 1935 final MultiHashMap<PackageItemInfo, WidgetItem> widgets 1936 = mBgWidgetsModel.getWidgetsMap().clone(); 1937 mHandler.post(new Runnable() { 1938 @Override 1939 public void run() { 1940 Callbacks cb = getCallback(); 1941 if (callbacks == cb && cb != null) { 1942 callbacks.bindAllWidgets(widgets); 1943 } 1944 } 1945 }); 1946 } 1947 refreshAndBindWidgetsAndShortcuts(final Callbacks callbacks, final boolean bindFirst, @Nullable final PackageUserKey packageUser)1948 public void refreshAndBindWidgetsAndShortcuts(final Callbacks callbacks, 1949 final boolean bindFirst, @Nullable final PackageUserKey packageUser) { 1950 runOnWorkerThread(new Runnable() { 1951 @Override 1952 public void run() { 1953 if (bindFirst && !mBgWidgetsModel.isEmpty()) { 1954 bindWidgetsModel(callbacks); 1955 } 1956 ArrayList<WidgetItem> widgets = mBgWidgetsModel.update( 1957 mApp.getContext(), packageUser); 1958 bindWidgetsModel(callbacks); 1959 1960 // update the Widget entries inside DB on the worker thread. 1961 mApp.getWidgetCache().removeObsoletePreviews(widgets, packageUser); 1962 } 1963 }); 1964 } 1965 isValidProvider(AppWidgetProviderInfo provider)1966 static boolean isValidProvider(AppWidgetProviderInfo provider) { 1967 return (provider != null) && (provider.provider != null) 1968 && (provider.provider.getPackageName() != null); 1969 } 1970 dumpState(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)1971 public void dumpState(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 1972 if (args.length > 0 && TextUtils.equals(args[0], "--all")) { 1973 writer.println(prefix + "All apps list: size=" + mBgAllAppsList.data.size()); 1974 for (AppInfo info : mBgAllAppsList.data) { 1975 writer.println(prefix + " title=\"" + info.title + "\" iconBitmap=" + info.iconBitmap 1976 + " componentName=" + info.componentName.getPackageName()); 1977 } 1978 } 1979 sBgDataModel.dump(prefix, fd, writer, args); 1980 } 1981 getCallback()1982 public Callbacks getCallback() { 1983 return mCallbacks != null ? mCallbacks.get() : null; 1984 } 1985 1986 /** 1987 * @return {@link FolderInfo} if its already loaded. 1988 */ findFolderById(Long folderId)1989 public FolderInfo findFolderById(Long folderId) { 1990 synchronized (sBgDataModel) { 1991 return sBgDataModel.folders.get(folderId); 1992 } 1993 } 1994 1995 @Thunk class DeferredMainThreadExecutor implements Executor { 1996 1997 @Override execute(Runnable command)1998 public void execute(Runnable command) { 1999 runOnMainThread(command); 2000 } 2001 } 2002 2003 /** 2004 * @return the looper for the worker thread which can be used to start background tasks. 2005 */ getWorkerLooper()2006 public static Looper getWorkerLooper() { 2007 return sWorkerThread.getLooper(); 2008 } 2009 } 2010