• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 package com.android.launcher3.model;
3 
4 import android.appwidget.AppWidgetProviderInfo;
5 import android.content.Context;
6 import android.content.pm.PackageManager;
7 import android.os.Process;
8 import android.os.UserHandle;
9 import android.support.annotation.Nullable;
10 import android.util.Log;
11 
12 import com.android.launcher3.AppFilter;
13 import com.android.launcher3.IconCache;
14 import com.android.launcher3.InvariantDeviceProfile;
15 import com.android.launcher3.LauncherAppState;
16 import com.android.launcher3.LauncherAppWidgetProviderInfo;
17 import com.android.launcher3.Utilities;
18 import com.android.launcher3.compat.AppWidgetManagerCompat;
19 import com.android.launcher3.compat.LauncherAppsCompat;
20 import com.android.launcher3.compat.ShortcutConfigActivityInfo;
21 import com.android.launcher3.config.ProviderConfig;
22 import com.android.launcher3.util.MultiHashMap;
23 import com.android.launcher3.util.PackageUserKey;
24 import com.android.launcher3.util.Preconditions;
25 
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.Iterator;
29 
30 /**
31  * Widgets data model that is used by the adapters of the widget views and controllers.
32  *
33  * <p> The widgets and shortcuts are organized using package name as its index.
34  */
35 public class WidgetsModel {
36 
37     private static final String TAG = "WidgetsModel";
38     private static final boolean DEBUG = false;
39 
40     /* Map of widgets and shortcuts that are tracked per package. */
41     private final MultiHashMap<PackageItemInfo, WidgetItem> mWidgetsList;
42 
43     private final IconCache mIconCache;
44     private final AppFilter mAppFilter;
45 
WidgetsModel(IconCache iconCache, AppFilter appFilter)46     public WidgetsModel(IconCache iconCache, AppFilter appFilter) {
47         mIconCache = iconCache;
48         mAppFilter = appFilter;
49         mWidgetsList = new MultiHashMap<>();
50     }
51 
getWidgetsMap()52     public MultiHashMap<PackageItemInfo, WidgetItem> getWidgetsMap() {
53         return mWidgetsList;
54     }
55 
isEmpty()56     public boolean isEmpty() {
57         return mWidgetsList.isEmpty();
58     }
59 
60     /**
61      * @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
62      *                    only widgets and shortcuts associated with the package/user are.
63      */
update(Context context, @Nullable PackageUserKey packageUser)64     public ArrayList<WidgetItem> update(Context context, @Nullable PackageUserKey packageUser) {
65         Preconditions.assertWorkerThread();
66 
67         final ArrayList<WidgetItem> widgetsAndShortcuts = new ArrayList<>();
68         try {
69             PackageManager pm = context.getPackageManager();
70             InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
71 
72             // Widgets
73             AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(context);
74             for (AppWidgetProviderInfo widgetInfo : widgetManager.getAllProviders(packageUser)) {
75                 widgetsAndShortcuts.add(new WidgetItem(LauncherAppWidgetProviderInfo
76                         .fromProviderInfo(context, widgetInfo), pm, idp));
77             }
78 
79             // Shortcuts
80             for (ShortcutConfigActivityInfo info : LauncherAppsCompat.getInstance(context)
81                     .getCustomShortcutActivityList(packageUser)) {
82                 widgetsAndShortcuts.add(new WidgetItem(info));
83             }
84             setWidgetsAndShortcuts(widgetsAndShortcuts, context, packageUser);
85         } catch (Exception e) {
86             if (!ProviderConfig.IS_DOGFOOD_BUILD && Utilities.isBinderSizeError(e)) {
87                 // the returned value may be incomplete and will not be refreshed until the next
88                 // time Launcher starts.
89                 // TODO: after figuring out a repro step, introduce a dirty bit to check when
90                 // onResume is called to refresh the widget provider list.
91             } else {
92                 throw e;
93             }
94         }
95         return widgetsAndShortcuts;
96     }
97 
setWidgetsAndShortcuts(ArrayList<WidgetItem> rawWidgetsShortcuts, Context context, @Nullable PackageUserKey packageUser)98     private void setWidgetsAndShortcuts(ArrayList<WidgetItem> rawWidgetsShortcuts,
99             Context context, @Nullable PackageUserKey packageUser) {
100         if (DEBUG) {
101             Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + rawWidgetsShortcuts.size());
102         }
103 
104         // Temporary list for {@link PackageItemInfos} to avoid having to go through
105         // {@link mPackageItemInfos} to locate the key to be used for {@link #mWidgetsList}
106         HashMap<String, PackageItemInfo> tmpPackageItemInfos = new HashMap<>();
107 
108         // clear the lists.
109         if (packageUser == null) {
110             mWidgetsList.clear();
111         } else {
112             // Only clear the widgets for the given package/user.
113             PackageItemInfo packageItem = null;
114             for (PackageItemInfo item : mWidgetsList.keySet()) {
115                 if (item.packageName.equals(packageUser.mPackageName)) {
116                     packageItem = item;
117                     break;
118                 }
119             }
120             if (packageItem != null) {
121                 // We want to preserve the user that was on the packageItem previously,
122                 // so add it to tmpPackageItemInfos here to avoid creating a new entry.
123                 tmpPackageItemInfos.put(packageItem.packageName, packageItem);
124 
125                 Iterator<WidgetItem> widgetItemIterator = mWidgetsList.get(packageItem).iterator();
126                 while (widgetItemIterator.hasNext()) {
127                     WidgetItem nextWidget = widgetItemIterator.next();
128                     if (nextWidget.componentName.getPackageName().equals(packageUser.mPackageName)
129                             && nextWidget.user.equals(packageUser.mUser)) {
130                         widgetItemIterator.remove();
131                     }
132                 }
133             }
134         }
135 
136         InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
137         UserHandle myUser = Process.myUserHandle();
138 
139         // add and update.
140         for (WidgetItem item : rawWidgetsShortcuts) {
141             if (item.widgetInfo != null) {
142                 // Ensure that all widgets we show can be added on a workspace of this size
143                 int minSpanX = Math.min(item.widgetInfo.spanX, item.widgetInfo.minSpanX);
144                 int minSpanY = Math.min(item.widgetInfo.spanY, item.widgetInfo.minSpanY);
145                 if (minSpanX > idp.numColumns || minSpanY > idp.numRows) {
146                     if (DEBUG) {
147                         Log.d(TAG, String.format(
148                                 "Widget %s : (%d X %d) can't fit on this device",
149                                 item.componentName, minSpanX, minSpanY));
150                     }
151                     continue;
152                 }
153             }
154 
155             if (!mAppFilter.shouldShowApp(item.componentName)) {
156                 if (DEBUG) {
157                     Log.d(TAG, String.format("%s is filtered and not added to the widget tray.",
158                             item.componentName));
159                 }
160                 continue;
161             }
162 
163             String packageName = item.componentName.getPackageName();
164             PackageItemInfo pInfo = tmpPackageItemInfos.get(packageName);
165             if (pInfo == null) {
166                 pInfo = new PackageItemInfo(packageName);
167                 pInfo.user = item.user;
168                 tmpPackageItemInfos.put(packageName,  pInfo);
169             } else if (!myUser.equals(pInfo.user)) {
170                 // Keep updating the user, until we get the primary user.
171                 pInfo.user = item.user;
172             }
173             mWidgetsList.addToList(pInfo, item);
174         }
175 
176         // Update each package entry
177         for (PackageItemInfo p : tmpPackageItemInfos.values()) {
178             mIconCache.getTitleAndIconForApp(p, true /* userLowResIcon */);
179         }
180     }
181 }