Home | History | Annotate | Download | only in model
      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 }