• 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_STUDIO_BUILD;
21 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
22 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
23 
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.pm.LauncherApps;
27 import android.content.pm.PackageInstaller;
28 import android.content.pm.ShortcutInfo;
29 import android.os.UserHandle;
30 import android.text.TextUtils;
31 import android.util.Log;
32 import android.util.Pair;
33 
34 import androidx.annotation.Nullable;
35 import androidx.annotation.WorkerThread;
36 
37 import com.android.launcher3.config.FeatureFlags;
38 import com.android.launcher3.icons.IconCache;
39 import com.android.launcher3.logging.FileLog;
40 import com.android.launcher3.model.AddWorkspaceItemsTask;
41 import com.android.launcher3.model.AllAppsList;
42 import com.android.launcher3.model.BaseModelUpdateTask;
43 import com.android.launcher3.model.BgDataModel;
44 import com.android.launcher3.model.BgDataModel.Callbacks;
45 import com.android.launcher3.model.CacheDataUpdatedTask;
46 import com.android.launcher3.model.ItemInstallQueue;
47 import com.android.launcher3.model.LoaderResults;
48 import com.android.launcher3.model.LoaderTask;
49 import com.android.launcher3.model.ModelDelegate;
50 import com.android.launcher3.model.ModelWriter;
51 import com.android.launcher3.model.PackageIncrementalDownloadUpdatedTask;
52 import com.android.launcher3.model.PackageInstallStateChangedTask;
53 import com.android.launcher3.model.PackageUpdatedTask;
54 import com.android.launcher3.model.ShortcutsChangedTask;
55 import com.android.launcher3.model.UserLockStateChangedTask;
56 import com.android.launcher3.model.data.AppInfo;
57 import com.android.launcher3.model.data.ItemInfo;
58 import com.android.launcher3.model.data.WorkspaceItemInfo;
59 import com.android.launcher3.pm.InstallSessionTracker;
60 import com.android.launcher3.pm.PackageInstallInfo;
61 import com.android.launcher3.pm.UserCache;
62 import com.android.launcher3.shortcuts.ShortcutRequest;
63 import com.android.launcher3.util.IntSet;
64 import com.android.launcher3.util.ItemInfoMatcher;
65 import com.android.launcher3.util.PackageUserKey;
66 import com.android.launcher3.util.Preconditions;
67 
68 import java.io.FileDescriptor;
69 import java.io.PrintWriter;
70 import java.util.ArrayList;
71 import java.util.HashSet;
72 import java.util.List;
73 import java.util.concurrent.CancellationException;
74 import java.util.concurrent.Executor;
75 import java.util.function.Consumer;
76 import java.util.function.Supplier;
77 
78 /**
79  * Maintains in-memory state of the Launcher. It is expected that there should be only one
80  * LauncherModel object held in a static. Also provide APIs for updating the database state
81  * for the Launcher.
82  */
83 public class LauncherModel extends LauncherApps.Callback implements InstallSessionTracker.Callback {
84     private static final boolean DEBUG_RECEIVER = false;
85 
86     static final String TAG = "Launcher.Model";
87 
88     private final LauncherAppState mApp;
89     private final Object mLock = new Object();
90 
91     private LoaderTask mLoaderTask;
92     private boolean mIsLoaderTaskRunning;
93 
94     // Indicates whether the current model data is valid or not.
95     // We start off with everything not loaded. After that, we assume that
96     // our monitoring of the package manager provides all updates and we never
97     // need to do a requery. This is only ever touched from the loader thread.
98     private boolean mModelLoaded;
isModelLoaded()99     public boolean isModelLoaded() {
100         synchronized (mLock) {
101             return mModelLoaded && mLoaderTask == null;
102         }
103     }
104 
105     private final ArrayList<Callbacks> mCallbacksList = new ArrayList<>(1);
106 
107     // < only access in worker thread >
108     private final AllAppsList mBgAllAppsList;
109 
110     /**
111      * All the static data should be accessed on the background thread, A lock should be acquired
112      * on this object when accessing any data from this model.
113      */
114     private final BgDataModel mBgDataModel = new BgDataModel();
115 
116     private final ModelDelegate mModelDelegate;
117 
118     // Runnable to check if the shortcuts permission has changed.
119     private final Runnable mDataValidationCheck = new Runnable() {
120         @Override
121         public void run() {
122             if (mModelLoaded) {
123                 mModelDelegate.validateData();
124             }
125         }
126     };
127 
LauncherModel(Context context, LauncherAppState app, IconCache iconCache, AppFilter appFilter)128     LauncherModel(Context context, LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
129         mApp = app;
130         mBgAllAppsList = new AllAppsList(iconCache, appFilter);
131         mModelDelegate = ModelDelegate.newInstance(context, app, mBgAllAppsList, mBgDataModel);
132     }
133 
getModelDelegate()134     public ModelDelegate getModelDelegate() {
135         return mModelDelegate;
136     }
137 
138     /**
139      * Adds the provided items to the workspace.
140      */
addAndBindAddedWorkspaceItems(List<Pair<ItemInfo, Object>> itemList)141     public void addAndBindAddedWorkspaceItems(List<Pair<ItemInfo, Object>> itemList) {
142         for (Callbacks cb : getCallbacks()) {
143             cb.preAddApps();
144         }
145         enqueueModelUpdateTask(new AddWorkspaceItemsTask(itemList));
146     }
147 
getWriter(boolean hasVerticalHotseat, boolean verifyChanges)148     public ModelWriter getWriter(boolean hasVerticalHotseat, boolean verifyChanges) {
149         return new ModelWriter(mApp.getContext(), this, mBgDataModel,
150                 hasVerticalHotseat, verifyChanges);
151     }
152 
153     @Override
onPackageChanged(String packageName, UserHandle user)154     public void onPackageChanged(String packageName, UserHandle user) {
155         int op = PackageUpdatedTask.OP_UPDATE;
156         enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packageName));
157     }
158 
159     @Override
onPackageRemoved(String packageName, UserHandle user)160     public void onPackageRemoved(String packageName, UserHandle user) {
161         onPackagesRemoved(user, packageName);
162     }
163 
onPackagesRemoved(UserHandle user, String... packages)164     public void onPackagesRemoved(UserHandle user, String... packages) {
165         int op = PackageUpdatedTask.OP_REMOVE;
166         FileLog.d(TAG, "package removed received " + TextUtils.join(",", packages));
167         enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packages));
168     }
169 
170     @Override
onPackageAdded(String packageName, UserHandle user)171     public void onPackageAdded(String packageName, UserHandle user) {
172         int op = PackageUpdatedTask.OP_ADD;
173         enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packageName));
174     }
175 
176     @Override
onPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing)177     public void onPackagesAvailable(String[] packageNames, UserHandle user,
178             boolean replacing) {
179         enqueueModelUpdateTask(
180                 new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE, user, packageNames));
181     }
182 
183     @Override
onPackagesUnavailable(String[] packageNames, UserHandle user, boolean replacing)184     public void onPackagesUnavailable(String[] packageNames, UserHandle user,
185             boolean replacing) {
186         if (!replacing) {
187             enqueueModelUpdateTask(new PackageUpdatedTask(
188                     PackageUpdatedTask.OP_UNAVAILABLE, user, packageNames));
189         }
190     }
191 
192     @Override
onPackagesSuspended(String[] packageNames, UserHandle user)193     public void onPackagesSuspended(String[] packageNames, UserHandle user) {
194         enqueueModelUpdateTask(new PackageUpdatedTask(
195                 PackageUpdatedTask.OP_SUSPEND, user, packageNames));
196     }
197 
198     @Override
onPackagesUnsuspended(String[] packageNames, UserHandle user)199     public void onPackagesUnsuspended(String[] packageNames, UserHandle user) {
200         enqueueModelUpdateTask(new PackageUpdatedTask(
201                 PackageUpdatedTask.OP_UNSUSPEND, user, packageNames));
202     }
203 
204     @Override
onPackageLoadingProgressChanged( String packageName, UserHandle user, float progress)205     public void onPackageLoadingProgressChanged(
206                 String packageName, UserHandle user, float progress) {
207         if (Utilities.ATLEAST_S) {
208             enqueueModelUpdateTask(new PackageIncrementalDownloadUpdatedTask(
209                     packageName, user, progress));
210         }
211     }
212 
213     @Override
onShortcutsChanged(String packageName, List<ShortcutInfo> shortcuts, UserHandle user)214     public void onShortcutsChanged(String packageName, List<ShortcutInfo> shortcuts,
215             UserHandle user) {
216         enqueueModelUpdateTask(new ShortcutsChangedTask(packageName, shortcuts, user, true));
217     }
218 
219     /**
220      * Called when the icon for an app changes, outside of package event
221      */
222     @WorkerThread
onAppIconChanged(String packageName, UserHandle user)223     public void onAppIconChanged(String packageName, UserHandle user) {
224         // Update the icon for the calendar package
225         Context context = mApp.getContext();
226         onPackageChanged(packageName, user);
227 
228         List<ShortcutInfo> pinnedShortcuts = new ShortcutRequest(context, user)
229                 .forPackage(packageName).query(ShortcutRequest.PINNED);
230         if (!pinnedShortcuts.isEmpty()) {
231             enqueueModelUpdateTask(new ShortcutsChangedTask(packageName, pinnedShortcuts, user,
232                     false));
233         }
234     }
235 
236     /**
237      * Called when the workspace items have drastically changed
238      */
onWorkspaceUiChanged()239     public void onWorkspaceUiChanged() {
240         MODEL_EXECUTOR.execute(mModelDelegate::workspaceLoadComplete);
241     }
242 
243     /**
244      * Called when the model is destroyed
245      */
destroy()246     public void destroy() {
247         MODEL_EXECUTOR.execute(mModelDelegate::destroy);
248     }
249 
onBroadcastIntent(Intent intent)250     public void onBroadcastIntent(Intent intent) {
251         if (DEBUG_RECEIVER) Log.d(TAG, "onReceive intent=" + intent);
252         final String action = intent.getAction();
253         if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
254             // If we have changed locale we need to clear out the labels in all apps/workspace.
255             forceReload();
256         } else if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) ||
257                 Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action) ||
258                 Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) {
259             UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
260             if (user != null) {
261                 if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) ||
262                         Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) {
263                     enqueueModelUpdateTask(new PackageUpdatedTask(
264                             PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE, user));
265                 }
266 
267                 // ACTION_MANAGED_PROFILE_UNAVAILABLE sends the profile back to locked mode, so
268                 // we need to run the state change task again.
269                 if (Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action) ||
270                         Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) {
271                     enqueueModelUpdateTask(new UserLockStateChangedTask(
272                             user, Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)));
273                 }
274             }
275         } else if (IS_STUDIO_BUILD && ACTION_FORCE_ROLOAD.equals(action)) {
276             for (Callbacks cb : getCallbacks()) {
277                 if (cb instanceof Launcher) {
278                     ((Launcher) cb).recreate();
279                 }
280             }
281         }
282     }
283 
284     /**
285      * Reloads the workspace items from the DB and re-binds the workspace. This should generally
286      * not be called as DB updates are automatically followed by UI update
287      */
forceReload()288     public void forceReload() {
289         synchronized (mLock) {
290             // Stop any existing loaders first, so they don't set mModelLoaded to true later
291             stopLoader();
292             mModelLoaded = false;
293         }
294 
295         // Start the loader if launcher is already running, otherwise the loader will run,
296         // the next time launcher starts
297         if (hasCallbacks()) {
298             startLoader();
299         }
300     }
301 
302     /**
303      * Rebinds all existing callbacks with already loaded model
304      */
rebindCallbacks()305     public void rebindCallbacks() {
306         if (hasCallbacks()) {
307             startLoader();
308         }
309     }
310 
311     /**
312      * Removes an existing callback
313      */
removeCallbacks(Callbacks callbacks)314     public void removeCallbacks(Callbacks callbacks) {
315         synchronized (mCallbacksList) {
316             Preconditions.assertUIThread();
317             if (mCallbacksList.remove(callbacks)) {
318                 if (stopLoader()) {
319                     // Rebind existing callbacks
320                     startLoader();
321                 }
322             }
323         }
324     }
325 
326     /**
327      * Adds a callbacks to receive model updates
328      * @return true if workspace load was performed synchronously
329      */
addCallbacksAndLoad(Callbacks callbacks)330     public boolean addCallbacksAndLoad(Callbacks callbacks) {
331         synchronized (mLock) {
332             addCallbacks(callbacks);
333             return startLoader();
334 
335         }
336     }
337 
338     /**
339      * Adds a callbacks to receive model updates
340      */
addCallbacks(Callbacks callbacks)341     public void addCallbacks(Callbacks callbacks) {
342         Preconditions.assertUIThread();
343         synchronized (mCallbacksList) {
344             mCallbacksList.add(callbacks);
345         }
346     }
347 
348     /**
349      * Starts the loader. Tries to bind {@params synchronousBindPage} synchronously if possible.
350      * @return true if the page could be bound synchronously.
351      */
startLoader()352     public boolean startLoader() {
353         // Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
354         ItemInstallQueue.INSTANCE.get(mApp.getContext())
355                 .pauseModelPush(ItemInstallQueue.FLAG_LOADER_RUNNING);
356         synchronized (mLock) {
357             // Don't bother to start the thread if we know it's not going to do anything
358             final Callbacks[] callbacksList = getCallbacks();
359             if (callbacksList.length > 0) {
360                 // Clear any pending bind-runnables from the synchronized load process.
361                 for (Callbacks cb : callbacksList) {
362                     MAIN_EXECUTOR.execute(cb::clearPendingBinds);
363                 }
364 
365                 // If there is already one running, tell it to stop.
366                 stopLoader();
367                 LoaderResults loaderResults = new LoaderResults(
368                         mApp, mBgDataModel, mBgAllAppsList, callbacksList);
369                 if (mModelLoaded && !mIsLoaderTaskRunning) {
370                     // Divide the set of loaded items into those that we are binding synchronously,
371                     // and everything else that is to be bound normally (asynchronously).
372                     loaderResults.bindWorkspace();
373                     // For now, continue posting the binding of AllApps as there are other
374                     // issues that arise from that.
375                     loaderResults.bindAllApps();
376                     loaderResults.bindDeepShortcuts();
377                     loaderResults.bindWidgets();
378                     return true;
379                 } else {
380                     stopLoader();
381                     mLoaderTask = new LoaderTask(
382                             mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, loaderResults);
383 
384                     // Always post the loader task, instead of running directly
385                     // (even on same thread) so that we exit any nested synchronized blocks
386                     MODEL_EXECUTOR.post(mLoaderTask);
387                 }
388             }
389         }
390         return false;
391     }
392 
393     /**
394      * If there is already a loader task running, tell it to stop.
395      * @return true if an existing loader was stopped.
396      */
stopLoader()397     public boolean stopLoader() {
398         synchronized (mLock) {
399             LoaderTask oldTask = mLoaderTask;
400             mLoaderTask = null;
401             if (oldTask != null) {
402                 oldTask.stopLocked();
403                 return true;
404             }
405             return false;
406         }
407     }
408 
409     /**
410      * Loads the model if not loaded
411      * @param callback called with the data model upon successful load or null on model thread.
412      */
loadAsync(Consumer<BgDataModel> callback)413     public void loadAsync(Consumer<BgDataModel> callback) {
414         synchronized (mLock) {
415             if (!mModelLoaded && !mIsLoaderTaskRunning) {
416                 startLoader();
417             }
418         }
419         MODEL_EXECUTOR.post(() -> callback.accept(isModelLoaded() ? mBgDataModel : null));
420     }
421 
422     @Override
onInstallSessionCreated(final PackageInstallInfo sessionInfo)423     public void onInstallSessionCreated(final PackageInstallInfo sessionInfo) {
424         if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) {
425             enqueueModelUpdateTask(new BaseModelUpdateTask() {
426                 @Override
427                 public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
428                     apps.addPromiseApp(app.getContext(), sessionInfo);
429                     bindApplicationsIfNeeded();
430                 }
431             });
432         }
433     }
434 
435     @Override
onSessionFailure(String packageName, UserHandle user)436     public void onSessionFailure(String packageName, UserHandle user) {
437         if (!FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()) {
438             return;
439         }
440         enqueueModelUpdateTask(new BaseModelUpdateTask() {
441             @Override
442             public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
443                 final IntSet removedIds = new IntSet();
444                 synchronized (dataModel) {
445                     for (ItemInfo info : dataModel.itemsIdMap) {
446                         if (info instanceof WorkspaceItemInfo
447                                 && ((WorkspaceItemInfo) info).hasPromiseIconUi()
448                                 && user.equals(info.user)
449                                 && info.getIntent() != null
450                                 && TextUtils.equals(packageName, info.getIntent().getPackage())) {
451                             removedIds.add(info.id);
452                         }
453                     }
454                 }
455 
456                 if (!removedIds.isEmpty()) {
457                     deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedIds));
458                 }
459             }
460         });
461     }
462 
463     @Override
onPackageStateChanged(PackageInstallInfo installInfo)464     public void onPackageStateChanged(PackageInstallInfo installInfo) {
465         enqueueModelUpdateTask(new PackageInstallStateChangedTask(installInfo));
466     }
467 
468     /**
469      * Updates the icons and label of all pending icons for the provided package name.
470      */
471     @Override
onUpdateSessionDisplay(PackageUserKey key, PackageInstaller.SessionInfo info)472     public void onUpdateSessionDisplay(PackageUserKey key, PackageInstaller.SessionInfo info) {
473         mApp.getIconCache().updateSessionCache(key, info);
474 
475         HashSet<String> packages = new HashSet<>();
476         packages.add(key.mPackageName);
477         enqueueModelUpdateTask(new CacheDataUpdatedTask(
478                 CacheDataUpdatedTask.OP_SESSION_UPDATE, key.mUser, packages));
479     }
480 
481     public class LoaderTransaction implements AutoCloseable {
482 
483         private final LoaderTask mTask;
484 
LoaderTransaction(LoaderTask task)485         private LoaderTransaction(LoaderTask task) throws CancellationException {
486             synchronized (mLock) {
487                 if (mLoaderTask != task) {
488                     throw new CancellationException("Loader already stopped");
489                 }
490                 mTask = task;
491                 mIsLoaderTaskRunning = true;
492                 mModelLoaded = false;
493             }
494         }
495 
commit()496         public void commit() {
497             synchronized (mLock) {
498                 // Everything loaded bind the data.
499                 mModelLoaded = true;
500             }
501         }
502 
503         @Override
close()504         public void close() {
505             synchronized (mLock) {
506                 // If we are still the last one to be scheduled, remove ourselves.
507                 if (mLoaderTask == mTask) {
508                     mLoaderTask = null;
509                 }
510                 mIsLoaderTaskRunning = false;
511             }
512         }
513     }
514 
beginLoader(LoaderTask task)515     public LoaderTransaction beginLoader(LoaderTask task) throws CancellationException {
516         return new LoaderTransaction(task);
517     }
518 
519     /**
520      * Refreshes the cached shortcuts if the shortcut permission has changed.
521      * Current implementation simply reloads the workspace, but it can be optimized to
522      * use partial updates similar to {@link UserCache}
523      */
validateModelDataOnResume()524     public void validateModelDataOnResume() {
525         MODEL_EXECUTOR.getHandler().removeCallbacks(mDataValidationCheck);
526         MODEL_EXECUTOR.post(mDataValidationCheck);
527     }
528 
529     /**
530      * Called when the icons for packages have been updated in the icon cache.
531      */
onPackageIconsUpdated(HashSet<String> updatedPackages, UserHandle user)532     public void onPackageIconsUpdated(HashSet<String> updatedPackages, UserHandle user) {
533         // If any package icon has changed (app was updated while launcher was dead),
534         // update the corresponding shortcuts.
535         enqueueModelUpdateTask(new CacheDataUpdatedTask(
536                 CacheDataUpdatedTask.OP_CACHE_UPDATE, user, updatedPackages));
537     }
538 
539     /**
540      * Called when the labels for the widgets has updated in the icon cache.
541      */
onWidgetLabelsUpdated(HashSet<String> updatedPackages, UserHandle user)542     public void onWidgetLabelsUpdated(HashSet<String> updatedPackages, UserHandle user) {
543         enqueueModelUpdateTask(new BaseModelUpdateTask() {
544             @Override
545             public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
546                 dataModel.widgetsModel.onPackageIconsUpdated(updatedPackages, user, app);
547                 bindUpdatedWidgets(dataModel);
548             }
549         });
550     }
551 
enqueueModelUpdateTask(ModelUpdateTask task)552     public void enqueueModelUpdateTask(ModelUpdateTask task) {
553         task.init(mApp, this, mBgDataModel, mBgAllAppsList, MAIN_EXECUTOR);
554         MODEL_EXECUTOR.execute(task);
555     }
556 
557     /**
558      * A task to be executed on the current callbacks on the UI thread.
559      * If there is no current callbacks, the task is ignored.
560      */
561     public interface CallbackTask {
562 
execute(Callbacks callbacks)563         void execute(Callbacks callbacks);
564     }
565 
566     /**
567      * A runnable which changes/updates the data model of the launcher based on certain events.
568      */
569     public interface ModelUpdateTask extends Runnable {
570 
571         /**
572          * Called before the task is posted to initialize the internal state.
573          */
init(LauncherAppState app, LauncherModel model, BgDataModel dataModel, AllAppsList allAppsList, Executor uiExecutor)574         void init(LauncherAppState app, LauncherModel model,
575                 BgDataModel dataModel, AllAppsList allAppsList, Executor uiExecutor);
576 
577     }
578 
updateAndBindWorkspaceItem(WorkspaceItemInfo si, ShortcutInfo info)579     public void updateAndBindWorkspaceItem(WorkspaceItemInfo si, ShortcutInfo info) {
580         updateAndBindWorkspaceItem(() -> {
581             si.updateFromDeepShortcutInfo(info, mApp.getContext());
582             mApp.getIconCache().getShortcutIcon(si, info);
583             return si;
584         });
585     }
586 
587     /**
588      * Utility method to update a shortcut on the background thread.
589      */
updateAndBindWorkspaceItem(final Supplier<WorkspaceItemInfo> itemProvider)590     public void updateAndBindWorkspaceItem(final Supplier<WorkspaceItemInfo> itemProvider) {
591         enqueueModelUpdateTask(new BaseModelUpdateTask() {
592             @Override
593             public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
594                 WorkspaceItemInfo info = itemProvider.get();
595                 getModelWriter().updateItemInDatabase(info);
596                 ArrayList<WorkspaceItemInfo> update = new ArrayList<>();
597                 update.add(info);
598                 bindUpdatedWorkspaceItems(update);
599             }
600         });
601     }
602 
refreshAndBindWidgetsAndShortcuts(@ullable final PackageUserKey packageUser)603     public void refreshAndBindWidgetsAndShortcuts(@Nullable final PackageUserKey packageUser) {
604         enqueueModelUpdateTask(new BaseModelUpdateTask() {
605             @Override
606             public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
607                 dataModel.widgetsModel.update(app, packageUser);
608                 bindUpdatedWidgets(dataModel);
609             }
610         });
611     }
612 
dumpState(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)613     public void dumpState(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
614         if (args.length > 0 && TextUtils.equals(args[0], "--all")) {
615             writer.println(prefix + "All apps list: size=" + mBgAllAppsList.data.size());
616             for (AppInfo info : mBgAllAppsList.data) {
617                 writer.println(prefix + "   title=\"" + info.title
618                         + "\" bitmapIcon=" + info.bitmap.icon
619                         + " componentName=" + info.componentName.getPackageName());
620             }
621             writer.println();
622         }
623         mModelDelegate.dump(prefix, fd, writer, args);
624         mBgDataModel.dump(prefix, fd, writer, args);
625     }
626 
627     /**
628      * Returns true if there are any callbacks attached to the model
629      */
hasCallbacks()630     public boolean hasCallbacks() {
631         synchronized (mCallbacksList) {
632             return !mCallbacksList.isEmpty();
633         }
634     }
635 
636     /**
637      * Returns an array of currently attached callbacks
638      */
getCallbacks()639     public Callbacks[] getCallbacks() {
640         synchronized (mCallbacksList) {
641             return mCallbacksList.toArray(new Callbacks[mCallbacksList.size()]);
642         }
643     }
644 }
645