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