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 static com.android.launcher3.LauncherAppState.ACTION_FORCE_ROLOAD; 20 import static com.android.launcher3.config.FeatureFlags.IS_DOGFOOD_BUILD; 21 22 import android.content.BroadcastReceiver; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.pm.ShortcutInfo; 26 import android.os.Handler; 27 import android.os.HandlerThread; 28 import android.os.Looper; 29 import android.os.Process; 30 import android.os.UserHandle; 31 import android.text.TextUtils; 32 import android.util.Log; 33 import android.util.Pair; 34 35 import com.android.launcher3.compat.LauncherAppsCompat; 36 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; 37 import com.android.launcher3.compat.UserManagerCompat; 38 import com.android.launcher3.icons.IconCache; 39 import com.android.launcher3.icons.LauncherIcons; 40 import com.android.launcher3.model.AddWorkspaceItemsTask; 41 import com.android.launcher3.model.BaseModelUpdateTask; 42 import com.android.launcher3.model.BgDataModel; 43 import com.android.launcher3.model.CacheDataUpdatedTask; 44 import com.android.launcher3.model.LoaderResults; 45 import com.android.launcher3.model.LoaderTask; 46 import com.android.launcher3.model.ModelWriter; 47 import com.android.launcher3.model.PackageInstallStateChangedTask; 48 import com.android.launcher3.model.PackageUpdatedTask; 49 import com.android.launcher3.model.ShortcutsChangedTask; 50 import com.android.launcher3.model.UserLockStateChangedTask; 51 import com.android.launcher3.shortcuts.DeepShortcutManager; 52 import com.android.launcher3.util.ComponentKey; 53 import com.android.launcher3.util.IntArray; 54 import com.android.launcher3.util.ItemInfoMatcher; 55 import com.android.launcher3.util.PackageUserKey; 56 import com.android.launcher3.util.Preconditions; 57 import com.android.launcher3.util.Thunk; 58 import com.android.launcher3.util.ViewOnDrawExecutor; 59 import com.android.launcher3.widget.WidgetListRowEntry; 60 61 import java.io.FileDescriptor; 62 import java.io.PrintWriter; 63 import java.lang.ref.WeakReference; 64 import java.util.ArrayList; 65 import java.util.HashMap; 66 import java.util.HashSet; 67 import java.util.List; 68 import java.util.concurrent.CancellationException; 69 import java.util.concurrent.Executor; 70 import java.util.function.Supplier; 71 72 import androidx.annotation.Nullable; 73 74 /** 75 * Maintains in-memory state of the Launcher. It is expected that there should be only one 76 * LauncherModel object held in a static. Also provide APIs for updating the database state 77 * for the Launcher. 78 */ 79 public class LauncherModel extends BroadcastReceiver 80 implements LauncherAppsCompat.OnAppsChangedCallbackCompat { 81 private static final boolean DEBUG_RECEIVER = false; 82 83 static final String TAG = "Launcher.Model"; 84 85 private final MainThreadExecutor mUiExecutor = new MainThreadExecutor(); 86 @Thunk final LauncherAppState mApp; 87 @Thunk final Object mLock = new Object(); 88 @Thunk 89 LoaderTask mLoaderTask; 90 @Thunk boolean mIsLoaderTaskRunning; 91 92 @Thunk static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader"); 93 private static final Looper mWorkerLooper; 94 static { sWorkerThread.start()95 sWorkerThread.start(); 96 mWorkerLooper = sWorkerThread.getLooper(); 97 } 98 @Thunk static final Handler sWorker = new Handler(mWorkerLooper); 99 100 // Indicates whether the current model data is valid or not. 101 // We start off with everything not loaded. After that, we assume that 102 // our monitoring of the package manager provides all updates and we never 103 // need to do a requery. This is only ever touched from the loader thread. 104 private boolean mModelLoaded; isModelLoaded()105 public boolean isModelLoaded() { 106 synchronized (mLock) { 107 return mModelLoaded && mLoaderTask == null; 108 } 109 } 110 111 @Thunk WeakReference<Callbacks> mCallbacks; 112 113 // < only access in worker thread > 114 private final AllAppsList mBgAllAppsList; 115 116 /** 117 * All the static data should be accessed on the background thread, A lock should be acquired 118 * on this object when accessing any data from this model. 119 */ 120 static final BgDataModel sBgDataModel = new BgDataModel(); 121 122 // Runnable to check if the shortcuts permission has changed. 123 private final Runnable mShortcutPermissionCheckRunnable = new Runnable() { 124 @Override 125 public void run() { 126 if (mModelLoaded) { 127 boolean hasShortcutHostPermission = 128 DeepShortcutManager.getInstance(mApp.getContext()).hasHostPermission(); 129 if (hasShortcutHostPermission != sBgDataModel.hasShortcutHostPermission) { 130 forceReload(); 131 } 132 } 133 } 134 }; 135 136 public interface Callbacks { rebindModel()137 public void rebindModel(); 138 getCurrentWorkspaceScreen()139 public int getCurrentWorkspaceScreen(); clearPendingBinds()140 public void clearPendingBinds(); startBinding()141 public void startBinding(); bindItems(List<ItemInfo> shortcuts, boolean forceAnimateIcons)142 public void bindItems(List<ItemInfo> shortcuts, boolean forceAnimateIcons); bindScreens(IntArray orderedScreenIds)143 public void bindScreens(IntArray orderedScreenIds); finishFirstPageBind(ViewOnDrawExecutor executor)144 public void finishFirstPageBind(ViewOnDrawExecutor executor); finishBindingItems(int pageBoundFirst)145 public void finishBindingItems(int pageBoundFirst); bindAllApplications(ArrayList<AppInfo> apps)146 public void bindAllApplications(ArrayList<AppInfo> apps); bindAppsAddedOrUpdated(ArrayList<AppInfo> apps)147 public void bindAppsAddedOrUpdated(ArrayList<AppInfo> apps); preAddApps()148 public void preAddApps(); bindAppsAdded(IntArray newScreens, ArrayList<ItemInfo> addNotAnimated, ArrayList<ItemInfo> addAnimated)149 public void bindAppsAdded(IntArray newScreens, 150 ArrayList<ItemInfo> addNotAnimated, ArrayList<ItemInfo> addAnimated); bindPromiseAppProgressUpdated(PromiseAppInfo app)151 public void bindPromiseAppProgressUpdated(PromiseAppInfo app); bindWorkspaceItemsChanged(ArrayList<WorkspaceItemInfo> updated)152 public void bindWorkspaceItemsChanged(ArrayList<WorkspaceItemInfo> updated); bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets)153 public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets); bindRestoreItemsChange(HashSet<ItemInfo> updates)154 public void bindRestoreItemsChange(HashSet<ItemInfo> updates); bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher)155 public void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher); bindAppInfosRemoved(ArrayList<AppInfo> appInfos)156 public void bindAppInfosRemoved(ArrayList<AppInfo> appInfos); bindAllWidgets(ArrayList<WidgetListRowEntry> widgets)157 public void bindAllWidgets(ArrayList<WidgetListRowEntry> widgets); onPageBoundSynchronously(int page)158 public void onPageBoundSynchronously(int page); executeOnNextDraw(ViewOnDrawExecutor executor)159 public void executeOnNextDraw(ViewOnDrawExecutor executor); bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMap)160 public void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMap); 161 } 162 LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter)163 LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) { 164 mApp = app; 165 mBgAllAppsList = new AllAppsList(iconCache, appFilter); 166 } 167 setPackageState(PackageInstallInfo installInfo)168 public void setPackageState(PackageInstallInfo installInfo) { 169 enqueueModelUpdateTask(new PackageInstallStateChangedTask(installInfo)); 170 } 171 172 /** 173 * Updates the icons and label of all pending icons for the provided package name. 174 */ updateSessionDisplayInfo(final String packageName)175 public void updateSessionDisplayInfo(final String packageName) { 176 HashSet<String> packages = new HashSet<>(); 177 packages.add(packageName); 178 enqueueModelUpdateTask(new CacheDataUpdatedTask( 179 CacheDataUpdatedTask.OP_SESSION_UPDATE, Process.myUserHandle(), packages)); 180 } 181 182 /** 183 * Adds the provided items to the workspace. 184 */ addAndBindAddedWorkspaceItems(List<Pair<ItemInfo, Object>> itemList)185 public void addAndBindAddedWorkspaceItems(List<Pair<ItemInfo, Object>> itemList) { 186 Callbacks callbacks = getCallback(); 187 if (callbacks != null) { 188 callbacks.preAddApps(); 189 } 190 enqueueModelUpdateTask(new AddWorkspaceItemsTask(itemList)); 191 } 192 getWriter(boolean hasVerticalHotseat, boolean verifyChanges)193 public ModelWriter getWriter(boolean hasVerticalHotseat, boolean verifyChanges) { 194 return new ModelWriter(mApp.getContext(), this, sBgDataModel, 195 hasVerticalHotseat, verifyChanges); 196 } 197 198 /** 199 * Set this as the current Launcher activity object for the loader. 200 */ initialize(Callbacks callbacks)201 public void initialize(Callbacks callbacks) { 202 synchronized (mLock) { 203 Preconditions.assertUIThread(); 204 mCallbacks = new WeakReference<>(callbacks); 205 } 206 } 207 208 @Override onPackageChanged(String packageName, UserHandle user)209 public void onPackageChanged(String packageName, UserHandle user) { 210 int op = PackageUpdatedTask.OP_UPDATE; 211 enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packageName)); 212 } 213 214 @Override onPackageRemoved(String packageName, UserHandle user)215 public void onPackageRemoved(String packageName, UserHandle user) { 216 onPackagesRemoved(user, packageName); 217 } 218 onPackagesRemoved(UserHandle user, String... packages)219 public void onPackagesRemoved(UserHandle user, String... packages) { 220 int op = PackageUpdatedTask.OP_REMOVE; 221 enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packages)); 222 } 223 224 @Override onPackageAdded(String packageName, UserHandle user)225 public void onPackageAdded(String packageName, UserHandle user) { 226 int op = PackageUpdatedTask.OP_ADD; 227 enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packageName)); 228 } 229 230 @Override onPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing)231 public void onPackagesAvailable(String[] packageNames, UserHandle user, 232 boolean replacing) { 233 enqueueModelUpdateTask( 234 new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE, user, packageNames)); 235 } 236 237 @Override onPackagesUnavailable(String[] packageNames, UserHandle user, boolean replacing)238 public void onPackagesUnavailable(String[] packageNames, UserHandle user, 239 boolean replacing) { 240 if (!replacing) { 241 enqueueModelUpdateTask(new PackageUpdatedTask( 242 PackageUpdatedTask.OP_UNAVAILABLE, user, packageNames)); 243 } 244 } 245 246 @Override onPackagesSuspended(String[] packageNames, UserHandle user)247 public void onPackagesSuspended(String[] packageNames, UserHandle user) { 248 enqueueModelUpdateTask(new PackageUpdatedTask( 249 PackageUpdatedTask.OP_SUSPEND, user, packageNames)); 250 } 251 252 @Override onPackagesUnsuspended(String[] packageNames, UserHandle user)253 public void onPackagesUnsuspended(String[] packageNames, UserHandle user) { 254 enqueueModelUpdateTask(new PackageUpdatedTask( 255 PackageUpdatedTask.OP_UNSUSPEND, user, packageNames)); 256 } 257 258 @Override onShortcutsChanged(String packageName, List<ShortcutInfo> shortcuts, UserHandle user)259 public void onShortcutsChanged(String packageName, List<ShortcutInfo> shortcuts, 260 UserHandle user) { 261 enqueueModelUpdateTask(new ShortcutsChangedTask(packageName, shortcuts, user, true)); 262 } 263 updatePinnedShortcuts(String packageName, List<ShortcutInfo> shortcuts, UserHandle user)264 public void updatePinnedShortcuts(String packageName, List<ShortcutInfo> shortcuts, 265 UserHandle user) { 266 enqueueModelUpdateTask(new ShortcutsChangedTask(packageName, shortcuts, user, false)); 267 } 268 269 /** 270 * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and 271 * ACTION_PACKAGE_CHANGED. 272 */ 273 @Override onReceive(Context context, Intent intent)274 public void onReceive(Context context, Intent intent) { 275 if (DEBUG_RECEIVER) Log.d(TAG, "onReceive intent=" + intent); 276 277 final String action = intent.getAction(); 278 if (Intent.ACTION_LOCALE_CHANGED.equals(action)) { 279 // If we have changed locale we need to clear out the labels in all apps/workspace. 280 forceReload(); 281 } else if (Intent.ACTION_MANAGED_PROFILE_ADDED.equals(action) 282 || Intent.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) { 283 UserManagerCompat.getInstance(context).enableAndResetCache(); 284 forceReload(); 285 } else if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) || 286 Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action) || 287 Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) { 288 UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER); 289 if (user != null) { 290 if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) || 291 Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) { 292 enqueueModelUpdateTask(new PackageUpdatedTask( 293 PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE, user)); 294 } 295 296 // ACTION_MANAGED_PROFILE_UNAVAILABLE sends the profile back to locked mode, so 297 // we need to run the state change task again. 298 if (Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action) || 299 Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) { 300 enqueueModelUpdateTask(new UserLockStateChangedTask(user)); 301 } 302 } 303 } else if (IS_DOGFOOD_BUILD && ACTION_FORCE_ROLOAD.equals(action)) { 304 Launcher l = (Launcher) getCallback(); 305 l.reload(); 306 } 307 } 308 forceReload()309 public void forceReload() { 310 forceReload(-1); 311 } 312 313 /** 314 * Reloads the workspace items from the DB and re-binds the workspace. This should generally 315 * not be called as DB updates are automatically followed by UI update 316 * @param synchronousBindPage The page to bind first. Can pass -1 to use the current page. 317 */ forceReload(int synchronousBindPage)318 public void forceReload(int synchronousBindPage) { 319 synchronized (mLock) { 320 // Stop any existing loaders first, so they don't set mModelLoaded to true later 321 stopLoader(); 322 mModelLoaded = false; 323 } 324 325 // Start the loader if launcher is already running, otherwise the loader will run, 326 // the next time launcher starts 327 Callbacks callbacks = getCallback(); 328 if (callbacks != null) { 329 if (synchronousBindPage < 0) { 330 synchronousBindPage = callbacks.getCurrentWorkspaceScreen(); 331 } 332 startLoader(synchronousBindPage); 333 } 334 } 335 isCurrentCallbacks(Callbacks callbacks)336 public boolean isCurrentCallbacks(Callbacks callbacks) { 337 return (mCallbacks != null && mCallbacks.get() == callbacks); 338 } 339 340 /** 341 * Starts the loader. Tries to bind {@params synchronousBindPage} synchronously if possible. 342 * @return true if the page could be bound synchronously. 343 */ startLoader(int synchronousBindPage)344 public boolean startLoader(int synchronousBindPage) { 345 // Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems 346 InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_LOADER_RUNNING); 347 synchronized (mLock) { 348 // Don't bother to start the thread if we know it's not going to do anything 349 if (mCallbacks != null && mCallbacks.get() != null) { 350 final Callbacks oldCallbacks = mCallbacks.get(); 351 // Clear any pending bind-runnables from the synchronized load process. 352 mUiExecutor.execute(oldCallbacks::clearPendingBinds); 353 354 // If there is already one running, tell it to stop. 355 stopLoader(); 356 LoaderResults loaderResults = new LoaderResults(mApp, sBgDataModel, 357 mBgAllAppsList, synchronousBindPage, mCallbacks); 358 if (mModelLoaded && !mIsLoaderTaskRunning) { 359 // Divide the set of loaded items into those that we are binding synchronously, 360 // and everything else that is to be bound normally (asynchronously). 361 loaderResults.bindWorkspace(); 362 // For now, continue posting the binding of AllApps as there are other 363 // issues that arise from that. 364 loaderResults.bindAllApps(); 365 loaderResults.bindDeepShortcuts(); 366 loaderResults.bindWidgets(); 367 return true; 368 } else { 369 startLoaderForResults(loaderResults); 370 } 371 } 372 } 373 return false; 374 } 375 376 /** 377 * If there is already a loader task running, tell it to stop. 378 */ stopLoader()379 public void stopLoader() { 380 synchronized (mLock) { 381 LoaderTask oldTask = mLoaderTask; 382 mLoaderTask = null; 383 if (oldTask != null) { 384 oldTask.stopLocked(); 385 } 386 } 387 } 388 startLoaderForResults(LoaderResults results)389 public void startLoaderForResults(LoaderResults results) { 390 synchronized (mLock) { 391 stopLoader(); 392 mLoaderTask = new LoaderTask(mApp, mBgAllAppsList, sBgDataModel, results); 393 394 // Always post the loader task, instead of running directly (even on same thread) so 395 // that we exit any nested synchronized blocks 396 sWorker.post(mLoaderTask); 397 } 398 } 399 startLoaderForResultsIfNotLoaded(LoaderResults results)400 public void startLoaderForResultsIfNotLoaded(LoaderResults results) { 401 synchronized (mLock) { 402 if (!isModelLoaded()) { 403 Log.d(TAG, "Workspace not loaded, loading now"); 404 startLoaderForResults(results); 405 } 406 } 407 } 408 onInstallSessionCreated(final PackageInstallInfo sessionInfo)409 public void onInstallSessionCreated(final PackageInstallInfo sessionInfo) { 410 enqueueModelUpdateTask(new BaseModelUpdateTask() { 411 @Override 412 public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) { 413 apps.addPromiseApp(app.getContext(), sessionInfo); 414 if (!apps.added.isEmpty()) { 415 final ArrayList<AppInfo> arrayList = new ArrayList<>(apps.added); 416 apps.added.clear(); 417 scheduleCallbackTask(new CallbackTask() { 418 @Override 419 public void execute(Callbacks callbacks) { 420 callbacks.bindAppsAddedOrUpdated(arrayList); 421 } 422 }); 423 } 424 } 425 }); 426 } 427 428 public class LoaderTransaction implements AutoCloseable { 429 430 private final LoaderTask mTask; 431 LoaderTransaction(LoaderTask task)432 private LoaderTransaction(LoaderTask task) throws CancellationException { 433 synchronized (mLock) { 434 if (mLoaderTask != task) { 435 throw new CancellationException("Loader already stopped"); 436 } 437 mTask = task; 438 mIsLoaderTaskRunning = true; 439 mModelLoaded = false; 440 } 441 } 442 commit()443 public void commit() { 444 synchronized (mLock) { 445 // Everything loaded bind the data. 446 mModelLoaded = true; 447 } 448 } 449 450 @Override close()451 public void close() { 452 synchronized (mLock) { 453 // If we are still the last one to be scheduled, remove ourselves. 454 if (mLoaderTask == mTask) { 455 mLoaderTask = null; 456 } 457 mIsLoaderTaskRunning = false; 458 } 459 } 460 } 461 beginLoader(LoaderTask task)462 public LoaderTransaction beginLoader(LoaderTask task) throws CancellationException { 463 return new LoaderTransaction(task); 464 } 465 466 /** 467 * Refreshes the cached shortcuts if the shortcut permission has changed. 468 * Current implementation simply reloads the workspace, but it can be optimized to 469 * use partial updates similar to {@link UserManagerCompat} 470 */ refreshShortcutsIfRequired()471 public void refreshShortcutsIfRequired() { 472 sWorker.removeCallbacks(mShortcutPermissionCheckRunnable); 473 sWorker.post(mShortcutPermissionCheckRunnable); 474 } 475 476 /** 477 * Called when the icons for packages have been updated in the icon cache. 478 */ onPackageIconsUpdated(HashSet<String> updatedPackages, UserHandle user)479 public void onPackageIconsUpdated(HashSet<String> updatedPackages, UserHandle user) { 480 // If any package icon has changed (app was updated while launcher was dead), 481 // update the corresponding shortcuts. 482 enqueueModelUpdateTask(new CacheDataUpdatedTask( 483 CacheDataUpdatedTask.OP_CACHE_UPDATE, user, updatedPackages)); 484 } 485 486 /** 487 * Called when the labels for the widgets has updated in the icon cache. 488 */ onWidgetLabelsUpdated(HashSet<String> updatedPackages, UserHandle user)489 public void onWidgetLabelsUpdated(HashSet<String> updatedPackages, UserHandle user) { 490 enqueueModelUpdateTask(new BaseModelUpdateTask() { 491 @Override 492 public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) { 493 dataModel.widgetsModel.onPackageIconsUpdated(updatedPackages, user, app); 494 bindUpdatedWidgets(dataModel); 495 } 496 }); 497 } 498 enqueueModelUpdateTask(ModelUpdateTask task)499 public void enqueueModelUpdateTask(ModelUpdateTask task) { 500 task.init(mApp, this, sBgDataModel, mBgAllAppsList, mUiExecutor); 501 502 if (sWorkerThread.getThreadId() == Process.myTid()) { 503 task.run(); 504 } else { 505 // If we are not on the worker thread, then post to the worker handler 506 sWorker.post(task); 507 } 508 } 509 510 /** 511 * A task to be executed on the current callbacks on the UI thread. 512 * If there is no current callbacks, the task is ignored. 513 */ 514 public interface CallbackTask { 515 execute(Callbacks callbacks)516 void execute(Callbacks callbacks); 517 } 518 519 /** 520 * A runnable which changes/updates the data model of the launcher based on certain events. 521 */ 522 public interface ModelUpdateTask extends Runnable { 523 524 /** 525 * Called before the task is posted to initialize the internal state. 526 */ init(LauncherAppState app, LauncherModel model, BgDataModel dataModel, AllAppsList allAppsList, Executor uiExecutor)527 void init(LauncherAppState app, LauncherModel model, 528 BgDataModel dataModel, AllAppsList allAppsList, Executor uiExecutor); 529 530 } 531 updateAndBindWorkspaceItem(WorkspaceItemInfo si, ShortcutInfo info)532 public void updateAndBindWorkspaceItem(WorkspaceItemInfo si, ShortcutInfo info) { 533 updateAndBindWorkspaceItem(() -> { 534 si.updateFromDeepShortcutInfo(info, mApp.getContext()); 535 LauncherIcons li = LauncherIcons.obtain(mApp.getContext()); 536 si.applyFrom(li.createShortcutIcon(info)); 537 li.recycle(); 538 return si; 539 }); 540 } 541 542 /** 543 * Utility method to update a shortcut on the background thread. 544 */ updateAndBindWorkspaceItem(final Supplier<WorkspaceItemInfo> itemProvider)545 public void updateAndBindWorkspaceItem(final Supplier<WorkspaceItemInfo> itemProvider) { 546 enqueueModelUpdateTask(new BaseModelUpdateTask() { 547 @Override 548 public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) { 549 WorkspaceItemInfo info = itemProvider.get(); 550 getModelWriter().updateItemInDatabase(info); 551 ArrayList<WorkspaceItemInfo> update = new ArrayList<>(); 552 update.add(info); 553 bindUpdatedWorkspaceItems(update); 554 } 555 }); 556 } 557 refreshAndBindWidgetsAndShortcuts(@ullable final PackageUserKey packageUser)558 public void refreshAndBindWidgetsAndShortcuts(@Nullable final PackageUserKey packageUser) { 559 enqueueModelUpdateTask(new BaseModelUpdateTask() { 560 @Override 561 public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) { 562 dataModel.widgetsModel.update(app, packageUser); 563 bindUpdatedWidgets(dataModel); 564 } 565 }); 566 } 567 dumpState(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)568 public void dumpState(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 569 if (args.length > 0 && TextUtils.equals(args[0], "--all")) { 570 writer.println(prefix + "All apps list: size=" + mBgAllAppsList.data.size()); 571 for (AppInfo info : mBgAllAppsList.data) { 572 writer.println(prefix + " title=\"" + info.title + "\" iconBitmap=" + info.iconBitmap 573 + " componentName=" + info.componentName.getPackageName()); 574 } 575 } 576 sBgDataModel.dump(prefix, fd, writer, args); 577 } 578 getCallback()579 public Callbacks getCallback() { 580 return mCallbacks != null ? mCallbacks.get() : null; 581 } 582 583 /** 584 * @return the looper for the worker thread which can be used to start background tasks. 585 */ getWorkerLooper()586 public static Looper getWorkerLooper() { 587 return mWorkerLooper; 588 } 589 setWorkerPriority(final int priority)590 public static void setWorkerPriority(final int priority) { 591 Process.setThreadPriority(sWorkerThread.getThreadId(), priority); 592 } 593 } 594