1 /*
2  * Copyright (C) 2016 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 package com.android.launcher3.model;
17 
18 import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY;
19 
20 import static com.android.launcher3.BuildConfig.QSB_ON_FIRST_SCREEN;
21 import static com.android.launcher3.BuildConfig.WIDGETS_ENABLED;
22 import static com.android.launcher3.Flags.enableSmartspaceRemovalToggle;
23 import static com.android.launcher3.Utilities.SHOULD_SHOW_FIRST_PAGE_WIDGET;
24 import static com.android.launcher3.shortcuts.ShortcutRequest.PINNED;
25 
26 import static java.util.stream.Collectors.groupingBy;
27 import static java.util.stream.Collectors.mapping;
28 
29 import android.content.Context;
30 import android.content.pm.LauncherApps;
31 import android.content.pm.ShortcutInfo;
32 import android.os.UserHandle;
33 import android.text.TextUtils;
34 import android.util.ArraySet;
35 import android.util.Log;
36 import android.util.Pair;
37 import android.view.View;
38 
39 import androidx.annotation.NonNull;
40 import androidx.annotation.Nullable;
41 
42 import com.android.launcher3.LauncherSettings;
43 import com.android.launcher3.LauncherSettings.Favorites;
44 import com.android.launcher3.Workspace;
45 import com.android.launcher3.config.FeatureFlags;
46 import com.android.launcher3.model.data.AppInfo;
47 import com.android.launcher3.model.data.AppPairInfo;
48 import com.android.launcher3.model.data.CollectionInfo;
49 import com.android.launcher3.model.data.FolderInfo;
50 import com.android.launcher3.model.data.ItemInfo;
51 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
52 import com.android.launcher3.model.data.WorkspaceItemInfo;
53 import com.android.launcher3.pm.UserCache;
54 import com.android.launcher3.shortcuts.ShortcutKey;
55 import com.android.launcher3.shortcuts.ShortcutRequest;
56 import com.android.launcher3.shortcuts.ShortcutRequest.QueryResult;
57 import com.android.launcher3.util.ComponentKey;
58 import com.android.launcher3.util.IntArray;
59 import com.android.launcher3.util.IntSet;
60 import com.android.launcher3.util.IntSparseArrayMap;
61 import com.android.launcher3.util.ItemInflater;
62 import com.android.launcher3.util.PackageUserKey;
63 import com.android.launcher3.util.RunnableList;
64 import com.android.launcher3.widget.model.WidgetsListBaseEntry;
65 
66 import java.io.FileDescriptor;
67 import java.io.PrintWriter;
68 import java.util.ArrayList;
69 import java.util.Arrays;
70 import java.util.Collections;
71 import java.util.HashMap;
72 import java.util.HashSet;
73 import java.util.Iterator;
74 import java.util.List;
75 import java.util.Map;
76 import java.util.Set;
77 import java.util.function.Consumer;
78 import java.util.function.Predicate;
79 import java.util.stream.Collectors;
80 import java.util.stream.Stream;
81 
82 /**
83  * All the data stored in-memory and managed by the LauncherModel
84  */
85 public class BgDataModel {
86 
87     private static final String TAG = "BgDataModel";
88 
89     /**
90      * Map of all the ItemInfos (shortcuts, folders, and widgets) created by
91      * LauncherModel to their ids
92      */
93     public final IntSparseArrayMap<ItemInfo> itemsIdMap = new IntSparseArrayMap<>();
94 
95     /**
96      * List of all the folders and shortcuts directly on the home screen (no widgets
97      * or shortcuts within folders).
98      */
99     public final ArrayList<ItemInfo> workspaceItems = new ArrayList<>();
100 
101     /**
102      * All LauncherAppWidgetInfo created by LauncherModel.
103      */
104     public final ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<>();
105 
106     /**
107      * Map of id to CollectionInfos of all the folders or app pairs created by LauncherModel
108      */
109     public final IntSparseArrayMap<CollectionInfo> collections = new IntSparseArrayMap<>();
110 
111     /**
112      * Extra container based items
113      */
114     public final IntSparseArrayMap<FixedContainerItems> extraItems = new IntSparseArrayMap<>();
115 
116     /**
117      * Maps all launcher activities to counts of their shortcuts.
118      */
119     public final HashMap<ComponentKey, Integer> deepShortcutMap = new HashMap<>();
120 
121     /**
122      * Entire list of widgets.
123      */
124     public final WidgetsModel widgetsModel = new WidgetsModel();
125 
126     /**
127      * Cache for strings used in launcher
128      */
129     public final StringCache stringCache = new StringCache();
130 
131     /**
132      * Id when the model was last bound
133      */
134     public int lastBindId = 0;
135 
136     /**
137      * Load id for which the callbacks were successfully bound
138      */
139     public int lastLoadId = -1;
140     public boolean isFirstPagePinnedItemEnabled = QSB_ON_FIRST_SCREEN
141             && !enableSmartspaceRemovalToggle();
142 
143     /**
144      * Clears all the data
145      */
clear()146     public synchronized void clear() {
147         workspaceItems.clear();
148         appWidgets.clear();
149         collections.clear();
150         itemsIdMap.clear();
151         deepShortcutMap.clear();
152         extraItems.clear();
153     }
154 
155     /**
156      * Creates an array of valid workspace screens based on current items in the model.
157      */
collectWorkspaceScreens()158     public synchronized IntArray collectWorkspaceScreens() {
159         IntSet screenSet = new IntSet();
160         for (ItemInfo item: itemsIdMap) {
161             if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
162                 screenSet.add(item.screenId);
163             }
164         }
165         if ((FeatureFlags.QSB_ON_FIRST_SCREEN
166                 && !SHOULD_SHOW_FIRST_PAGE_WIDGET)
167                 || screenSet.isEmpty()) {
168             screenSet.add(Workspace.FIRST_SCREEN_ID);
169         }
170         return screenSet.getArray();
171     }
172 
dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)173     public synchronized void dump(String prefix, FileDescriptor fd, PrintWriter writer,
174             String[] args) {
175         writer.println(prefix + "Data Model:");
176         writer.println(prefix + " ---- workspace items ");
177         for (int i = 0; i < workspaceItems.size(); i++) {
178             writer.println(prefix + '\t' + workspaceItems.get(i).toString());
179         }
180         writer.println(prefix + " ---- appwidget items ");
181         for (int i = 0; i < appWidgets.size(); i++) {
182             writer.println(prefix + '\t' + appWidgets.get(i).toString());
183         }
184         writer.println(prefix + " ---- collection items ");
185         for (int i = 0; i < collections.size(); i++) {
186             writer.println(prefix + '\t' + collections.valueAt(i).toString());
187         }
188         writer.println(prefix + " ---- extra items ");
189         for (int i = 0; i < extraItems.size(); i++) {
190             writer.println(prefix + '\t' + extraItems.valueAt(i).toString());
191         }
192         writer.println(prefix + " ---- items id map ");
193         for (int i = 0; i < itemsIdMap.size(); i++) {
194             writer.println(prefix + '\t' + itemsIdMap.valueAt(i).toString());
195         }
196 
197         if (args.length > 0 && TextUtils.equals(args[0], "--all")) {
198             writer.println(prefix + "shortcut counts ");
199             for (Integer count : deepShortcutMap.values()) {
200                 writer.print(count + ", ");
201             }
202             writer.println();
203         }
204     }
205 
removeItem(Context context, ItemInfo... items)206     public synchronized void removeItem(Context context, ItemInfo... items) {
207         removeItem(context, Arrays.asList(items));
208     }
209 
removeItem(Context context, Iterable<? extends ItemInfo> items)210     public synchronized void removeItem(Context context, Iterable<? extends ItemInfo> items) {
211         ArraySet<UserHandle> updatedDeepShortcuts = new ArraySet<>();
212         for (ItemInfo item : items) {
213             switch (item.itemType) {
214                 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
215                 case LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR:
216                     collections.remove(item.id);
217                     if (FeatureFlags.IS_STUDIO_BUILD) {
218                         for (ItemInfo info : itemsIdMap) {
219                             if (info.container == item.id) {
220                                 // We are deleting a collection which still contains items that
221                                 // think they are contained by that collection.
222                                 String msg = "deleting a collection (" + item + ") which still "
223                                         + "contains items (" + info + ")";
224                                 Log.e(TAG, msg);
225                             }
226                         }
227                     }
228                     workspaceItems.remove(item);
229                     break;
230                 case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: {
231                     updatedDeepShortcuts.add(item.user);
232                     // Fall through.
233                 }
234                 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
235                     workspaceItems.remove(item);
236                     break;
237                 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
238                 case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
239                     appWidgets.remove(item);
240                     break;
241             }
242             itemsIdMap.remove(item.id);
243         }
244         updatedDeepShortcuts.forEach(user -> updateShortcutPinnedState(context, user));
245     }
246 
addItem(Context context, ItemInfo item, boolean newItem)247     public synchronized void addItem(Context context, ItemInfo item, boolean newItem) {
248         addItem(context, item, newItem, null);
249     }
250 
addItem( Context context, ItemInfo item, boolean newItem, @Nullable LoaderMemoryLogger logger)251     public synchronized void addItem(
252             Context context, ItemInfo item, boolean newItem, @Nullable LoaderMemoryLogger logger) {
253         if (logger != null) {
254             logger.addLog(
255                     Log.DEBUG,
256                     TAG,
257                     String.format("Adding item to ID map: %s", item.toString()),
258                     /* stackTrace= */ null);
259         }
260         itemsIdMap.put(item.id, item);
261         switch (item.itemType) {
262             case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
263                 collections.put(item.id, (FolderInfo) item);
264                 workspaceItems.add(item);
265                 break;
266             case LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR:
267                 collections.put(item.id, (AppPairInfo) item);
268                 // Fall through here. App pairs are both containers (like folders) and containable
269                 // items (can be placed in folders). So we need to add app pairs to the folders
270                 // array (above) but also verify the existence of their container, like regular
271                 // apps (below).
272             case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
273             case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
274                 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
275                         item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
276                     workspaceItems.add(item);
277                 } else {
278                     if (newItem) {
279                         if (!collections.containsKey(item.container)) {
280                             // Adding an item to a nonexistent collection.
281                             String msg = "attempted to add item: " + item + " to a nonexistent app"
282                                     + " collection";
283                             Log.e(TAG, msg);
284                         }
285                     } else {
286                         findOrMakeFolder(item.container).add(item);
287                     }
288                 }
289                 break;
290             case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
291             case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
292                 appWidgets.add((LauncherAppWidgetInfo) item);
293                 break;
294         }
295         if (newItem && item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
296             updateShortcutPinnedState(context, item.user);
297         }
298     }
299 
300     /**
301      * Updates the deep shortucts state in system to match out internal model, pinning any missing
302      * shortcuts and unpinning any extra shortcuts.
303      */
updateShortcutPinnedState(Context context)304     public void updateShortcutPinnedState(Context context) {
305         for (UserHandle user : UserCache.INSTANCE.get(context).getUserProfiles()) {
306             updateShortcutPinnedState(context, user);
307         }
308     }
309 
310     /**
311      * Updates the deep shortucts state in system to match out internal model, pinning any missing
312      * shortcuts and unpinning any extra shortcuts.
313      */
updateShortcutPinnedState(Context context, UserHandle user)314     public synchronized void updateShortcutPinnedState(Context context, UserHandle user) {
315         if (!WIDGETS_ENABLED) {
316             return;
317         }
318 
319         // Collect all system shortcuts
320         QueryResult result = new ShortcutRequest(context, user)
321                 .query(PINNED | FLAG_GET_KEY_FIELDS_ONLY);
322         if (!result.wasSuccess()) {
323             return;
324         }
325         // Map of packageName to shortcutIds that are currently in the system
326         Map<String, Set<String>> systemMap = result.stream()
327                 .collect(groupingBy(ShortcutInfo::getPackage,
328                         mapping(ShortcutInfo::getId, Collectors.toSet())));
329 
330         // Collect all model shortcuts
331         Stream.Builder<WorkspaceItemInfo> itemStream = Stream.builder();
332         forAllWorkspaceItemInfos(user, itemStream::accept);
333         // Map of packageName to shortcutIds that are currently in our model
334         Map<String, Set<String>> modelMap = Stream.concat(
335                     // Model shortcuts
336                     itemStream.build()
337                         .filter(wi -> wi.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
338                         .map(ShortcutKey::fromItemInfo),
339                     // Pending shortcuts
340                     ItemInstallQueue.INSTANCE.get(context).getPendingShortcuts(user))
341                 .collect(groupingBy(ShortcutKey::getPackageName,
342                         mapping(ShortcutKey::getId, Collectors.toSet())));
343 
344         // Check for diff
345         for (Map.Entry<String, Set<String>> entry : modelMap.entrySet()) {
346             Set<String> modelShortcuts = entry.getValue();
347             Set<String> systemShortcuts = systemMap.remove(entry.getKey());
348             if (systemShortcuts == null) {
349                 systemShortcuts = Collections.emptySet();
350             }
351 
352             // Do not use .equals as it can vary based on the type of set
353             if (systemShortcuts.size() != modelShortcuts.size()
354                     || !systemShortcuts.containsAll(modelShortcuts)) {
355                 // Update system state for this package
356                 try {
357                     context.getSystemService(LauncherApps.class).pinShortcuts(
358                             entry.getKey(), new ArrayList<>(modelShortcuts), user);
359                 } catch (SecurityException | IllegalStateException e) {
360                     Log.w(TAG, "Failed to pin shortcut", e);
361                 }
362             }
363         }
364 
365         // If there are any extra pinned shortcuts, remove them
366         systemMap.keySet().forEach(packageName -> {
367             // Update system state
368             try {
369                 context.getSystemService(LauncherApps.class).pinShortcuts(
370                         packageName, Collections.emptyList(), user);
371             } catch (SecurityException | IllegalStateException e) {
372                 Log.w(TAG, "Failed to unpin shortcut", e);
373             }
374         });
375     }
376 
377     /**
378      * Return an existing FolderInfo object if we have encountered this ID previously,
379      * or make a new one.
380      */
findOrMakeFolder(int id)381     public synchronized CollectionInfo findOrMakeFolder(int id) {
382         // See if a placeholder was created for us already
383         CollectionInfo collectionInfo = collections.get(id);
384         if (collectionInfo == null) {
385             // No placeholder -- create a new blank folder instance. At this point, we don't know
386             // if the desired container is supposed to be a folder or an app pair. In the case that
387             // it is an app pair, the blank folder will be replaced by a blank app pair when the app
388             // pair is getting processed, in WorkspaceItemProcessor.processFolderOrAppPair().
389             collectionInfo = new FolderInfo();
390             collections.put(id, collectionInfo);
391         }
392         return collectionInfo;
393     }
394 
395     /**
396      * Clear all the deep shortcut counts for the given package, and re-add the new shortcut counts.
397      */
updateDeepShortcutCounts( String packageName, UserHandle user, List<ShortcutInfo> shortcuts)398     public synchronized void updateDeepShortcutCounts(
399             String packageName, UserHandle user, List<ShortcutInfo> shortcuts) {
400         if (packageName != null) {
401             Iterator<ComponentKey> keysIter = deepShortcutMap.keySet().iterator();
402             while (keysIter.hasNext()) {
403                 ComponentKey next = keysIter.next();
404                 if (next.componentName.getPackageName().equals(packageName)
405                         && next.user.equals(user)) {
406                     keysIter.remove();
407                 }
408             }
409         }
410 
411         // Now add the new shortcuts to the map.
412         for (ShortcutInfo shortcut : shortcuts) {
413             boolean shouldShowInContainer = shortcut.isEnabled()
414                     && (shortcut.isDeclaredInManifest() || shortcut.isDynamic())
415                     && shortcut.getActivity() != null;
416             if (shouldShowInContainer) {
417                 ComponentKey targetComponent
418                         = new ComponentKey(shortcut.getActivity(), shortcut.getUserHandle());
419 
420                 Integer previousCount = deepShortcutMap.get(targetComponent);
421                 deepShortcutMap.put(targetComponent, previousCount == null ? 1 : previousCount + 1);
422             }
423         }
424     }
425 
426     /**
427      * Returns a list containing all workspace items including widgets.
428      */
getAllWorkspaceItems()429     public synchronized ArrayList<ItemInfo> getAllWorkspaceItems() {
430         ArrayList<ItemInfo> items = new ArrayList<>(workspaceItems.size() + appWidgets.size());
431         items.addAll(workspaceItems);
432         items.addAll(appWidgets);
433         return items;
434     }
435 
436     /**
437      * Calls the provided {@code op} for all workspaceItems in the in-memory model (both persisted
438      * items and dynamic/predicted items for the provided {@code userHandle}.
439      * Note the call is not synchronized over the model, that should be handled by the called.
440      */
forAllWorkspaceItemInfos(UserHandle userHandle, Consumer<WorkspaceItemInfo> op)441     public void forAllWorkspaceItemInfos(UserHandle userHandle, Consumer<WorkspaceItemInfo> op) {
442         for (ItemInfo info : itemsIdMap) {
443             if (info instanceof WorkspaceItemInfo && userHandle.equals(info.user)) {
444                 op.accept((WorkspaceItemInfo) info);
445             }
446         }
447 
448         for (int i = extraItems.size() - 1; i >= 0; i--) {
449             for (ItemInfo info : extraItems.valueAt(i).items) {
450                 if (info instanceof WorkspaceItemInfo && userHandle.equals(info.user)) {
451                     op.accept((WorkspaceItemInfo) info);
452                 }
453             }
454         }
455     }
456 
457     /**
458      * An object containing items corresponding to a fixed container
459      */
460     public static class FixedContainerItems {
461 
462         public final int containerId;
463         public final List<ItemInfo> items;
464 
FixedContainerItems(int containerId, List<ItemInfo> items)465         public FixedContainerItems(int containerId, List<ItemInfo> items) {
466             this.containerId = containerId;
467             this.items = Collections.unmodifiableList(items);
468         }
469 
470         @Override
471         @NonNull
toString()472         public final String toString() {
473             StringBuilder s = new StringBuilder();
474             s.append("FixedContainerItems:");
475             s.append(" id=").append(containerId);
476             s.append(" itemCount=").append(items.size());
477             for (int i = 0; i < items.size(); i++) {
478                 s.append(" item #").append(i).append(": ").append(items.get(i).toString());
479             }
480             return s.toString();
481         }
482 
483     }
484 
485 
486     public interface Callbacks {
487         // If the launcher has permission to access deep shortcuts.
488         int FLAG_HAS_SHORTCUT_PERMISSION = 1 << 0;
489         // If quiet mode is enabled for any user
490         int FLAG_QUIET_MODE_ENABLED = 1 << 1;
491         // If launcher can change quiet mode
492         int FLAG_QUIET_MODE_CHANGE_PERMISSION = 1 << 2;
493         // If quiet mode is enabled for work profile user
494         int FLAG_WORK_PROFILE_QUIET_MODE_ENABLED = 1 << 3;
495         // If quiet mode is enabled for private profile user
496         int FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED = 1 << 4;
497 
498         /**
499          * Returns an IntSet of page ids to bind first, synchronously if possible
500          * or an empty IntSet
501          * @param orderedScreenIds All the page ids to be bound
502          */
503         @NonNull
getPagesToBindSynchronously(IntArray orderedScreenIds)504         default IntSet getPagesToBindSynchronously(IntArray orderedScreenIds) {
505             return new IntSet();
506         }
507 
clearPendingBinds()508         default void clearPendingBinds() { }
startBinding()509         default void startBinding() { }
510 
511         @Nullable
getItemInflater()512         default ItemInflater getItemInflater() {
513             return null;
514         }
515 
bindItems(@onNull List<ItemInfo> shortcuts, boolean forceAnimateIcons)516         default void bindItems(@NonNull List<ItemInfo> shortcuts, boolean forceAnimateIcons) { }
517         /** Alternate method to bind preinflated views */
bindInflatedItems(@onNull List<Pair<ItemInfo, View>> items)518         default void bindInflatedItems(@NonNull List<Pair<ItemInfo, View>> items) { }
519 
bindScreens(IntArray orderedScreenIds)520         default void bindScreens(IntArray orderedScreenIds) { }
setIsFirstPagePinnedItemEnabled(boolean isFirstPagePinnedItemEnabled)521         default void setIsFirstPagePinnedItemEnabled(boolean isFirstPagePinnedItemEnabled) { }
finishBindingItems(IntSet pagesBoundFirst)522         default void finishBindingItems(IntSet pagesBoundFirst) { }
preAddApps()523         default void preAddApps() { }
bindAppsAdded(IntArray newScreens, ArrayList<ItemInfo> addNotAnimated, ArrayList<ItemInfo> addAnimated)524         default void bindAppsAdded(IntArray newScreens,
525                 ArrayList<ItemInfo> addNotAnimated, ArrayList<ItemInfo> addAnimated) { }
526 
527         /**
528          * Called when some persistent property of an item is modified
529          */
bindItemsModified(List<ItemInfo> items)530         default void bindItemsModified(List<ItemInfo> items) { }
531 
532         /**
533          * Binds updated incremental download progress
534          */
bindIncrementalDownloadProgressUpdated(AppInfo app)535         default void bindIncrementalDownloadProgressUpdated(AppInfo app) { }
bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated)536         default void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated) { }
bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets)537         default void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets) { }
bindRestoreItemsChange(HashSet<ItemInfo> updates)538         default void bindRestoreItemsChange(HashSet<ItemInfo> updates) { }
bindWorkspaceComponentsRemoved(Predicate<ItemInfo> matcher)539         default void bindWorkspaceComponentsRemoved(Predicate<ItemInfo> matcher) { }
bindAllWidgets(List<WidgetsListBaseEntry> widgets)540         default void bindAllWidgets(List<WidgetsListBaseEntry> widgets) { }
bindSmartspaceWidget()541         default void bindSmartspaceWidget() { }
542 
543         /** Called when workspace has been bound. */
onInitialBindComplete(@onNull IntSet boundPages, @NonNull RunnableList pendingTasks, @NonNull RunnableList onCompleteSignal, int workspaceItemCount, boolean isBindSync)544         default void onInitialBindComplete(@NonNull IntSet boundPages,
545                 @NonNull RunnableList pendingTasks,
546                 @NonNull RunnableList onCompleteSignal,
547                 int workspaceItemCount, boolean isBindSync) {
548             pendingTasks.executeAllAndDestroy();
549         }
550 
bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMap)551         default void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMap) { }
552 
553         /**
554          * Binds extra item provided any external source
555          */
bindExtraContainerItems(FixedContainerItems item)556         default void bindExtraContainerItems(FixedContainerItems item) { }
557 
bindAllApplications(AppInfo[] apps, int flags, Map<PackageUserKey, Integer> packageUserKeytoUidMap)558         default void bindAllApplications(AppInfo[] apps, int flags,
559                 Map<PackageUserKey, Integer> packageUserKeytoUidMap) {
560         }
561 
562         /**
563          * Binds the cache of string resources
564          */
bindStringCache(StringCache cache)565         default void bindStringCache(StringCache cache) { }
566     }
567 }
568