2015-04-08 19:01:34 -07:00
|
|
|
|
2015-05-21 13:04:53 -07:00
|
|
|
package com.android.launcher3.model;
|
2015-04-08 19:01:34 -07:00
|
|
|
|
2018-03-05 19:39:21 +00:00
|
|
|
import static android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_HIDE_FROM_PICKER;
|
|
|
|
|
|
2019-10-02 16:13:34 -07:00
|
|
|
import static com.android.launcher3.pm.ShortcutConfigActivityInfo.queryList;
|
|
|
|
|
|
2021-03-02 21:26:00 +00:00
|
|
|
import static java.util.stream.Collectors.toList;
|
|
|
|
|
|
2016-03-03 16:58:55 -08:00
|
|
|
import android.appwidget.AppWidgetProviderInfo;
|
2020-02-18 11:52:53 -08:00
|
|
|
import android.content.ComponentName;
|
2015-04-08 19:01:34 -07:00
|
|
|
import android.content.Context;
|
2016-03-10 12:02:29 -08:00
|
|
|
import android.content.pm.PackageManager;
|
2016-12-15 15:53:17 -08:00
|
|
|
import android.os.UserHandle;
|
2015-04-08 19:01:34 -07:00
|
|
|
import android.util.Log;
|
|
|
|
|
|
2019-10-02 16:13:34 -07:00
|
|
|
import androidx.annotation.Nullable;
|
|
|
|
|
|
2015-06-17 21:12:44 -07:00
|
|
|
import com.android.launcher3.AppFilter;
|
2015-08-03 13:05:01 -07:00
|
|
|
import com.android.launcher3.InvariantDeviceProfile;
|
|
|
|
|
import com.android.launcher3.LauncherAppState;
|
2016-11-04 10:19:58 -07:00
|
|
|
import com.android.launcher3.Utilities;
|
2017-10-10 15:21:15 -07:00
|
|
|
import com.android.launcher3.compat.AlphabeticIndexCompat;
|
2017-03-06 16:56:39 -08:00
|
|
|
import com.android.launcher3.config.FeatureFlags;
|
2020-02-06 11:28:01 -08:00
|
|
|
import com.android.launcher3.icons.ComponentWithLabelAndIcon;
|
2019-09-11 16:51:50 -07:00
|
|
|
import com.android.launcher3.icons.IconCache;
|
2020-04-06 15:11:17 -07:00
|
|
|
import com.android.launcher3.model.data.PackageItemInfo;
|
2019-10-02 16:13:34 -07:00
|
|
|
import com.android.launcher3.pm.ShortcutConfigActivityInfo;
|
2017-03-29 15:30:43 -07:00
|
|
|
import com.android.launcher3.util.PackageUserKey;
|
2016-04-15 18:03:27 -07:00
|
|
|
import com.android.launcher3.util.Preconditions;
|
2021-02-22 14:03:44 +00:00
|
|
|
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
|
2019-12-10 12:19:13 -08:00
|
|
|
import com.android.launcher3.widget.WidgetManagerHelper;
|
2021-02-08 17:18:25 +00:00
|
|
|
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
|
|
|
|
|
import com.android.launcher3.widget.model.WidgetsListContentEntry;
|
2021-02-10 17:10:15 +00:00
|
|
|
import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
|
2021-02-08 17:18:25 +00:00
|
|
|
import com.android.launcher3.widget.picker.WidgetsDiffReporter;
|
2015-04-08 19:01:34 -07:00
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
2021-04-08 22:32:57 +01:00
|
|
|
import java.util.Arrays;
|
2015-04-08 19:01:34 -07:00
|
|
|
import java.util.HashMap;
|
2018-09-26 12:00:30 -07:00
|
|
|
import java.util.List;
|
2017-10-10 15:21:15 -07:00
|
|
|
import java.util.Map;
|
2018-09-26 12:00:30 -07:00
|
|
|
import java.util.Map.Entry;
|
|
|
|
|
import java.util.Set;
|
2020-08-22 02:09:42 -07:00
|
|
|
import java.util.function.Predicate;
|
|
|
|
|
import java.util.stream.Collectors;
|
2015-04-08 19:01:34 -07:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Widgets data model that is used by the adapters of the widget views and controllers.
|
|
|
|
|
*
|
|
|
|
|
* <p> The widgets and shortcuts are organized using package name as its index.
|
|
|
|
|
*/
|
|
|
|
|
public class WidgetsModel {
|
|
|
|
|
|
2019-09-11 16:51:50 -07:00
|
|
|
// True is the widget support is disabled.
|
|
|
|
|
public static final boolean GO_DISABLE_WIDGETS = false;
|
2020-08-19 17:00:20 -07:00
|
|
|
public static final boolean GO_DISABLE_NOTIFICATION_DOTS = false;
|
2019-09-11 16:51:50 -07:00
|
|
|
|
2015-04-08 19:01:34 -07:00
|
|
|
private static final String TAG = "WidgetsModel";
|
|
|
|
|
private static final boolean DEBUG = false;
|
|
|
|
|
|
2021-04-08 22:32:57 +01:00
|
|
|
private static final ComponentName CONVERSATION_WIDGET = ComponentName.createRelative(
|
|
|
|
|
"com.android.systemui", ".people.widget.PeopleSpaceWidgetProvider");
|
|
|
|
|
|
2015-04-08 19:01:34 -07:00
|
|
|
/* Map of widgets and shortcuts that are tracked per package. */
|
2020-08-22 02:09:42 -07:00
|
|
|
private final Map<PackageItemInfo, List<WidgetItem>> mWidgetsList = new HashMap<>();
|
2015-04-08 19:01:34 -07:00
|
|
|
|
2017-10-10 15:21:15 -07:00
|
|
|
/**
|
2021-02-08 17:18:25 +00:00
|
|
|
* Returns a list of {@link WidgetsListBaseEntry}. All {@link WidgetItem} in a single row
|
|
|
|
|
* are sorted (based on label and user), but the overall list of
|
|
|
|
|
* {@link WidgetsListBaseEntry}s is not sorted. This list is sorted at the UI when using
|
|
|
|
|
* {@link WidgetsDiffReporter}
|
2017-10-10 15:21:15 -07:00
|
|
|
*
|
2021-02-08 17:18:25 +00:00
|
|
|
* @see com.android.launcher3.widget.picker.WidgetsListAdapter#setWidgets(List)
|
2017-10-10 15:21:15 -07:00
|
|
|
*/
|
2021-03-02 21:26:00 +00:00
|
|
|
public synchronized ArrayList<WidgetsListBaseEntry> getWidgetsListForPicker(Context context) {
|
2021-02-08 17:18:25 +00:00
|
|
|
ArrayList<WidgetsListBaseEntry> result = new ArrayList<>();
|
2017-10-10 15:21:15 -07:00
|
|
|
AlphabeticIndexCompat indexer = new AlphabeticIndexCompat(context);
|
|
|
|
|
|
2020-08-22 02:09:42 -07:00
|
|
|
for (Map.Entry<PackageItemInfo, List<WidgetItem>> entry : mWidgetsList.entrySet()) {
|
2021-02-08 17:18:25 +00:00
|
|
|
PackageItemInfo pkgItem = entry.getKey();
|
2021-02-10 17:10:15 +00:00
|
|
|
List<WidgetItem> widgetItems = entry.getValue();
|
2021-02-08 17:18:25 +00:00
|
|
|
String sectionName = (pkgItem.title == null) ? "" :
|
|
|
|
|
indexer.computeSectionName(pkgItem.title);
|
2021-02-10 17:10:15 +00:00
|
|
|
result.add(new WidgetsListHeaderEntry(pkgItem, sectionName, widgetItems));
|
|
|
|
|
result.add(new WidgetsListContentEntry(pkgItem, sectionName, widgetItems));
|
2017-10-10 15:21:15 -07:00
|
|
|
}
|
|
|
|
|
return result;
|
2016-03-03 16:58:55 -08:00
|
|
|
}
|
|
|
|
|
|
2021-03-02 21:26:00 +00:00
|
|
|
/** Returns a mapping of packages to their widgets without static shortcuts. */
|
|
|
|
|
public synchronized Map<PackageUserKey, List<WidgetItem>> getAllWidgetsWithoutShortcuts() {
|
|
|
|
|
Map<PackageUserKey, List<WidgetItem>> packagesToWidgets = new HashMap<>();
|
|
|
|
|
mWidgetsList.forEach((packageItemInfo, widgetsAndShortcuts) -> {
|
|
|
|
|
List<WidgetItem> widgets = widgetsAndShortcuts.stream()
|
|
|
|
|
.filter(item -> item.widgetInfo != null)
|
|
|
|
|
.collect(toList());
|
|
|
|
|
if (widgets.size() > 0) {
|
|
|
|
|
packagesToWidgets.put(
|
|
|
|
|
new PackageUserKey(packageItemInfo.packageName, packageItemInfo.user),
|
|
|
|
|
widgets);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return packagesToWidgets;
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-29 15:30:43 -07:00
|
|
|
/**
|
|
|
|
|
* @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
|
|
|
|
|
* only widgets and shortcuts associated with the package/user are.
|
|
|
|
|
*/
|
2020-02-06 11:28:01 -08:00
|
|
|
public List<ComponentWithLabelAndIcon> update(
|
|
|
|
|
LauncherAppState app, @Nullable PackageUserKey packageUser) {
|
2016-04-15 18:03:27 -07:00
|
|
|
Preconditions.assertWorkerThread();
|
2016-03-03 16:58:55 -08:00
|
|
|
|
2017-06-02 13:46:55 -07:00
|
|
|
Context context = app.getContext();
|
2016-10-12 20:49:31 -07:00
|
|
|
final ArrayList<WidgetItem> widgetsAndShortcuts = new ArrayList<>();
|
2020-02-06 11:28:01 -08:00
|
|
|
List<ComponentWithLabelAndIcon> updatedItems = new ArrayList<>();
|
2016-03-03 16:58:55 -08:00
|
|
|
try {
|
2017-06-02 13:46:55 -07:00
|
|
|
InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
|
2018-09-26 12:00:30 -07:00
|
|
|
PackageManager pm = app.getContext().getPackageManager();
|
2016-12-16 15:04:51 -08:00
|
|
|
|
2016-03-03 16:58:55 -08:00
|
|
|
// Widgets
|
2019-12-10 12:19:13 -08:00
|
|
|
WidgetManagerHelper widgetManager = new WidgetManagerHelper(context);
|
2017-03-29 15:30:43 -07:00
|
|
|
for (AppWidgetProviderInfo widgetInfo : widgetManager.getAllProviders(packageUser)) {
|
2018-09-26 12:00:30 -07:00
|
|
|
LauncherAppWidgetProviderInfo launcherWidgetInfo =
|
|
|
|
|
LauncherAppWidgetProviderInfo.fromProviderInfo(context, widgetInfo);
|
|
|
|
|
|
|
|
|
|
widgetsAndShortcuts.add(new WidgetItem(
|
|
|
|
|
launcherWidgetInfo, idp, app.getIconCache()));
|
|
|
|
|
updatedItems.add(launcherWidgetInfo);
|
2016-03-03 16:58:55 -08:00
|
|
|
}
|
2016-03-10 12:02:29 -08:00
|
|
|
|
2016-03-03 16:58:55 -08:00
|
|
|
// Shortcuts
|
2019-10-02 16:13:34 -07:00
|
|
|
for (ShortcutConfigActivityInfo info :
|
|
|
|
|
queryList(context, packageUser)) {
|
2018-09-26 12:00:30 -07:00
|
|
|
widgetsAndShortcuts.add(new WidgetItem(info, app.getIconCache(), pm));
|
|
|
|
|
updatedItems.add(info);
|
2016-03-10 12:02:29 -08:00
|
|
|
}
|
2021-03-16 19:30:04 +00:00
|
|
|
setWidgetsAndShortcuts(widgetsAndShortcuts, app, packageUser);
|
2016-03-03 16:58:55 -08:00
|
|
|
} catch (Exception e) {
|
2020-02-14 14:15:13 -08:00
|
|
|
if (!FeatureFlags.IS_STUDIO_BUILD && Utilities.isBinderSizeError(e)) {
|
2016-03-03 16:58:55 -08:00
|
|
|
// the returned value may be incomplete and will not be refreshed until the next
|
|
|
|
|
// time Launcher starts.
|
|
|
|
|
// TODO: after figuring out a repro step, introduce a dirty bit to check when
|
|
|
|
|
// onResume is called to refresh the widget provider list.
|
|
|
|
|
} else {
|
|
|
|
|
throw e;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-06-02 13:46:55 -07:00
|
|
|
|
|
|
|
|
app.getWidgetCache().removeObsoletePreviews(widgetsAndShortcuts, packageUser);
|
2018-09-26 12:00:30 -07:00
|
|
|
return updatedItems;
|
2016-03-03 16:58:55 -08:00
|
|
|
}
|
|
|
|
|
|
2017-06-02 13:46:55 -07:00
|
|
|
private synchronized void setWidgetsAndShortcuts(ArrayList<WidgetItem> rawWidgetsShortcuts,
|
2021-03-16 19:30:04 +00:00
|
|
|
LauncherAppState app, @Nullable PackageUserKey packageUser) {
|
2015-04-08 19:01:34 -07:00
|
|
|
if (DEBUG) {
|
2015-05-21 13:04:53 -07:00
|
|
|
Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + rawWidgetsShortcuts.size());
|
2015-04-08 19:01:34 -07:00
|
|
|
}
|
|
|
|
|
|
2015-04-11 15:44:32 -07:00
|
|
|
// Temporary list for {@link PackageItemInfos} to avoid having to go through
|
|
|
|
|
// {@link mPackageItemInfos} to locate the key to be used for {@link #mWidgetsList}
|
2021-04-08 22:32:57 +01:00
|
|
|
HashMap<WidgetPackageOrCategoryKey, PackageItemInfo> tmpPackageItemInfos = new HashMap<>();
|
2015-04-14 11:50:44 -07:00
|
|
|
|
2021-03-16 19:30:04 +00:00
|
|
|
// Clear the lists only if this is an update on all widgets and shortcuts. If packageUser
|
|
|
|
|
// isn't null, only updates the shortcuts and widgets for the app represented in
|
|
|
|
|
// packageUser.
|
|
|
|
|
if (packageUser == null) {
|
|
|
|
|
mWidgetsList.clear();
|
|
|
|
|
}
|
2015-04-08 19:01:34 -07:00
|
|
|
// add and update.
|
2020-08-22 02:09:42 -07:00
|
|
|
mWidgetsList.putAll(rawWidgetsShortcuts.stream()
|
|
|
|
|
.filter(new WidgetValidityCheck(app))
|
|
|
|
|
.collect(Collectors.groupingBy(item -> {
|
2021-04-08 22:32:57 +01:00
|
|
|
WidgetPackageOrCategoryKey packageUserKey = getWidgetPackageOrCategoryKey(item);
|
2021-02-17 15:58:23 +00:00
|
|
|
PackageItemInfo pInfo = tmpPackageItemInfos.get(packageUserKey);
|
2020-08-22 02:09:42 -07:00
|
|
|
if (pInfo == null) {
|
2021-04-08 22:32:57 +01:00
|
|
|
pInfo = new PackageItemInfo(item.componentName.getPackageName(),
|
|
|
|
|
packageUserKey.mCategory);
|
2020-08-22 02:09:42 -07:00
|
|
|
pInfo.user = item.user;
|
2021-02-17 15:58:23 +00:00
|
|
|
tmpPackageItemInfos.put(packageUserKey, pInfo);
|
2015-08-03 13:05:01 -07:00
|
|
|
}
|
2020-08-22 02:09:42 -07:00
|
|
|
return pInfo;
|
|
|
|
|
})));
|
2015-04-08 19:01:34 -07:00
|
|
|
|
2016-03-10 12:02:29 -08:00
|
|
|
// Update each package entry
|
2017-06-02 13:46:55 -07:00
|
|
|
IconCache iconCache = app.getIconCache();
|
2016-10-12 20:49:31 -07:00
|
|
|
for (PackageItemInfo p : tmpPackageItemInfos.values()) {
|
2017-06-02 13:46:55 -07:00
|
|
|
iconCache.getTitleAndIconForApp(p, true /* userLowResIcon */);
|
2015-04-08 19:01:34 -07:00
|
|
|
}
|
2015-05-22 14:49:23 -07:00
|
|
|
}
|
2018-09-26 12:00:30 -07:00
|
|
|
|
|
|
|
|
public void onPackageIconsUpdated(Set<String> packageNames, UserHandle user,
|
|
|
|
|
LauncherAppState app) {
|
2020-08-22 02:09:42 -07:00
|
|
|
for (Entry<PackageItemInfo, List<WidgetItem>> entry : mWidgetsList.entrySet()) {
|
2018-09-26 12:00:30 -07:00
|
|
|
if (packageNames.contains(entry.getKey().packageName)) {
|
2020-08-22 02:09:42 -07:00
|
|
|
List<WidgetItem> items = entry.getValue();
|
2018-09-26 12:00:30 -07:00
|
|
|
int count = items.size();
|
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
|
WidgetItem item = items.get(i);
|
|
|
|
|
if (item.user.equals(user)) {
|
|
|
|
|
if (item.activityInfo != null) {
|
|
|
|
|
items.set(i, new WidgetItem(item.activityInfo, app.getIconCache(),
|
|
|
|
|
app.getContext().getPackageManager()));
|
|
|
|
|
} else {
|
|
|
|
|
items.set(i, new WidgetItem(item.widgetInfo,
|
|
|
|
|
app.getInvariantDeviceProfile(), app.getIconCache()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-02-18 11:52:53 -08:00
|
|
|
|
|
|
|
|
public WidgetItem getWidgetProviderInfoByProviderName(
|
|
|
|
|
ComponentName providerName) {
|
2020-08-22 02:09:42 -07:00
|
|
|
List<WidgetItem> widgetsList = mWidgetsList.get(
|
2020-02-18 11:52:53 -08:00
|
|
|
new PackageItemInfo(providerName.getPackageName()));
|
2020-02-24 13:50:29 -08:00
|
|
|
if (widgetsList == null) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-18 11:52:53 -08:00
|
|
|
for (WidgetItem item : widgetsList) {
|
|
|
|
|
if (item.componentName.equals(providerName)) {
|
|
|
|
|
return item;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2020-08-22 02:09:42 -07:00
|
|
|
|
2021-04-08 22:32:57 +01:00
|
|
|
private WidgetPackageOrCategoryKey getWidgetPackageOrCategoryKey(WidgetItem item) {
|
|
|
|
|
if (CONVERSATION_WIDGET.equals(item.componentName)) {
|
|
|
|
|
return new WidgetPackageOrCategoryKey(PackageItemInfo.CONVERSATIONS, item.user);
|
|
|
|
|
}
|
|
|
|
|
return new WidgetPackageOrCategoryKey(item.componentName.getPackageName(), item.user);
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-22 02:09:42 -07:00
|
|
|
private static class WidgetValidityCheck implements Predicate<WidgetItem> {
|
|
|
|
|
|
|
|
|
|
private final InvariantDeviceProfile mIdp;
|
|
|
|
|
private final AppFilter mAppFilter;
|
|
|
|
|
|
|
|
|
|
WidgetValidityCheck(LauncherAppState app) {
|
|
|
|
|
mIdp = app.getInvariantDeviceProfile();
|
2020-08-25 23:22:18 -07:00
|
|
|
mAppFilter = new AppFilter(app.getContext());
|
2020-08-22 02:09:42 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean test(WidgetItem item) {
|
|
|
|
|
if (item.widgetInfo != null) {
|
|
|
|
|
if ((item.widgetInfo.getWidgetFeatures() & WIDGET_FEATURE_HIDE_FROM_PICKER) != 0) {
|
|
|
|
|
// Widget is hidden from picker
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ensure that all widgets we show can be added on a workspace of this size
|
|
|
|
|
int minSpanX = Math.min(item.widgetInfo.spanX, item.widgetInfo.minSpanX);
|
|
|
|
|
int minSpanY = Math.min(item.widgetInfo.spanY, item.widgetInfo.minSpanY);
|
|
|
|
|
if (minSpanX > mIdp.numColumns || minSpanY > mIdp.numRows) {
|
|
|
|
|
if (DEBUG) {
|
|
|
|
|
Log.d(TAG, String.format(
|
|
|
|
|
"Widget %s : (%d X %d) can't fit on this device",
|
|
|
|
|
item.componentName, minSpanX, minSpanY));
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!mAppFilter.shouldShowApp(item.componentName)) {
|
|
|
|
|
if (DEBUG) {
|
|
|
|
|
Log.d(TAG, String.format("%s is filtered and not added to the widget tray.",
|
|
|
|
|
item.componentName));
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-04-08 22:32:57 +01:00
|
|
|
|
|
|
|
|
/** A hash key for grouping widgets by package name or category. */
|
|
|
|
|
private static class WidgetPackageOrCategoryKey {
|
|
|
|
|
/**
|
|
|
|
|
* The package name of the widget provider.
|
|
|
|
|
*
|
|
|
|
|
* <p>This shouldn't be empty if {@link #mCategory} has a value,
|
|
|
|
|
* {@link PackageItemInfo#NO_CATEGORY}.
|
|
|
|
|
*/
|
|
|
|
|
public final String mPackage;
|
|
|
|
|
/** A widget category. */
|
|
|
|
|
@PackageItemInfo.Category public final int mCategory;
|
|
|
|
|
public final UserHandle mUser;
|
|
|
|
|
private final int mHashCode;
|
|
|
|
|
|
|
|
|
|
WidgetPackageOrCategoryKey(String packageName, UserHandle user) {
|
|
|
|
|
this(packageName, PackageItemInfo.NO_CATEGORY, user);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WidgetPackageOrCategoryKey(@PackageItemInfo.Category int category, UserHandle user) {
|
|
|
|
|
this("", category, user);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private WidgetPackageOrCategoryKey(String packageName,
|
|
|
|
|
@PackageItemInfo.Category int category, UserHandle user) {
|
|
|
|
|
mPackage = packageName;
|
|
|
|
|
mCategory = category;
|
|
|
|
|
mUser = user;
|
|
|
|
|
mHashCode = Arrays.hashCode(new Object[]{mPackage, mCategory, mUser});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public int hashCode() {
|
|
|
|
|
return mHashCode;
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-04-11 15:44:32 -07:00
|
|
|
}
|