• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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