• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 package com.android.launcher3.model;
3 
4 import android.appwidget.AppWidgetProviderInfo;
5 import android.content.ComponentName;
6 import android.content.Context;
7 import android.content.Intent;
8 import android.content.pm.ResolveInfo;
9 import android.os.DeadObjectException;
10 import android.os.TransactionTooLargeException;
11 import android.util.Log;
12 
13 import com.android.launcher3.AppFilter;
14 import com.android.launcher3.IconCache;
15 import com.android.launcher3.InvariantDeviceProfile;
16 import com.android.launcher3.ItemInfo;
17 import com.android.launcher3.LauncherAppState;
18 import com.android.launcher3.LauncherAppWidgetProviderInfo;
19 import com.android.launcher3.Utilities;
20 import com.android.launcher3.compat.AlphabeticIndexCompat;
21 import com.android.launcher3.compat.AppWidgetManagerCompat;
22 import com.android.launcher3.compat.UserHandleCompat;
23 import com.android.launcher3.config.ProviderConfig;
24 
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.Comparator;
28 import java.util.HashMap;
29 import java.util.List;
30 
31 /**
32  * Widgets data model that is used by the adapters of the widget views and controllers.
33  *
34  * <p> The widgets and shortcuts are organized using package name as its index.
35  */
36 public class WidgetsModel {
37 
38     private static final String TAG = "WidgetsModel";
39     private static final boolean DEBUG = false;
40 
41     /* List of packages that is tracked by this model. */
42     private final ArrayList<PackageItemInfo> mPackageItemInfos;
43 
44     /* Map of widgets and shortcuts that are tracked per package. */
45     private final HashMap<PackageItemInfo, ArrayList<Object>> mWidgetsList;
46 
47     private final AppWidgetManagerCompat mAppWidgetMgr;
48     private final WidgetsAndShortcutNameComparator mWidgetAndShortcutNameComparator;
49     private final Comparator<ItemInfo> mAppNameComparator;
50     private final IconCache mIconCache;
51     private final AppFilter mAppFilter;
52     private final AlphabeticIndexCompat mIndexer;
53 
54     private ArrayList<Object> mRawList;
55 
WidgetsModel(Context context, IconCache iconCache, AppFilter appFilter)56     public WidgetsModel(Context context,  IconCache iconCache, AppFilter appFilter) {
57         mAppWidgetMgr = AppWidgetManagerCompat.getInstance(context);
58         mWidgetAndShortcutNameComparator = new WidgetsAndShortcutNameComparator(context);
59         mAppNameComparator = (new AppNameComparator(context)).getAppInfoComparator();
60         mIconCache = iconCache;
61         mAppFilter = appFilter;
62         mIndexer = new AlphabeticIndexCompat(context);
63         mPackageItemInfos = new ArrayList<>();
64         mWidgetsList = new HashMap<>();
65 
66         mRawList = new ArrayList<>();
67     }
68 
69     @SuppressWarnings("unchecked")
WidgetsModel(WidgetsModel model)70     private WidgetsModel(WidgetsModel model) {
71         mAppWidgetMgr = model.mAppWidgetMgr;
72         mPackageItemInfos = (ArrayList<PackageItemInfo>) model.mPackageItemInfos.clone();
73         mWidgetsList = (HashMap<PackageItemInfo, ArrayList<Object>>) model.mWidgetsList.clone();
74         mWidgetAndShortcutNameComparator = model.mWidgetAndShortcutNameComparator;
75         mAppNameComparator = model.mAppNameComparator;
76         mIconCache = model.mIconCache;
77         mAppFilter = model.mAppFilter;
78         mIndexer = model.mIndexer;
79         mRawList = (ArrayList<Object>) model.mRawList.clone();
80     }
81 
82     // Access methods that may be deleted if the private fields are made package-private.
getPackageSize()83     public int getPackageSize() {
84         return mPackageItemInfos.size();
85     }
86 
87     // Access methods that may be deleted if the private fields are made package-private.
getPackageItemInfo(int pos)88     public PackageItemInfo getPackageItemInfo(int pos) {
89         if (pos >= mPackageItemInfos.size() || pos < 0) {
90             return null;
91         }
92         return mPackageItemInfos.get(pos);
93     }
94 
getSortedWidgets(int pos)95     public List<Object> getSortedWidgets(int pos) {
96         return mWidgetsList.get(mPackageItemInfos.get(pos));
97     }
98 
getRawList()99     public ArrayList<Object> getRawList() {
100         return mRawList;
101     }
102 
isEmpty()103     public boolean isEmpty() {
104         return mRawList.isEmpty();
105     }
106 
updateAndClone(Context context)107     public WidgetsModel updateAndClone(Context context) {
108         Utilities.assertWorkerThread();
109 
110         try {
111             final ArrayList<Object> widgetsAndShortcuts = new ArrayList<>();
112             // Widgets
113             for (AppWidgetProviderInfo widgetInfo :
114                     AppWidgetManagerCompat.getInstance(context).getAllProviders()) {
115                 widgetsAndShortcuts.add(LauncherAppWidgetProviderInfo
116                         .fromProviderInfo(context, widgetInfo));
117             }
118             // Shortcuts
119             widgetsAndShortcuts.addAll(context.getPackageManager().queryIntentActivities(
120                     new Intent(Intent.ACTION_CREATE_SHORTCUT), 0));
121             setWidgetsAndShortcuts(widgetsAndShortcuts);
122         } catch (Exception e) {
123             if (!LauncherAppState.isDogfoodBuild() &&
124                     (e.getCause() instanceof TransactionTooLargeException ||
125                             e.getCause() instanceof DeadObjectException)) {
126                 // the returned value may be incomplete and will not be refreshed until the next
127                 // time Launcher starts.
128                 // TODO: after figuring out a repro step, introduce a dirty bit to check when
129                 // onResume is called to refresh the widget provider list.
130             } else {
131                 throw e;
132             }
133         }
134         return clone();
135     }
136 
setWidgetsAndShortcuts(ArrayList<Object> rawWidgetsShortcuts)137     private void setWidgetsAndShortcuts(ArrayList<Object> rawWidgetsShortcuts) {
138         mRawList = rawWidgetsShortcuts;
139         if (DEBUG) {
140             Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + rawWidgetsShortcuts.size());
141         }
142 
143         // Temporary list for {@link PackageItemInfos} to avoid having to go through
144         // {@link mPackageItemInfos} to locate the key to be used for {@link #mWidgetsList}
145         HashMap<String, PackageItemInfo> tmpPackageItemInfos = new HashMap<>();
146 
147         // clear the lists.
148         mWidgetsList.clear();
149         mPackageItemInfos.clear();
150         mWidgetAndShortcutNameComparator.reset();
151 
152         InvariantDeviceProfile idp = LauncherAppState.getInstance().getInvariantDeviceProfile();
153 
154         // add and update.
155         for (Object o: rawWidgetsShortcuts) {
156             String packageName = "";
157             UserHandleCompat userHandle = null;
158             ComponentName componentName = null;
159             if (o instanceof LauncherAppWidgetProviderInfo) {
160                 LauncherAppWidgetProviderInfo widgetInfo = (LauncherAppWidgetProviderInfo) o;
161 
162                 // Ensure that all widgets we show can be added on a workspace of this size
163                 int minSpanX = Math.min(widgetInfo.spanX, widgetInfo.minSpanX);
164                 int minSpanY = Math.min(widgetInfo.spanY, widgetInfo.minSpanY);
165                 if (minSpanX <= (int) idp.numColumns &&
166                     minSpanY <= (int) idp.numRows) {
167                     componentName = widgetInfo.provider;
168                     packageName = widgetInfo.provider.getPackageName();
169                     userHandle = mAppWidgetMgr.getUser(widgetInfo);
170                 } else {
171                     if (DEBUG) {
172                         Log.d(TAG, String.format(
173                                 "Widget %s : (%d X %d) can't fit on this device",
174                                 widgetInfo.provider, minSpanX, minSpanY));
175                     }
176                     continue;
177                 }
178             } else if (o instanceof ResolveInfo) {
179                 ResolveInfo resolveInfo = (ResolveInfo) o;
180                 componentName = new ComponentName(resolveInfo.activityInfo.packageName,
181                         resolveInfo.activityInfo.name);
182                 packageName = resolveInfo.activityInfo.packageName;
183                 userHandle = UserHandleCompat.myUserHandle();
184             }
185 
186             if (componentName == null || userHandle == null) {
187                 Log.e(TAG, String.format("Widget cannot be set for %s.", o.getClass().toString()));
188                 continue;
189             }
190             if (mAppFilter != null && !mAppFilter.shouldShowApp(componentName)) {
191                 if (DEBUG) {
192                     Log.d(TAG, String.format("%s is filtered and not added to the widget tray.",
193                         packageName));
194                 }
195                 continue;
196             }
197 
198             PackageItemInfo pInfo = tmpPackageItemInfos.get(packageName);
199             ArrayList<Object> widgetsShortcutsList = mWidgetsList.get(pInfo);
200             if (widgetsShortcutsList != null) {
201                 widgetsShortcutsList.add(o);
202             } else {
203                 widgetsShortcutsList = new ArrayList<>();
204                 widgetsShortcutsList.add(o);
205                 pInfo = new PackageItemInfo(packageName);
206                 mIconCache.getTitleAndIconForApp(packageName, userHandle,
207                         true /* userLowResIcon */, pInfo);
208                 pInfo.titleSectionName = mIndexer.computeSectionName(pInfo.title);
209                 mWidgetsList.put(pInfo, widgetsShortcutsList);
210                 tmpPackageItemInfos.put(packageName,  pInfo);
211                 mPackageItemInfos.add(pInfo);
212             }
213         }
214 
215         // sort.
216         Collections.sort(mPackageItemInfos, mAppNameComparator);
217         for (PackageItemInfo p: mPackageItemInfos) {
218             Collections.sort(mWidgetsList.get(p), mWidgetAndShortcutNameComparator);
219         }
220     }
221 
222     /**
223      * Create a snapshot of the widgets model.
224      * <p>
225      * Usage case: view binding without being modified from package updates.
226      */
227     @Override
clone()228     public WidgetsModel clone(){
229         return new WidgetsModel(this);
230     }
231 }