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.FeatureFlags; 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 = new MultiHashMap<>(); 42 43 private AppFilter mAppFilter; 44 45 public synchronized MultiHashMap<PackageItemInfo, WidgetItem> getWidgetsMap() { 46 return mWidgetsList.clone(); 47 } 48 49 /** 50 * @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise 51 * only widgets and shortcuts associated with the package/user are. 52 */ 53 public void update(LauncherAppState app, @Nullable PackageUserKey packageUser) { 54 Preconditions.assertWorkerThread(); 55 56 Context context = app.getContext(); 57 final ArrayList<WidgetItem> widgetsAndShortcuts = new ArrayList<>(); 58 try { 59 PackageManager pm = context.getPackageManager(); 60 InvariantDeviceProfile idp = app.getInvariantDeviceProfile(); 61 62 // Widgets 63 AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(context); 64 for (AppWidgetProviderInfo widgetInfo : widgetManager.getAllProviders(packageUser)) { 65 widgetsAndShortcuts.add(new WidgetItem(LauncherAppWidgetProviderInfo 66 .fromProviderInfo(context, widgetInfo), pm, idp)); 67 } 68 69 // Shortcuts 70 for (ShortcutConfigActivityInfo info : LauncherAppsCompat.getInstance(context) 71 .getCustomShortcutActivityList(packageUser)) { 72 widgetsAndShortcuts.add(new WidgetItem(info)); 73 } 74 setWidgetsAndShortcuts(widgetsAndShortcuts, app, packageUser); 75 } catch (Exception e) { 76 if (!FeatureFlags.IS_DOGFOOD_BUILD && Utilities.isBinderSizeError(e)) { 77 // the returned value may be incomplete and will not be refreshed until the next 78 // time Launcher starts. 79 // TODO: after figuring out a repro step, introduce a dirty bit to check when 80 // onResume is called to refresh the widget provider list. 81 } else { 82 throw e; 83 } 84 } 85 86 app.getWidgetCache().removeObsoletePreviews(widgetsAndShortcuts, packageUser); 87 } 88 89 private synchronized void setWidgetsAndShortcuts(ArrayList<WidgetItem> rawWidgetsShortcuts, 90 LauncherAppState app, @Nullable PackageUserKey packageUser) { 91 if (DEBUG) { 92 Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + rawWidgetsShortcuts.size()); 93 } 94 95 // Temporary list for {@link PackageItemInfos} to avoid having to go through 96 // {@link mPackageItemInfos} to locate the key to be used for {@link #mWidgetsList} 97 HashMap<String, PackageItemInfo> tmpPackageItemInfos = new HashMap<>(); 98 99 // clear the lists. 100 if (packageUser == null) { 101 mWidgetsList.clear(); 102 } else { 103 // Only clear the widgets for the given package/user. 104 PackageItemInfo packageItem = null; 105 for (PackageItemInfo item : mWidgetsList.keySet()) { 106 if (item.packageName.equals(packageUser.mPackageName)) { 107 packageItem = item; 108 break; 109 } 110 } 111 if (packageItem != null) { 112 // We want to preserve the user that was on the packageItem previously, 113 // so add it to tmpPackageItemInfos here to avoid creating a new entry. 114 tmpPackageItemInfos.put(packageItem.packageName, packageItem); 115 116 Iterator<WidgetItem> widgetItemIterator = mWidgetsList.get(packageItem).iterator(); 117 while (widgetItemIterator.hasNext()) { 118 WidgetItem nextWidget = widgetItemIterator.next(); 119 if (nextWidget.componentName.getPackageName().equals(packageUser.mPackageName) 120 && nextWidget.user.equals(packageUser.mUser)) { 121 widgetItemIterator.remove(); 122 } 123 } 124 } 125 } 126 127 InvariantDeviceProfile idp = app.getInvariantDeviceProfile(); 128 UserHandle myUser = Process.myUserHandle(); 129 130 // add and update. 131 for (WidgetItem item : rawWidgetsShortcuts) { 132 if (item.widgetInfo != null) { 133 // Ensure that all widgets we show can be added on a workspace of this size 134 int minSpanX = Math.min(item.widgetInfo.spanX, item.widgetInfo.minSpanX); 135 int minSpanY = Math.min(item.widgetInfo.spanY, item.widgetInfo.minSpanY); 136 if (minSpanX > idp.numColumns || minSpanY > idp.numRows) { 137 if (DEBUG) { 138 Log.d(TAG, String.format( 139 "Widget %s : (%d X %d) can't fit on this device", 140 item.componentName, minSpanX, minSpanY)); 141 } 142 continue; 143 } 144 } 145 146 if (mAppFilter == null) { 147 mAppFilter = AppFilter.newInstance(app.getContext()); 148 } 149 if (!mAppFilter.shouldShowApp(item.componentName)) { 150 if (DEBUG) { 151 Log.d(TAG, String.format("%s is filtered and not added to the widget tray.", 152 item.componentName)); 153 } 154 continue; 155 } 156 157 String packageName = item.componentName.getPackageName(); 158 PackageItemInfo pInfo = tmpPackageItemInfos.get(packageName); 159 if (pInfo == null) { 160 pInfo = new PackageItemInfo(packageName); 161 pInfo.user = item.user; 162 tmpPackageItemInfos.put(packageName, pInfo); 163 } else if (!myUser.equals(pInfo.user)) { 164 // Keep updating the user, until we get the primary user. 165 pInfo.user = item.user; 166 } 167 mWidgetsList.addToList(pInfo, item); 168 } 169 170 // Update each package entry 171 IconCache iconCache = app.getIconCache(); 172 for (PackageItemInfo p : tmpPackageItemInfos.values()) { 173 iconCache.getTitleAndIconForApp(p, true /* userLowResIcon */); 174 } 175 } 176 }