Group weather widgets under the widget category

Test: manual (see video in bug) and added PackageUserKeyTest
      Also tested pending conversation widget to ensure the right icon
      is shown.
Fix: 201062480
Change-Id: If23c28bd93c54fb1747648309ab3c238a1810902
This commit is contained in:
Steven Ng 2021-09-24 12:39:55 +01:00
parent 9c871ae7b6
commit 43859f10bc
25 changed files with 507 additions and 142 deletions

View File

@ -79,12 +79,13 @@ public class WidgetsModel {
} }
public WidgetItem getWidgetProviderInfoByProviderName( public WidgetItem getWidgetProviderInfoByProviderName(
ComponentName providerName) { ComponentName providerName, UserHandle user) {
return null; return null;
} }
/** Returns {@link PackageItemInfo} of a pending widget. */ /** Returns {@link PackageItemInfo} of a pending widget. */
public static PackageItemInfo newPendingItemInfo(ComponentName provider) { public static PackageItemInfo newPendingItemInfo(
return new PackageItemInfo(provider.getPackageName()); Context context, ComponentName provider, UserHandle userHandle) {
return new PackageItemInfo(provider.getPackageName(), userHandle);
} }
} }

View File

@ -258,4 +258,18 @@
<declare-styleable name="WidgetsListRowHeader"> <declare-styleable name="WidgetsListRowHeader">
<attr name="appIconSize" format="dimension" /> <attr name="appIconSize" format="dimension" />
</declare-styleable> </declare-styleable>
<declare-styleable name="WidgetSections">
<!-- Component name of an app widget provider. -->
<attr name="provider" format="string" />
<!-- If true, keep the app widget under its app listing in addition to the widget category
in the widget picker. Defaults to false if not specified. -->
<attr name="alsoKeepInApp" format="boolean" />
<!-- The category of an app widget provider. Defaults to -1 if not specified. -->
<attr name="category" format="integer" />
<!-- The title name of a widget category. -->
<attr name="sectionTitle" format="reference" />
<!-- The icon drawable of a widget category. -->
<attr name="sectionDrawable" format="reference" />
</declare-styleable>
</resources> </resources>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2021 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<widget-sections xmlns:launcher="http://schemas.android.com/apk/res-auto">
<section
launcher:category="0"
launcher:sectionDrawable="@drawable/ic_conversations_widget_category"
launcher:sectionTitle="@string/widget_category_conversations">
<widget launcher:provider="com.android.systemui/.people.widget.PeopleSpaceWidgetProvider" />
</section>
</widget-sections>

View File

@ -349,7 +349,7 @@ public class LauncherPreviewRenderer extends ContextWrapper
private void inflateAndAddWidgets(LauncherAppWidgetInfo info, WidgetsModel widgetsModel) { private void inflateAndAddWidgets(LauncherAppWidgetInfo info, WidgetsModel widgetsModel) {
WidgetItem widgetItem = widgetsModel.getWidgetProviderInfoByProviderName( WidgetItem widgetItem = widgetsModel.getWidgetProviderInfoByProviderName(
info.providerName); info.providerName, info.user);
if (widgetItem == null) { if (widgetItem == null) {
return; return;
} }

View File

@ -18,6 +18,7 @@ package com.android.launcher3.icons;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.widget.WidgetSections.NO_CATEGORY;
import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.groupingBy;
@ -46,7 +47,6 @@ import androidx.annotation.NonNull;
import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherFiles; import com.android.launcher3.LauncherFiles;
import com.android.launcher3.R;
import com.android.launcher3.Utilities; import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic; import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
@ -63,6 +63,8 @@ import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.InstantAppResolver; import com.android.launcher3.util.InstantAppResolver;
import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.Preconditions;
import com.android.launcher3.widget.WidgetSections;
import com.android.launcher3.widget.WidgetSections.WidgetSection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -275,7 +277,8 @@ public class IconCache extends BaseIconCache {
getTitleAndIcon(appInfo, false); getTitleAndIcon(appInfo, false);
return appInfo.bitmap; return appInfo.bitmap;
} else { } else {
PackageItemInfo pkgInfo = new PackageItemInfo(shortcutInfo.getPackage()); PackageItemInfo pkgInfo = new PackageItemInfo(shortcutInfo.getPackage(),
shortcutInfo.getUserHandle());
getTitleAndIconForApp(pkgInfo, false); getTitleAndIconForApp(pkgInfo, false);
return pkgInfo.bitmap; return pkgInfo.bitmap;
} }
@ -409,8 +412,10 @@ public class IconCache extends BaseIconCache {
CacheEntry entry = getEntryForPackageLocked( CacheEntry entry = getEntryForPackageLocked(
infoInOut.packageName, infoInOut.user, useLowResIcon); infoInOut.packageName, infoInOut.user, useLowResIcon);
applyCacheEntry(entry, infoInOut); applyCacheEntry(entry, infoInOut);
if (infoInOut.category == PackageItemInfo.CONVERSATIONS) { if (infoInOut.widgetCategory != NO_CATEGORY) {
infoInOut.title = mContext.getString(R.string.widget_category_conversations); WidgetSection widgetSection = WidgetSections.getWidgetSections(mContext)
.get(infoInOut.widgetCategory);
infoInOut.title = mContext.getString(widgetSection.mSectionTitle);
infoInOut.contentDescription = mPackageManager.getUserBadgedLabel( infoInOut.contentDescription = mPackageManager.getUserBadgedLabel(
infoInOut.title, infoInOut.user); infoInOut.title, infoInOut.user);
} }

View File

@ -804,8 +804,9 @@ public class LoaderTask implements Runnable {
if (appWidgetInfo.restoreStatus != if (appWidgetInfo.restoreStatus !=
LauncherAppWidgetInfo.RESTORE_COMPLETED) { LauncherAppWidgetInfo.RESTORE_COMPLETED) {
appWidgetInfo.pendingItemInfo = WidgetsModel.newPendingItemInfo( appWidgetInfo.pendingItemInfo = WidgetsModel.newPendingItemInfo(
appWidgetInfo.providerName); mApp.getContext(),
appWidgetInfo.pendingItemInfo.user = appWidgetInfo.user; appWidgetInfo.providerName,
appWidgetInfo.user);
mIconCache.getTitleAndIconForApp( mIconCache.getTitleAndIconForApp(
appWidgetInfo.pendingItemInfo, false); appWidgetInfo.pendingItemInfo, false);
} }

View File

@ -16,47 +16,41 @@
package com.android.launcher3.model.data; package com.android.launcher3.model.data;
import androidx.annotation.IntDef; import static com.android.launcher3.widget.WidgetSections.NO_CATEGORY;
import android.os.UserHandle;
import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherSettings;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects; import java.util.Objects;
/** /**
* Represents a {@link Package} in the widget tray section. * Represents a {@link Package} in the widget tray section.
*/ */
public class PackageItemInfo extends ItemInfoWithIcon { public class PackageItemInfo extends ItemInfoWithIcon {
@Retention(RetentionPolicy.SOURCE)
@IntDef({NO_CATEGORY, CONVERSATIONS})
public @interface Category{}
/** The package is not categorized in the widget tray. */
public static final int NO_CATEGORY = 0;
/** The package is categorized to conversations widget in the widget tray. */
public static final int CONVERSATIONS = 1;
/** /**
* Package name of the {@link PackageItemInfo}. * Package name of the {@link PackageItemInfo}.
*/ */
public final String packageName; public final String packageName;
/** Represents a widget category shown in the widget tray section. */ /** Represents a widget category shown in the widget tray section. */
@Category public final int category; public final int widgetCategory;
public PackageItemInfo(String packageName) { public PackageItemInfo(String packageName, UserHandle user) {
this(packageName, NO_CATEGORY); this(packageName, NO_CATEGORY, user);
} }
public PackageItemInfo(String packageName, @Category int category) { public PackageItemInfo(String packageName, int widgetCategory, UserHandle user) {
this.packageName = packageName; this.packageName = packageName;
this.category = category; this.widgetCategory = widgetCategory;
this.user = user;
this.itemType = LauncherSettings.Favorites.ITEM_TYPE_NON_ACTIONABLE; this.itemType = LauncherSettings.Favorites.ITEM_TYPE_NON_ACTIONABLE;
} }
public PackageItemInfo(PackageItemInfo copy) { public PackageItemInfo(PackageItemInfo copy) {
this.packageName = copy.packageName; this.packageName = copy.packageName;
this.category = copy.category; this.widgetCategory = copy.widgetCategory;
this.user = copy.user;
this.itemType = LauncherSettings.Favorites.ITEM_TYPE_NON_ACTIONABLE; this.itemType = LauncherSettings.Favorites.ITEM_TYPE_NON_ACTIONABLE;
} }
@ -75,11 +69,13 @@ public class PackageItemInfo extends ItemInfoWithIcon {
if (this == o) return true; if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; if (o == null || getClass() != o.getClass()) return false;
PackageItemInfo that = (PackageItemInfo) o; PackageItemInfo that = (PackageItemInfo) o;
return Objects.equals(packageName, that.packageName); return Objects.equals(packageName, that.packageName)
&& Objects.equals(user, that.user)
&& widgetCategory == that.widgetCategory;
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(packageName, user); return Objects.hash(packageName, user, widgetCategory);
} }
} }

View File

@ -1,19 +1,24 @@
package com.android.launcher3.util; package com.android.launcher3.util;
import static com.android.launcher3.widget.WidgetSections.NO_CATEGORY;
import android.os.UserHandle; import android.os.UserHandle;
import android.service.notification.StatusBarNotification; import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.PackageItemInfo;
import java.util.Arrays; import java.util.Objects;
/** Creates a hash key based on package name and user. */ /** Creates a hash key based on package name, widget category, and user. */
public class PackageUserKey { public class PackageUserKey {
public String mPackageName; public String mPackageName;
public int mWidgetCategory;
public UserHandle mUser; public UserHandle mUser;
private int mHashCode; private int mHashCode;
@ -27,14 +32,31 @@ public class PackageUserKey {
return new PackageUserKey(notification.getPackageName(), notification.getUser()); return new PackageUserKey(notification.getPackageName(), notification.getUser());
} }
/** Creates a {@link PackageUserKey} from {@link PackageItemInfo}. */
public static PackageUserKey fromPackageItemInfo(PackageItemInfo info) {
if (TextUtils.isEmpty(info.packageName) && info.widgetCategory != NO_CATEGORY) {
return new PackageUserKey(info.widgetCategory, info.user);
}
return new PackageUserKey(info.packageName, info.user);
}
public PackageUserKey(String packageName, UserHandle user) { public PackageUserKey(String packageName, UserHandle user) {
update(packageName, user); update(packageName, user);
} }
public PackageUserKey(int widgetCategory, UserHandle user) {
update(/* packageName= */ "", widgetCategory, user);
}
public void update(String packageName, UserHandle user) { public void update(String packageName, UserHandle user) {
update(packageName, NO_CATEGORY, user);
}
private void update(String packageName, int widgetCategory, UserHandle user) {
mPackageName = packageName; mPackageName = packageName;
mWidgetCategory = widgetCategory;
mUser = user; mUser = user;
mHashCode = Arrays.hashCode(new Object[] {packageName, user}); mHashCode = Objects.hash(packageName, widgetCategory, user);
} }
/** /**
@ -59,12 +81,14 @@ public class PackageUserKey {
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (!(obj instanceof PackageUserKey)) return false; if (!(obj instanceof PackageUserKey)) return false;
PackageUserKey otherKey = (PackageUserKey) obj; PackageUserKey otherKey = (PackageUserKey) obj;
return mPackageName.equals(otherKey.mPackageName) && mUser.equals(otherKey.mUser); return Objects.equals(mPackageName, otherKey.mPackageName)
&& mWidgetCategory == otherKey.mWidgetCategory
&& Objects.equals(mUser, otherKey.mUser);
} }
@NonNull @NonNull
@Override @Override
public String toString() { public String toString() {
return mPackageName + "#" + mUser; return mPackageName + "#" + mUser + ",category=" + mWidgetCategory;
} }
} }

View File

@ -17,7 +17,7 @@
package com.android.launcher3.widget; package com.android.launcher3.widget;
import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon; import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
import static com.android.launcher3.model.data.PackageItemInfo.CONVERSATIONS; import static com.android.launcher3.widget.WidgetSections.getWidgetSections;
import android.content.Context; import android.content.Context;
import android.graphics.Canvas; import android.graphics.Canvas;
@ -89,8 +89,8 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
setOnClickListener(ItemClickHandler.INSTANCE); setOnClickListener(ItemClickHandler.INSTANCE);
if (info.pendingItemInfo == null) { if (info.pendingItemInfo == null) {
info.pendingItemInfo = new PackageItemInfo(info.providerName.getPackageName()); info.pendingItemInfo = new PackageItemInfo(info.providerName.getPackageName(),
info.pendingItemInfo.user = info.user; info.user);
cache.updateIconInBackground(this, info.pendingItemInfo); cache.updateIconInBackground(this, info.pendingItemInfo);
} else { } else {
reapplyItemInfo(info.pendingItemInfo); reapplyItemInfo(info.pendingItemInfo);
@ -338,10 +338,11 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
*/ */
@Nullable @Nullable
private Drawable getWidgetCategoryIcon() { private Drawable getWidgetCategoryIcon() {
switch (mInfo.pendingItemInfo.category) { if (mInfo.pendingItemInfo.widgetCategory == WidgetSections.NO_CATEGORY) {
case CONVERSATIONS: return null;
return getContext().getDrawable(R.drawable.ic_conversations_widget_category);
} }
return null; Context context = getContext();
return context.getDrawable(getWidgetSections(context).get(
mInfo.pendingItemInfo.widgetCategory).mSectionDrawable);
} }
} }

View File

@ -0,0 +1,134 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.widget;
import static android.content.res.Resources.ID_NULL;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.util.Xml;
import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
import com.android.launcher3.R;
import com.android.launcher3.util.IntSet;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.Map;
/** A helper class to parse widget sections (categories) resource overlay. */
public final class WidgetSections {
/** The package is not categorized in the widget tray. */
public static final int NO_CATEGORY = -1;
private static final String TAG_SECTION_NAME = "section";
private static final String TAG_WIDGET_NAME = "widget";
private static SparseArray<WidgetSection> sWidgetSections;
private static Map<ComponentName, IntSet> sWidgetsToCategories;
/** Returns a list of widget sections that are shown in the widget picker. */
public static synchronized SparseArray<WidgetSection> getWidgetSections(Context context) {
if (sWidgetSections != null) {
return sWidgetSections;
}
parseWidgetSectionsXml(context);
return sWidgetSections;
}
/** Returns a map which maps app widget providers to app widget categories. */
public static synchronized Map<ComponentName, IntSet> getWidgetsToCategory(
Context context) {
if (sWidgetsToCategories != null) {
return sWidgetsToCategories;
}
parseWidgetSectionsXml(context);
return sWidgetsToCategories;
}
private static synchronized void parseWidgetSectionsXml(Context context) {
SparseArray<WidgetSection> widgetSections = new SparseArray();
Map<ComponentName, IntSet> widgetsToCategories = new ArrayMap<>();
try (XmlResourceParser parser = context.getResources().getXml(R.xml.widget_sections)) {
final int depth = parser.getDepth();
int type;
while (((type = parser.next()) != XmlPullParser.END_TAG
|| parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
if ((type == XmlPullParser.START_TAG)
&& TAG_SECTION_NAME.equals(parser.getName())) {
AttributeSet sectionAttributes = Xml.asAttributeSet(parser);
WidgetSection section = new WidgetSection(context, sectionAttributes);
final int sectionDepth = parser.getDepth();
while (((type = parser.next()) != XmlPullParser.END_TAG
|| parser.getDepth() > sectionDepth)
&& type != XmlPullParser.END_DOCUMENT) {
if ((type == XmlPullParser.START_TAG)
&& TAG_WIDGET_NAME.equals(parser.getName())) {
TypedArray a = context.obtainStyledAttributes(
Xml.asAttributeSet(parser), R.styleable.WidgetSections);
ComponentName provider = ComponentName.unflattenFromString(
a.getString(R.styleable.WidgetSections_provider));
boolean alsoKeepInApp = a.getBoolean(
R.styleable.WidgetSections_alsoKeepInApp,
/* defValue= */ false);
final IntSet categories;
if (widgetsToCategories.containsKey(provider)) {
categories = widgetsToCategories.get(provider);
} else {
categories = new IntSet();
widgetsToCategories.put(provider, categories);
}
if (alsoKeepInApp) {
categories.add(NO_CATEGORY);
}
categories.add(section.mCategory);
}
}
widgetSections.put(section.mCategory, section);
}
}
sWidgetSections = widgetSections;
sWidgetsToCategories = widgetsToCategories;
} catch (IOException | XmlPullParserException e) {
throw new RuntimeException(e);
}
}
/** A data class which contains a widget section's information. */
public static final class WidgetSection {
public final int mCategory;
@StringRes
public final int mSectionTitle;
@DrawableRes
public final int mSectionDrawable;
public WidgetSection(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.WidgetSections);
mCategory = a.getInt(R.styleable.WidgetSections_category, NO_CATEGORY);
mSectionTitle = a.getResourceId(R.styleable.WidgetSections_sectionTitle, ID_NULL);
mSectionDrawable = a.getResourceId(R.styleable.WidgetSections_sectionDrawable, ID_NULL);
}
}
}

View File

@ -16,6 +16,8 @@
package com.android.launcher3.widget.model; package com.android.launcher3.widget.model;
import android.os.Process;
import com.android.launcher3.model.data.PackageItemInfo; import com.android.launcher3.model.data.PackageItemInfo;
import java.util.Collections; import java.util.Collections;
@ -26,7 +28,9 @@ import java.util.Collections;
public class WidgetListSpaceEntry extends WidgetsListBaseEntry { public class WidgetListSpaceEntry extends WidgetsListBaseEntry {
public WidgetListSpaceEntry() { public WidgetListSpaceEntry() {
super(new PackageItemInfo(""), "", Collections.EMPTY_LIST); super(new PackageItemInfo(/* packageName= */ "", Process.myUserHandle()),
/* titleSectionName= */ "",
Collections.EMPTY_LIST);
mPkgItem.title = ""; mPkgItem.title = "";
} }

View File

@ -24,5 +24,5 @@ public interface OnHeaderClickListener {
/** /**
* Calls when a header is clicked to show / hide widgets for a package. * Calls when a header is clicked to show / hide widgets for a package.
*/ */
void onHeaderClicked(boolean showWidgets, PackageUserKey packageUserKey); void onHeaderClicked(boolean showWidgets, PackageUserKey key);
} }

View File

@ -98,7 +98,7 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
private Predicate<WidgetsListBaseEntry> mHeaderAndSelectedContentFilter = entry -> private Predicate<WidgetsListBaseEntry> mHeaderAndSelectedContentFilter = entry ->
entry instanceof WidgetsListHeaderEntry entry instanceof WidgetsListHeaderEntry
|| entry instanceof WidgetsListSearchHeaderEntry || entry instanceof WidgetsListSearchHeaderEntry
|| new PackageUserKey(entry.mPkgItem.packageName, entry.mPkgItem.user) || PackageUserKey.fromPackageItemInfo(entry.mPkgItem)
.equals(mWidgetsContentVisiblePackageUserKey); .equals(mWidgetsContentVisiblePackageUserKey);
@Nullable private Predicate<WidgetsListBaseEntry> mFilter = null; @Nullable private Predicate<WidgetsListBaseEntry> mFilter = null;
@Nullable private RecyclerView mRecyclerView; @Nullable private RecyclerView mRecyclerView;
@ -252,10 +252,11 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
return entry instanceof WidgetsListBaseEntry.Header && matchesKey(entry, key); return entry instanceof WidgetsListBaseEntry.Header && matchesKey(entry, key);
} }
private static boolean matchesKey( private static boolean matchesKey(@NonNull WidgetsListBaseEntry entry,
@NonNull WidgetsListBaseEntry entry, @Nullable PackageUserKey key) { @Nullable PackageUserKey key) {
if (key == null) return false; if (key == null) return false;
return entry.mPkgItem.packageName.equals(key.mPackageName) return entry.mPkgItem.packageName.equals(key.mPackageName)
&& entry.mPkgItem.widgetCategory == key.mWidgetCategory
&& entry.mPkgItem.user.equals(key.mUser); && entry.mPkgItem.user.equals(key.mUser);
} }
@ -434,11 +435,10 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
.filter(entry -> entry instanceof WidgetsListHeaderEntry) .filter(entry -> entry instanceof WidgetsListHeaderEntry)
.map(entry -> entry.mPkgItem) .map(entry -> entry.mPkgItem)
.collect(Collectors.toMap( .collect(Collectors.toMap(
entry -> new PackageUserKey(entry.packageName, entry.user), entry -> PackageUserKey.fromPackageItemInfo(entry),
entry -> entry)); entry -> entry));
for (WidgetsListBaseEntry visibleEntry: mVisibleEntries) { for (WidgetsListBaseEntry visibleEntry: mVisibleEntries) {
PackageUserKey key = new PackageUserKey(visibleEntry.mPkgItem.packageName, PackageUserKey key = PackageUserKey.fromPackageItemInfo(visibleEntry.mPkgItem);
visibleEntry.mPkgItem.user);
PackageItemInfo packageItemInfo = packagesInfo.get(key); PackageItemInfo packageItemInfo = packagesInfo.get(key);
if (packageItemInfo != null if (packageItemInfo != null
&& !visibleEntry.mPkgItem.title.equals(packageItemInfo.title)) { && !visibleEntry.mPkgItem.title.equals(packageItemInfo.title)) {

View File

@ -15,6 +15,8 @@
*/ */
package com.android.launcher3.widget.picker; package com.android.launcher3.widget.picker;
import static com.android.launcher3.widget.WidgetSections.NO_CATEGORY;
import android.content.Context; import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.content.res.TypedArray; import android.content.res.TypedArray;
@ -41,6 +43,8 @@ import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.PackageItemInfo; import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.util.PluralMessageFormat; import com.android.launcher3.util.PluralMessageFormat;
import com.android.launcher3.views.ActivityContext; import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.widget.WidgetSections;
import com.android.launcher3.widget.WidgetSections.WidgetSection;
import com.android.launcher3.widget.model.WidgetsListHeaderEntry; import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry; import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry;
@ -173,12 +177,12 @@ public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpd
private void setIcon(PackageItemInfo info) { private void setIcon(PackageItemInfo info) {
Drawable icon; Drawable icon;
switch (info.category) { if (info.widgetCategory == NO_CATEGORY) {
case PackageItemInfo.CONVERSATIONS: icon = info.newIcon(getContext());
icon = getContext().getDrawable(R.drawable.ic_conversations_widget_category); } else {
break; WidgetSection widgetSection = WidgetSections.getWidgetSections(getContext())
default: .get(info.widgetCategory);
icon = info.newIcon(getContext()); icon = getContext().getDrawable(widgetSection.mSectionDrawable);
} }
applyDrawables(icon); applyDrawables(icon);
mIconDrawable = icon; mIconDrawable = icon;

View File

@ -62,9 +62,7 @@ public final class WidgetsListHeaderViewHolderBinder implements
(position & POSITION_LAST) != 0, (position & POSITION_LAST) != 0,
/* isExpanded= */ data.isWidgetListShown())); /* isExpanded= */ data.isWidgetListShown()));
widgetsListHeader.setOnExpandChangeListener(isExpanded -> widgetsListHeader.setOnExpandChangeListener(isExpanded ->
mOnHeaderClickListener.onHeaderClicked( mOnHeaderClickListener.onHeaderClicked(isExpanded,
isExpanded, PackageUserKey.fromPackageItemInfo(data.mPkgItem)));
new PackageUserKey(data.mPkgItem.packageName, data.mPkgItem.user)
));
} }
} }

View File

@ -64,6 +64,6 @@ public final class WidgetsListSearchHeaderViewHolderBinder implements
/* isExpanded= */ data.isWidgetListShown())); /* isExpanded= */ data.isWidgetListShown()));
widgetsListHeader.setOnExpandChangeListener(isExpanded -> widgetsListHeader.setOnExpandChangeListener(isExpanded ->
mOnHeaderClickListener.onHeaderClicked(isExpanded, mOnHeaderClickListener.onHeaderClicked(isExpanded,
new PackageUserKey(data.mPkgItem.packageName, data.mPkgItem.user))); PackageUserKey.fromPackageItemInfo(data.mPkgItem)));
} }
} }

View File

@ -4,7 +4,10 @@ package com.android.launcher3.model;
import static android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_HIDE_FROM_PICKER; import static android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_HIDE_FROM_PICKER;
import static com.android.launcher3.pm.ShortcutConfigActivityInfo.queryList; import static com.android.launcher3.pm.ShortcutConfigActivityInfo.queryList;
import static com.android.launcher3.widget.WidgetSections.NO_CATEGORY;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.mapping;
import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toList;
import android.appwidget.AppWidgetProviderInfo; import android.appwidget.AppWidgetProviderInfo;
@ -13,6 +16,7 @@ import android.content.Context;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.UserHandle; import android.os.UserHandle;
import android.util.Log; import android.util.Log;
import android.util.Pair;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.collection.ArrayMap; import androidx.collection.ArrayMap;
@ -27,10 +31,12 @@ import com.android.launcher3.icons.ComponentWithLabelAndIcon;
import com.android.launcher3.icons.IconCache; import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.data.PackageItemInfo; import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.pm.ShortcutConfigActivityInfo; import com.android.launcher3.pm.ShortcutConfigActivityInfo;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.Preconditions;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.WidgetManagerHelper; import com.android.launcher3.widget.WidgetManagerHelper;
import com.android.launcher3.widget.WidgetSections;
import com.android.launcher3.widget.model.WidgetsListBaseEntry; import com.android.launcher3.widget.model.WidgetsListBaseEntry;
import com.android.launcher3.widget.model.WidgetsListContentEntry; import com.android.launcher3.widget.model.WidgetsListContentEntry;
import com.android.launcher3.widget.model.WidgetsListHeaderEntry; import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
@ -40,12 +46,12 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors;
/** /**
* Widgets data model that is used by the adapters of the widget views and controllers. * Widgets data model that is used by the adapters of the widget views and controllers.
@ -61,9 +67,6 @@ public class WidgetsModel {
private static final String TAG = "WidgetsModel"; private static final String TAG = "WidgetsModel";
private static final boolean DEBUG = false; private static final boolean DEBUG = false;
private static final ComponentName CONVERSATION_WIDGET = ComponentName.createRelative(
"com.android.systemui", ".people.widget.PeopleSpaceWidgetProvider");
/* Map of widgets and shortcuts that are tracked per package. */ /* Map of widgets and shortcuts that are tracked per package. */
private final Map<PackageItemInfo, List<WidgetItem>> mWidgetsList = new HashMap<>(); private final Map<PackageItemInfo, List<WidgetItem>> mWidgetsList = new HashMap<>();
@ -168,16 +171,15 @@ public class WidgetsModel {
mWidgetsList.clear(); mWidgetsList.clear();
} else { } else {
// Otherwise, only clear the widgets and shortcuts for the changed package. // Otherwise, only clear the widgets and shortcuts for the changed package.
mWidgetsList.remove( mWidgetsList.remove(packageItemInfoCache.getOrCreate(packageUser));
packageItemInfoCache.getOrCreate(new WidgetPackageOrCategoryKey(packageUser)));
} }
// add and update. // add and update.
mWidgetsList.putAll(rawWidgetsShortcuts.stream() mWidgetsList.putAll(rawWidgetsShortcuts.stream()
.filter(new WidgetValidityCheck(app)) .filter(new WidgetValidityCheck(app))
.collect(Collectors.groupingBy(item -> .flatMap(widgetItem -> getPackageUserKeys(app.getContext(), widgetItem).stream()
packageItemInfoCache.getOrCreate(getWidgetPackageOrCategoryKey(item)) .map(key -> new Pair<>(packageItemInfoCache.getOrCreate(key), widgetItem)))
))); .collect(groupingBy(pair -> pair.first, mapping(pair -> pair.second, toList()))));
// Update each package entry // Update each package entry
IconCache iconCache = app.getIconCache(); IconCache iconCache = app.getIconCache();
@ -209,9 +211,9 @@ public class WidgetsModel {
} }
public WidgetItem getWidgetProviderInfoByProviderName( public WidgetItem getWidgetProviderInfoByProviderName(
ComponentName providerName) { ComponentName providerName, UserHandle user) {
List<WidgetItem> widgetsList = mWidgetsList.get( List<WidgetItem> widgetsList = mWidgetsList.get(
new PackageItemInfo(providerName.getPackageName())); new PackageItemInfo(providerName.getPackageName(), user));
if (widgetsList == null) { if (widgetsList == null) {
return null; return null;
} }
@ -225,18 +227,40 @@ public class WidgetsModel {
} }
/** Returns {@link PackageItemInfo} of a pending widget. */ /** Returns {@link PackageItemInfo} of a pending widget. */
public static PackageItemInfo newPendingItemInfo(ComponentName provider) { public static PackageItemInfo newPendingItemInfo(Context context, ComponentName provider,
if (CONVERSATION_WIDGET.equals(provider)) { UserHandle user) {
return new PackageItemInfo(provider.getPackageName(), PackageItemInfo.CONVERSATIONS); Map<ComponentName, IntSet> widgetsToCategories =
WidgetSections.getWidgetsToCategory(context);
if (widgetsToCategories.containsKey(provider)) {
Iterator<Integer> categoriesIterator = widgetsToCategories.get(provider).iterator();
int firstCategory = NO_CATEGORY;
while (categoriesIterator.hasNext() && firstCategory == NO_CATEGORY) {
firstCategory = categoriesIterator.next();
}
return new PackageItemInfo(provider.getPackageName(), firstCategory, user);
} }
return new PackageItemInfo(provider.getPackageName()); return new PackageItemInfo(provider.getPackageName(), user);
} }
private WidgetPackageOrCategoryKey getWidgetPackageOrCategoryKey(WidgetItem item) { private List<PackageUserKey> getPackageUserKeys(Context context, WidgetItem item) {
if (CONVERSATION_WIDGET.equals(item.componentName)) { Map<ComponentName, IntSet> widgetsToCategories =
return new WidgetPackageOrCategoryKey(PackageItemInfo.CONVERSATIONS, item.user); WidgetSections.getWidgetsToCategory(context);
IntSet categories = widgetsToCategories.get(item.componentName);
if (categories == null || categories.isEmpty()) {
return Arrays.asList(
new PackageUserKey(item.componentName.getPackageName(), item.user));
} }
return new WidgetPackageOrCategoryKey(item.componentName.getPackageName(), item.user); List<PackageUserKey> packageUserKeys = new ArrayList<>();
categories.forEach(category -> {
if (category == NO_CATEGORY) {
packageUserKeys.add(
new PackageUserKey(item.componentName.getPackageName(),
item.user));
} else {
packageUserKeys.add(new PackageUserKey(category, item.user));
}
});
return packageUserKeys;
} }
private static class WidgetValidityCheck implements Predicate<WidgetItem> { private static class WidgetValidityCheck implements Predicate<WidgetItem> {
@ -279,53 +303,13 @@ public class WidgetsModel {
} }
} }
/** 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(PackageUserKey key) {
this(key.mPackageName, key.mUser);
}
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;
}
}
private static final class PackageItemInfoCache { private static final class PackageItemInfoCache {
private final Map<WidgetPackageOrCategoryKey, PackageItemInfo> mMap = new ArrayMap<>(); private final Map<PackageUserKey, PackageItemInfo> mMap = new ArrayMap<>();
PackageItemInfo getOrCreate(WidgetPackageOrCategoryKey key) { PackageItemInfo getOrCreate(PackageUserKey key) {
PackageItemInfo pInfo = mMap.get(key); PackageItemInfo pInfo = mMap.get(key);
if (pInfo == null) { if (pInfo == null) {
pInfo = new PackageItemInfo(key.mPackage, key.mCategory); pInfo = new PackageItemInfo(key.mPackageName, key.mWidgetCategory, key.mUser);
pInfo.user = key.mUser; pInfo.user = key.mUser;
mMap.put(key, pInfo); mMap.put(key, pInfo);
} }

View File

@ -0,0 +1,162 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.util;
import static com.android.launcher3.widget.WidgetSections.NO_CATEGORY;
import static com.google.common.truth.Truth.assertThat;
import android.os.UserHandle;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.launcher3.model.data.PackageItemInfo;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidJUnit4.class)
public final class PackageUserKeyTest {
@Rule
public ExpectedException exception = ExpectedException.none();
private static final String TEST_PACKAGE = "com.android.test.package";
private static final int CONVERSATIONS = 0;
private static final int WEATHER = 1;
@Test
public void fromPackageItemInfo_shouldCreateExpectedObject() {
PackageUserKey packageUserKey = PackageUserKey.fromPackageItemInfo(
new PackageItemInfo(TEST_PACKAGE, UserHandle.CURRENT));
assertThat(packageUserKey.mPackageName).isEqualTo(TEST_PACKAGE);
assertThat(packageUserKey.mWidgetCategory).isEqualTo(NO_CATEGORY);
assertThat(packageUserKey.mUser).isEqualTo(UserHandle.CURRENT);
}
@Test
public void constructor_packageNameAndUserHandle_shouldCreateExpectedObject() {
PackageUserKey packageUserKey = new PackageUserKey(TEST_PACKAGE, UserHandle.CURRENT);
assertThat(packageUserKey.mPackageName).isEqualTo(TEST_PACKAGE);
assertThat(packageUserKey.mWidgetCategory).isEqualTo(NO_CATEGORY);
assertThat(packageUserKey.mUser).isEqualTo(UserHandle.CURRENT);
}
@Test
public void constructor_widgetCategoryAndUserHandle_shouldCreateExpectedObject() {
PackageUserKey packageUserKey = new PackageUserKey(CONVERSATIONS, UserHandle.CURRENT);
assertThat(packageUserKey.mPackageName).isEqualTo("");
assertThat(packageUserKey.mWidgetCategory).isEqualTo(CONVERSATIONS);
assertThat(packageUserKey.mUser).isEqualTo(UserHandle.CURRENT);
}
@Test
public void equals_sameObject_shouldReturnTrue() {
PackageUserKey packageUserKey = new PackageUserKey(TEST_PACKAGE, UserHandle.CURRENT);
PackageUserKey otherPackageUserKey = packageUserKey;
assertThat(packageUserKey).isEqualTo(otherPackageUserKey);
}
@Test
public void equals_differentObjectSameContent_shouldReturnTrue() {
PackageUserKey packageUserKey = new PackageUserKey(TEST_PACKAGE, UserHandle.CURRENT);
PackageUserKey otherPackageUserKey = new PackageUserKey(TEST_PACKAGE, UserHandle.CURRENT);
assertThat(packageUserKey).isEqualTo(otherPackageUserKey);
}
@Test
public void equals_compareAgainstNull_shouldReturnFalse() {
PackageUserKey packageUserKey = new PackageUserKey(TEST_PACKAGE, UserHandle.CURRENT);
assertThat(packageUserKey).isNotEqualTo(null);
}
@Test
public void equals_differentPackage_shouldReturnFalse() {
PackageUserKey packageUserKey = new PackageUserKey(TEST_PACKAGE, UserHandle.CURRENT);
PackageUserKey otherPackageUserKey = new PackageUserKey(TEST_PACKAGE + "1",
UserHandle.CURRENT);
assertThat(packageUserKey).isNotEqualTo(otherPackageUserKey);
}
@Test
public void equals_differentCategory_shouldReturnFalse() {
PackageUserKey packageUserKey = new PackageUserKey(WEATHER, UserHandle.CURRENT);
PackageUserKey otherPackageUserKey = new PackageUserKey(CONVERSATIONS, UserHandle.CURRENT);
assertThat(packageUserKey).isNotEqualTo(otherPackageUserKey);
}
@Test
public void equals_differentUser_shouldReturnFalse() {
PackageUserKey packageUserKey = new PackageUserKey(TEST_PACKAGE, UserHandle.of(1));
PackageUserKey otherPackageUserKey = new PackageUserKey(TEST_PACKAGE, UserHandle.of(2));
assertThat(packageUserKey).isNotEqualTo(otherPackageUserKey);
}
@Test
public void hashCode_sameObject_shouldBeTheSame() {
PackageUserKey packageUserKey = new PackageUserKey(WEATHER, UserHandle.CURRENT);
PackageUserKey otherPackageUserKey = packageUserKey;
assertThat(packageUserKey.hashCode()).isEqualTo(otherPackageUserKey.hashCode());
}
@Test
public void hashCode_differentObjectSameContent_shouldBeTheSame() {
PackageUserKey packageUserKey = new PackageUserKey(TEST_PACKAGE, UserHandle.CURRENT);
PackageUserKey otherPackageUserKey = new PackageUserKey(TEST_PACKAGE, UserHandle.CURRENT);
assertThat(packageUserKey.hashCode()).isEqualTo(otherPackageUserKey.hashCode());
}
@Test
public void hashCode_differentPackage_shouldBeDifferent() {
PackageUserKey packageUserKey = new PackageUserKey(TEST_PACKAGE, UserHandle.CURRENT);
PackageUserKey otherPackageUserKey = new PackageUserKey(TEST_PACKAGE + "1",
UserHandle.CURRENT);
assertThat(packageUserKey.hashCode()).isNotEqualTo(otherPackageUserKey.hashCode());
}
@Test
public void hashCode_differentCategory_shouldBeDifferent() {
PackageUserKey packageUserKey = new PackageUserKey(WEATHER, UserHandle.CURRENT);
PackageUserKey otherPackageUserKey = new PackageUserKey(CONVERSATIONS, UserHandle.CURRENT);
assertThat(packageUserKey.hashCode()).isNotEqualTo(otherPackageUserKey.hashCode());
}
@Test
public void hashCode_differentUser_shouldBeDifferent() {
PackageUserKey packageUserKey = new PackageUserKey(TEST_PACKAGE, UserHandle.of(1));
PackageUserKey otherPackageUserKey = new PackageUserKey(TEST_PACKAGE, UserHandle.of(2));
assertThat(packageUserKey.hashCode()).isNotEqualTo(otherPackageUserKey.hashCode());
}
}

View File

@ -288,9 +288,8 @@ public final class WidgetsDiffReporterTest {
private PackageItemInfo createPackageItemInfo(String packageName, String appName, private PackageItemInfo createPackageItemInfo(String packageName, String appName,
UserHandle userHandle) { UserHandle userHandle) {
PackageItemInfo pInfo = new PackageItemInfo(packageName); PackageItemInfo pInfo = new PackageItemInfo(packageName, userHandle);
pInfo.title = appName; pInfo.title = appName;
pInfo.user = userHandle;
pInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0); pInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
return pInfo; return pInfo;
} }

View File

@ -215,14 +215,23 @@ public final class WidgetsListAdapterTest {
@Test @Test
public void setWidgetsOnSearch_expandedApp_shouldResetExpandedApp() { public void setWidgetsOnSearch_expandedApp_shouldResetExpandedApp() {
// GIVEN a list of widgets entries: // GIVEN a list of widgets entries:
// [com.google.test0, com.google.test0 content, // [Empty item
// com.google.test1, com.google.test1 content, // com.google.test0,
// com.google.test2, com.google.test2 content] // com.google.test0 content,
// The visible widgets entries: [com.google.test0, com.google.test1, com.google.test2]. // com.google.test1,
ArrayList<WidgetsListBaseEntry> allEntries = generateSampleMap(2); // com.google.test1 content,
// com.google.test2,
// com.google.test2 content]
// The visible widgets entries:
// [Empty item,
// com.google.test0,
// com.google.test1,
// com.google.test2].
ArrayList<WidgetsListBaseEntry> allEntries = generateSampleMap(3);
mAdapter.setWidgetsOnSearch(allEntries); mAdapter.setWidgetsOnSearch(allEntries);
// GIVEN com.google.test.1 header is expanded. The visible entries list becomes: // GIVEN com.google.test.1 header is expanded. The visible entries list becomes:
// [com.google.test0, com.google.test1, com.google.test1 content, com.google.test2] // [Empty item, com.google.test0, com.google.test1, com.google.test1 content,
// com.google.test2]
mAdapter.onHeaderClicked(/* showWidgets= */ true, mAdapter.onHeaderClicked(/* showWidgets= */ true,
new PackageUserKey(TEST_PACKAGE_PLACEHOLDER + 1, mUserHandle)); new PackageUserKey(TEST_PACKAGE_PLACEHOLDER + 1, mUserHandle));
Mockito.reset(mListener); Mockito.reset(mListener);
@ -231,7 +240,7 @@ public final class WidgetsListAdapterTest {
mAdapter.setWidgetsOnSearch(allEntries); mAdapter.setWidgetsOnSearch(allEntries);
// THEN expanded app is reset and the visible entries list becomes: // THEN expanded app is reset and the visible entries list becomes:
// [com.google.test0, com.google.test1, com.google.test2] // [Empty item, com.google.test0, com.google.test1, com.google.test2]
verify(mListener).onItemRangeChanged(eq(2), eq(1), isNull()); verify(mListener).onItemRangeChanged(eq(2), eq(1), isNull());
verify(mListener).onItemRangeRemoved(/* positionStart= */ 3, /* itemCount= */ 1); verify(mListener).onItemRangeRemoved(/* positionStart= */ 3, /* itemCount= */ 1);
} }
@ -257,9 +266,8 @@ public final class WidgetsListAdapterTest {
List<WidgetItem> widgetItems = generateWidgetItems(packageName, /* numOfWidgets= */ 1); List<WidgetItem> widgetItems = generateWidgetItems(packageName, /* numOfWidgets= */ 1);
PackageItemInfo pInfo = new PackageItemInfo(packageName); PackageItemInfo pInfo = new PackageItemInfo(packageName, widgetItems.get(0).user);
pInfo.title = pInfo.packageName; pInfo.title = pInfo.packageName;
pInfo.user = widgetItems.get(0).user;
pInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0); pInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
result.add(new WidgetsListHeaderEntry(pInfo, /* titleSectionName= */ "", widgetItems)); result.add(new WidgetsListHeaderEntry(pInfo, /* titleSectionName= */ "", widgetItems));

View File

@ -30,6 +30,7 @@ import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.os.UserHandle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.TextView; import android.widget.TextView;
@ -124,12 +125,12 @@ public final class WidgetsListHeaderViewHolderBinderTest {
widgetsListHeader.callOnClick(); widgetsListHeader.callOnClick();
verify(mOnHeaderClickListener).onHeaderClicked(eq(true), verify(mOnHeaderClickListener).onHeaderClicked(eq(true),
eq(new PackageUserKey(entry.mPkgItem.packageName, entry.mPkgItem.user))); eq(PackageUserKey.fromPackageItemInfo(entry.mPkgItem)));
} }
private WidgetsListHeaderEntry generateSampleAppHeader(String appName, String packageName, private WidgetsListHeaderEntry generateSampleAppHeader(String appName, String packageName,
int numOfWidgets) { int numOfWidgets) {
PackageItemInfo appInfo = new PackageItemInfo(packageName); PackageItemInfo appInfo = new PackageItemInfo(packageName, UserHandle.CURRENT);
appInfo.title = appName; appInfo.title = appName;
appInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0); appInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);

View File

@ -30,6 +30,7 @@ import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.os.UserHandle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.TextView; import android.widget.TextView;
@ -124,12 +125,12 @@ public final class WidgetsListSearchHeaderViewHolderBinderTest {
widgetsListHeader.callOnClick(); widgetsListHeader.callOnClick();
verify(mOnHeaderClickListener).onHeaderClicked(eq(true), verify(mOnHeaderClickListener).onHeaderClicked(eq(true),
eq(new PackageUserKey(entry.mPkgItem.packageName, entry.mPkgItem.user))); eq(PackageUserKey.fromPackageItemInfo(entry.mPkgItem)));
} }
private WidgetsListSearchHeaderEntry generateSampleSearchHeader(String appName, private WidgetsListSearchHeaderEntry generateSampleSearchHeader(String appName,
String packageName, int numOfWidgets) { String packageName, int numOfWidgets) {
PackageItemInfo appInfo = new PackageItemInfo(packageName); PackageItemInfo appInfo = new PackageItemInfo(packageName, UserHandle.CURRENT);
appInfo.title = appName; appInfo.title = appName;
appInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0); appInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);

View File

@ -28,6 +28,7 @@ import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.os.UserHandle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
@ -125,7 +126,7 @@ public final class WidgetsListTableViewHolderBinderTest {
private WidgetsListContentEntry generateSampleAppWithWidgets(String appName, String packageName, private WidgetsListContentEntry generateSampleAppWithWidgets(String appName, String packageName,
int numOfWidgets) { int numOfWidgets) {
PackageItemInfo appInfo = new PackageItemInfo(packageName); PackageItemInfo appInfo = new PackageItemInfo(packageName, UserHandle.CURRENT);
appInfo.title = appName; appInfo.title = appName;
appInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0); appInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);

View File

@ -26,6 +26,7 @@ import static org.mockito.Mockito.doAnswer;
import android.appwidget.AppWidgetProviderInfo; import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName; import android.content.ComponentName;
import android.os.UserHandle;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
@ -53,8 +54,10 @@ import java.util.Map;
public final class WidgetsListContentEntryTest { public final class WidgetsListContentEntryTest {
private static final String PACKAGE_NAME = "com.android.test"; private static final String PACKAGE_NAME = "com.android.test";
private static final String PACKAGE_NAME_2 = "com.android.test2"; private static final String PACKAGE_NAME_2 = "com.android.test2";
private final PackageItemInfo mPackageItemInfo1 = new PackageItemInfo(PACKAGE_NAME); private final PackageItemInfo mPackageItemInfo1 = new PackageItemInfo(PACKAGE_NAME,
private final PackageItemInfo mPackageItemInfo2 = new PackageItemInfo(PACKAGE_NAME_2); UserHandle.CURRENT);
private final PackageItemInfo mPackageItemInfo2 = new PackageItemInfo(PACKAGE_NAME_2,
UserHandle.CURRENT);
private final ComponentName mWidget1 = ComponentName.createRelative(PACKAGE_NAME, ".mWidget1"); private final ComponentName mWidget1 = ComponentName.createRelative(PACKAGE_NAME, ".mWidget1");
private final ComponentName mWidget2 = ComponentName.createRelative(PACKAGE_NAME, ".mWidget2"); private final ComponentName mWidget2 = ComponentName.createRelative(PACKAGE_NAME, ".mWidget2");
private final ComponentName mWidget3 = ComponentName.createRelative(PACKAGE_NAME, ".mWidget3"); private final ComponentName mWidget3 = ComponentName.createRelative(PACKAGE_NAME, ".mWidget3");

View File

@ -189,9 +189,8 @@ public class SimpleWidgetsSearchAlgorithmTest {
private PackageItemInfo createPackageItemInfo(String packageName, String appName, private PackageItemInfo createPackageItemInfo(String packageName, String appName,
UserHandle userHandle) { UserHandle userHandle) {
PackageItemInfo pInfo = new PackageItemInfo(packageName); PackageItemInfo pInfo = new PackageItemInfo(packageName, userHandle);
pInfo.title = appName; pInfo.title = appName;
pInfo.user = userHandle;
pInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0); pInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
return pInfo; return pInfo;
} }