• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.content.Context;
19 import android.os.UserHandle;
20 import android.text.TextUtils;
21 import android.util.Log;
22 import android.util.MutableInt;
23 
24 import com.android.launcher3.FolderInfo;
25 import com.android.launcher3.InstallShortcutReceiver;
26 import com.android.launcher3.ItemInfo;
27 import com.android.launcher3.LauncherAppWidgetInfo;
28 import com.android.launcher3.LauncherSettings;
29 import com.android.launcher3.ShortcutInfo;
30 import com.android.launcher3.config.FeatureFlags;
31 import com.android.launcher3.logging.DumpTargetWrapper;
32 import com.android.launcher3.model.nano.LauncherDumpProto;
33 import com.android.launcher3.model.nano.LauncherDumpProto.ContainerType;
34 import com.android.launcher3.model.nano.LauncherDumpProto.DumpTarget;
35 import com.android.launcher3.shortcuts.DeepShortcutManager;
36 import com.android.launcher3.shortcuts.ShortcutInfoCompat;
37 import com.android.launcher3.shortcuts.ShortcutKey;
38 import com.android.launcher3.util.ComponentKey;
39 import com.android.launcher3.util.LongArrayMap;
40 import com.android.launcher3.util.MultiHashMap;
41 import com.google.protobuf.nano.MessageNano;
42 
43 import java.io.FileDescriptor;
44 import java.io.FileOutputStream;
45 import java.io.IOException;
46 import java.io.PrintWriter;
47 import java.util.ArrayList;
48 import java.util.Arrays;
49 import java.util.HashMap;
50 import java.util.Iterator;
51 import java.util.List;
52 import java.util.Map;
53 
54 /**
55  * All the data stored in-memory and managed by the LauncherModel
56  */
57 public class BgDataModel {
58 
59     private static final String TAG = "BgDataModel";
60 
61     /**
62      * Map of all the ItemInfos (shortcuts, folders, and widgets) created by
63      * LauncherModel to their ids
64      */
65     public final LongArrayMap<ItemInfo> itemsIdMap = new LongArrayMap<>();
66 
67     /**
68      * List of all the folders and shortcuts directly on the home screen (no widgets
69      * or shortcuts within folders).
70      */
71     public final ArrayList<ItemInfo> workspaceItems = new ArrayList<>();
72 
73     /**
74      * All LauncherAppWidgetInfo created by LauncherModel.
75      */
76     public final ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<>();
77 
78     /**
79      * Map of id to FolderInfos of all the folders created by LauncherModel
80      */
81     public final LongArrayMap<FolderInfo> folders = new LongArrayMap<>();
82 
83     /**
84      * Ordered list of workspace screens ids.
85      */
86     public final ArrayList<Long> workspaceScreens = new ArrayList<>();
87 
88     /**
89      * Map of ShortcutKey to the number of times it is pinned.
90      */
91     public final Map<ShortcutKey, MutableInt> pinnedShortcutCounts = new HashMap<>();
92 
93     /**
94      * True if the launcher has permission to access deep shortcuts.
95      */
96     public boolean hasShortcutHostPermission;
97 
98     /**
99      * Maps all launcher activities to the id's of their shortcuts (if they have any).
100      */
101     public final MultiHashMap<ComponentKey, String> deepShortcutMap = new MultiHashMap<>();
102 
103     /**
104      * Entire list of widgets.
105      */
106     public final WidgetsModel widgetsModel = new WidgetsModel();
107 
108     /**
109      * Id when the model was last bound
110      */
111     public int lastBindId = 0;
112 
113     /**
114      * Clears all the data
115      */
clear()116     public synchronized void clear() {
117         workspaceItems.clear();
118         appWidgets.clear();
119         folders.clear();
120         itemsIdMap.clear();
121         workspaceScreens.clear();
122         pinnedShortcutCounts.clear();
123         deepShortcutMap.clear();
124     }
125 
dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)126     public synchronized void dump(String prefix, FileDescriptor fd, PrintWriter writer,
127             String[] args) {
128         if (Arrays.asList(args).contains("--proto")) {
129             dumpProto(prefix, fd, writer, args);
130             return;
131         }
132         writer.println(prefix + "Data Model:");
133         writer.print(prefix + " ---- workspace screens: ");
134         for (int i = 0; i < workspaceScreens.size(); i++) {
135             writer.print(" " + workspaceScreens.get(i).toString());
136         }
137         writer.println();
138         writer.println(prefix + " ---- workspace items ");
139         for (int i = 0; i < workspaceItems.size(); i++) {
140             writer.println(prefix + '\t' + workspaceItems.get(i).toString());
141         }
142         writer.println(prefix + " ---- appwidget items ");
143         for (int i = 0; i < appWidgets.size(); i++) {
144             writer.println(prefix + '\t' + appWidgets.get(i).toString());
145         }
146         writer.println(prefix + " ---- folder items ");
147         for (int i = 0; i< folders.size(); i++) {
148             writer.println(prefix + '\t' + folders.valueAt(i).toString());
149         }
150         writer.println(prefix + " ---- items id map ");
151         for (int i = 0; i< itemsIdMap.size(); i++) {
152             writer.println(prefix + '\t' + itemsIdMap.valueAt(i).toString());
153         }
154 
155         if (args.length > 0 && TextUtils.equals(args[0], "--all")) {
156             writer.println(prefix + "shortcuts");
157             for (ArrayList<String> map : deepShortcutMap.values()) {
158                 writer.print(prefix + "  ");
159                 for (String str : map) {
160                     writer.print(str + ", ");
161                 }
162                 writer.println();
163             }
164         }
165     }
166 
dumpProto(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)167     private synchronized void dumpProto(String prefix, FileDescriptor fd, PrintWriter writer,
168             String[] args) {
169 
170         // Add top parent nodes. (L1)
171         DumpTargetWrapper hotseat = new DumpTargetWrapper(ContainerType.HOTSEAT, 0);
172         LongArrayMap<DumpTargetWrapper> workspaces = new LongArrayMap<>();
173         for (int i = 0; i < workspaceScreens.size(); i++) {
174             workspaces.put(workspaceScreens.get(i),
175                     new DumpTargetWrapper(ContainerType.WORKSPACE, i));
176         }
177         DumpTargetWrapper dtw;
178         // Add non leaf / non top nodes (L2)
179         for (int i = 0; i < folders.size(); i++) {
180             FolderInfo fInfo = folders.valueAt(i);
181             dtw = new DumpTargetWrapper(ContainerType.FOLDER, folders.size());
182             dtw.writeToDumpTarget(fInfo);
183             for(ShortcutInfo sInfo: fInfo.contents) {
184                 DumpTargetWrapper child = new DumpTargetWrapper(sInfo);
185                 child.writeToDumpTarget(sInfo);
186                 dtw.add(child);
187             }
188             if (fInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
189                 hotseat.add(dtw);
190             } else if (fInfo.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
191                 workspaces.get(fInfo.screenId).add(dtw);
192             }
193         }
194         // Add leaf nodes (L3): *Info
195         for (int i = 0; i < workspaceItems.size(); i++) {
196             ItemInfo info = workspaceItems.get(i);
197             if (info instanceof FolderInfo) {
198                 continue;
199             }
200             dtw = new DumpTargetWrapper(info);
201             dtw.writeToDumpTarget(info);
202             if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
203                 hotseat.add(dtw);
204             } else if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
205                 workspaces.get(info.screenId).add(dtw);
206             }
207         }
208         for (int i = 0; i < appWidgets.size(); i++) {
209             ItemInfo info = appWidgets.get(i);
210             dtw = new DumpTargetWrapper(info);
211             dtw.writeToDumpTarget(info);
212             if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
213                 hotseat.add(dtw);
214             } else if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
215                 workspaces.get(info.screenId).add(dtw);
216             }
217         }
218 
219 
220         // Traverse target wrapper
221         ArrayList<DumpTarget> targetList = new ArrayList<>();
222         targetList.addAll(hotseat.getFlattenedList());
223         for (int i = 0; i < workspaces.size(); i++) {
224             targetList.addAll(workspaces.valueAt(i).getFlattenedList());
225         }
226 
227         if (Arrays.asList(args).contains("--debug")) {
228             for (int i = 0; i < targetList.size(); i++) {
229                 writer.println(prefix + DumpTargetWrapper.getDumpTargetStr(targetList.get(i)));
230             }
231             return;
232         } else {
233             LauncherDumpProto.LauncherImpression proto = new LauncherDumpProto.LauncherImpression();
234             proto.targets = new DumpTarget[targetList.size()];
235             for (int i = 0; i < targetList.size(); i++) {
236                 proto.targets[i] = targetList.get(i);
237             }
238             FileOutputStream fos = new FileOutputStream(fd);
239             try {
240 
241                 fos.write(MessageNano.toByteArray(proto));
242                 Log.d(TAG, MessageNano.toByteArray(proto).length + "Bytes");
243             } catch (IOException e) {
244                 Log.e(TAG, "Exception writing dumpsys --proto", e);
245             }
246         }
247     }
248 
removeItem(Context context, ItemInfo... items)249     public synchronized void removeItem(Context context, ItemInfo... items) {
250         removeItem(context, Arrays.asList(items));
251     }
252 
removeItem(Context context, Iterable<? extends ItemInfo> items)253     public synchronized void removeItem(Context context, Iterable<? extends ItemInfo> items) {
254         for (ItemInfo item : items) {
255             switch (item.itemType) {
256                 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
257                     folders.remove(item.id);
258                     if (FeatureFlags.IS_DOGFOOD_BUILD) {
259                         for (ItemInfo info : itemsIdMap) {
260                             if (info.container == item.id) {
261                                 // We are deleting a folder which still contains items that
262                                 // think they are contained by that folder.
263                                 String msg = "deleting a folder (" + item + ") which still " +
264                                         "contains items (" + info + ")";
265                                 Log.e(TAG, msg);
266                             }
267                         }
268                     }
269                     workspaceItems.remove(item);
270                     break;
271                 case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: {
272                     // Decrement pinned shortcut count
273                     ShortcutKey pinnedShortcut = ShortcutKey.fromItemInfo(item);
274                     MutableInt count = pinnedShortcutCounts.get(pinnedShortcut);
275                     if ((count == null || --count.value == 0)
276                             && !InstallShortcutReceiver.getPendingShortcuts(context)
277                                 .contains(pinnedShortcut)) {
278                         DeepShortcutManager.getInstance(context).unpinShortcut(pinnedShortcut);
279                     }
280                     // Fall through.
281                 }
282                 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
283                 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
284                     workspaceItems.remove(item);
285                     break;
286                 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
287                 case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
288                     appWidgets.remove(item);
289                     break;
290             }
291             itemsIdMap.remove(item.id);
292         }
293     }
294 
addItem(Context context, ItemInfo item, boolean newItem)295     public synchronized void addItem(Context context, ItemInfo item, boolean newItem) {
296         itemsIdMap.put(item.id, item);
297         switch (item.itemType) {
298             case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
299                 folders.put(item.id, (FolderInfo) item);
300                 workspaceItems.add(item);
301                 break;
302             case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: {
303                 // Increment the count for the given shortcut
304                 ShortcutKey pinnedShortcut = ShortcutKey.fromItemInfo(item);
305                 MutableInt count = pinnedShortcutCounts.get(pinnedShortcut);
306                 if (count == null) {
307                     count = new MutableInt(1);
308                     pinnedShortcutCounts.put(pinnedShortcut, count);
309                 } else {
310                     count.value++;
311                 }
312 
313                 // Since this is a new item, pin the shortcut in the system server.
314                 if (newItem && count.value == 1) {
315                     DeepShortcutManager.getInstance(context).pinShortcut(pinnedShortcut);
316                 }
317                 // Fall through
318             }
319             case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
320             case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
321                 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
322                         item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
323                     workspaceItems.add(item);
324                 } else {
325                     if (newItem) {
326                         if (!folders.containsKey(item.container)) {
327                             // Adding an item to a folder that doesn't exist.
328                             String msg = "adding item: " + item + " to a folder that " +
329                                     " doesn't exist";
330                             Log.e(TAG, msg);
331                         }
332                     } else {
333                         findOrMakeFolder(item.container).add((ShortcutInfo) item, false);
334                     }
335 
336                 }
337                 break;
338             case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
339             case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
340                 appWidgets.add((LauncherAppWidgetInfo) item);
341                 break;
342         }
343     }
344 
345     /**
346      * Return an existing FolderInfo object if we have encountered this ID previously,
347      * or make a new one.
348      */
findOrMakeFolder(long id)349     public synchronized FolderInfo findOrMakeFolder(long id) {
350         // See if a placeholder was created for us already
351         FolderInfo folderInfo = folders.get(id);
352         if (folderInfo == null) {
353             // No placeholder -- create a new instance
354             folderInfo = new FolderInfo();
355             folders.put(id, folderInfo);
356         }
357         return folderInfo;
358     }
359 
360     /**
361      * Clear all the deep shortcuts for the given package, and re-add the new shortcuts.
362      */
updateDeepShortcutMap( String packageName, UserHandle user, List<ShortcutInfoCompat> shortcuts)363     public synchronized void updateDeepShortcutMap(
364             String packageName, UserHandle user, List<ShortcutInfoCompat> shortcuts) {
365         if (packageName != null) {
366             Iterator<ComponentKey> keysIter = deepShortcutMap.keySet().iterator();
367             while (keysIter.hasNext()) {
368                 ComponentKey next = keysIter.next();
369                 if (next.componentName.getPackageName().equals(packageName)
370                         && next.user.equals(user)) {
371                     keysIter.remove();
372                 }
373             }
374         }
375 
376         // Now add the new shortcuts to the map.
377         for (ShortcutInfoCompat shortcut : shortcuts) {
378             boolean shouldShowInContainer = shortcut.isEnabled()
379                     && (shortcut.isDeclaredInManifest() || shortcut.isDynamic());
380             if (shouldShowInContainer) {
381                 ComponentKey targetComponent
382                         = new ComponentKey(shortcut.getActivity(), shortcut.getUserHandle());
383                 deepShortcutMap.addToList(targetComponent, shortcut.getId());
384             }
385         }
386     }
387 }
388