Merge "Group weather widgets under the widget category" into sc-v2-dev

This commit is contained in:
Steven Ng
2021-10-01 10:17:47 +00:00
committed by Android (Google) Code Review
25 changed files with 507 additions and 142 deletions
@@ -352,7 +352,7 @@ public class LauncherPreviewRenderer extends ContextWrapper
private void inflateAndAddWidgets(LauncherAppWidgetInfo info, WidgetsModel widgetsModel) {
WidgetItem widgetItem = widgetsModel.getWidgetProviderInfoByProviderName(
info.providerName);
info.providerName, info.user);
if (widgetItem == null) {
return;
}
@@ -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.MODEL_EXECUTOR;
import static com.android.launcher3.widget.WidgetSections.NO_CATEGORY;
import static java.util.stream.Collectors.groupingBy;
@@ -46,7 +47,6 @@ import androidx.annotation.NonNull;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherFiles;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
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.PackageUserKey;
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.List;
@@ -275,7 +277,8 @@ public class IconCache extends BaseIconCache {
getTitleAndIcon(appInfo, false);
return appInfo.bitmap;
} else {
PackageItemInfo pkgInfo = new PackageItemInfo(shortcutInfo.getPackage());
PackageItemInfo pkgInfo = new PackageItemInfo(shortcutInfo.getPackage(),
shortcutInfo.getUserHandle());
getTitleAndIconForApp(pkgInfo, false);
return pkgInfo.bitmap;
}
@@ -409,8 +412,10 @@ public class IconCache extends BaseIconCache {
CacheEntry entry = getEntryForPackageLocked(
infoInOut.packageName, infoInOut.user, useLowResIcon);
applyCacheEntry(entry, infoInOut);
if (infoInOut.category == PackageItemInfo.CONVERSATIONS) {
infoInOut.title = mContext.getString(R.string.widget_category_conversations);
if (infoInOut.widgetCategory != NO_CATEGORY) {
WidgetSection widgetSection = WidgetSections.getWidgetSections(mContext)
.get(infoInOut.widgetCategory);
infoInOut.title = mContext.getString(widgetSection.mSectionTitle);
infoInOut.contentDescription = mPackageManager.getUserBadgedLabel(
infoInOut.title, infoInOut.user);
}
@@ -804,8 +804,9 @@ public class LoaderTask implements Runnable {
if (appWidgetInfo.restoreStatus !=
LauncherAppWidgetInfo.RESTORE_COMPLETED) {
appWidgetInfo.pendingItemInfo = WidgetsModel.newPendingItemInfo(
appWidgetInfo.providerName);
appWidgetInfo.pendingItemInfo.user = appWidgetInfo.user;
mApp.getContext(),
appWidgetInfo.providerName,
appWidgetInfo.user);
mIconCache.getTitleAndIconForApp(
appWidgetInfo.pendingItemInfo, false);
}
@@ -16,47 +16,41 @@
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 java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
* Represents a {@link Package} in the widget tray section.
*/
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}.
*/
public final String packageName;
/** Represents a widget category shown in the widget tray section. */
@Category public final int category;
public final int widgetCategory;
public PackageItemInfo(String packageName) {
this(packageName, NO_CATEGORY);
public PackageItemInfo(String packageName, UserHandle user) {
this(packageName, NO_CATEGORY, user);
}
public PackageItemInfo(String packageName, @Category int category) {
public PackageItemInfo(String packageName, int widgetCategory, UserHandle user) {
this.packageName = packageName;
this.category = category;
this.widgetCategory = widgetCategory;
this.user = user;
this.itemType = LauncherSettings.Favorites.ITEM_TYPE_NON_ACTIONABLE;
}
public PackageItemInfo(PackageItemInfo copy) {
this.packageName = copy.packageName;
this.category = copy.category;
this.widgetCategory = copy.widgetCategory;
this.user = copy.user;
this.itemType = LauncherSettings.Favorites.ITEM_TYPE_NON_ACTIONABLE;
}
@@ -75,11 +69,13 @@ public class PackageItemInfo extends ItemInfoWithIcon {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
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
public int hashCode() {
return Objects.hash(packageName, user);
return Objects.hash(packageName, user, widgetCategory);
}
}
@@ -1,19 +1,24 @@
package com.android.launcher3.util;
import static com.android.launcher3.widget.WidgetSections.NO_CATEGORY;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
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 String mPackageName;
public int mWidgetCategory;
public UserHandle mUser;
private int mHashCode;
@@ -27,14 +32,31 @@ public class PackageUserKey {
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) {
update(packageName, user);
}
public PackageUserKey(int widgetCategory, UserHandle user) {
update(/* packageName= */ "", widgetCategory, user);
}
public void update(String packageName, UserHandle user) {
update(packageName, NO_CATEGORY, user);
}
private void update(String packageName, int widgetCategory, UserHandle user) {
mPackageName = packageName;
mWidgetCategory = widgetCategory;
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) {
if (!(obj instanceof PackageUserKey)) return false;
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
@Override
public String toString() {
return mPackageName + "#" + mUser;
return mPackageName + "#" + mUser + ",category=" + mWidgetCategory;
}
}
@@ -17,7 +17,7 @@
package com.android.launcher3.widget;
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.graphics.Canvas;
@@ -89,8 +89,8 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
setOnClickListener(ItemClickHandler.INSTANCE);
if (info.pendingItemInfo == null) {
info.pendingItemInfo = new PackageItemInfo(info.providerName.getPackageName());
info.pendingItemInfo.user = info.user;
info.pendingItemInfo = new PackageItemInfo(info.providerName.getPackageName(),
info.user);
cache.updateIconInBackground(this, info.pendingItemInfo);
} else {
reapplyItemInfo(info.pendingItemInfo);
@@ -338,10 +338,11 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
*/
@Nullable
private Drawable getWidgetCategoryIcon() {
switch (mInfo.pendingItemInfo.category) {
case CONVERSATIONS:
return getContext().getDrawable(R.drawable.ic_conversations_widget_category);
if (mInfo.pendingItemInfo.widgetCategory == WidgetSections.NO_CATEGORY) {
return null;
}
return null;
Context context = getContext();
return context.getDrawable(getWidgetSections(context).get(
mInfo.pendingItemInfo.widgetCategory).mSectionDrawable);
}
}
@@ -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);
}
}
}
@@ -16,6 +16,8 @@
package com.android.launcher3.widget.model;
import android.os.Process;
import com.android.launcher3.model.data.PackageItemInfo;
import java.util.Collections;
@@ -26,7 +28,9 @@ import java.util.Collections;
public class WidgetListSpaceEntry extends WidgetsListBaseEntry {
public WidgetListSpaceEntry() {
super(new PackageItemInfo(""), "", Collections.EMPTY_LIST);
super(new PackageItemInfo(/* packageName= */ "", Process.myUserHandle()),
/* titleSectionName= */ "",
Collections.EMPTY_LIST);
mPkgItem.title = "";
}
@@ -24,5 +24,5 @@ public interface OnHeaderClickListener {
/**
* 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);
}
@@ -98,7 +98,7 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
private Predicate<WidgetsListBaseEntry> mHeaderAndSelectedContentFilter = entry ->
entry instanceof WidgetsListHeaderEntry
|| entry instanceof WidgetsListSearchHeaderEntry
|| new PackageUserKey(entry.mPkgItem.packageName, entry.mPkgItem.user)
|| PackageUserKey.fromPackageItemInfo(entry.mPkgItem)
.equals(mWidgetsContentVisiblePackageUserKey);
@Nullable private Predicate<WidgetsListBaseEntry> mFilter = null;
@Nullable private RecyclerView mRecyclerView;
@@ -252,10 +252,11 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
return entry instanceof WidgetsListBaseEntry.Header && matchesKey(entry, key);
}
private static boolean matchesKey(
@NonNull WidgetsListBaseEntry entry, @Nullable PackageUserKey key) {
private static boolean matchesKey(@NonNull WidgetsListBaseEntry entry,
@Nullable PackageUserKey key) {
if (key == null) return false;
return entry.mPkgItem.packageName.equals(key.mPackageName)
&& entry.mPkgItem.widgetCategory == key.mWidgetCategory
&& entry.mPkgItem.user.equals(key.mUser);
}
@@ -434,11 +435,10 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
.filter(entry -> entry instanceof WidgetsListHeaderEntry)
.map(entry -> entry.mPkgItem)
.collect(Collectors.toMap(
entry -> new PackageUserKey(entry.packageName, entry.user),
entry -> PackageUserKey.fromPackageItemInfo(entry),
entry -> entry));
for (WidgetsListBaseEntry visibleEntry: mVisibleEntries) {
PackageUserKey key = new PackageUserKey(visibleEntry.mPkgItem.packageName,
visibleEntry.mPkgItem.user);
PackageUserKey key = PackageUserKey.fromPackageItemInfo(visibleEntry.mPkgItem);
PackageItemInfo packageItemInfo = packagesInfo.get(key);
if (packageItemInfo != null
&& !visibleEntry.mPkgItem.title.equals(packageItemInfo.title)) {
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.widget.picker;
import static com.android.launcher3.widget.WidgetSections.NO_CATEGORY;
import android.content.Context;
import android.content.res.Resources;
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.util.PluralMessageFormat;
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.WidgetsListSearchHeaderEntry;
@@ -173,12 +177,12 @@ public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpd
private void setIcon(PackageItemInfo info) {
Drawable icon;
switch (info.category) {
case PackageItemInfo.CONVERSATIONS:
icon = getContext().getDrawable(R.drawable.ic_conversations_widget_category);
break;
default:
icon = info.newIcon(getContext());
if (info.widgetCategory == NO_CATEGORY) {
icon = info.newIcon(getContext());
} else {
WidgetSection widgetSection = WidgetSections.getWidgetSections(getContext())
.get(info.widgetCategory);
icon = getContext().getDrawable(widgetSection.mSectionDrawable);
}
applyDrawables(icon);
mIconDrawable = icon;
@@ -62,9 +62,7 @@ public final class WidgetsListHeaderViewHolderBinder implements
(position & POSITION_LAST) != 0,
/* isExpanded= */ data.isWidgetListShown()));
widgetsListHeader.setOnExpandChangeListener(isExpanded ->
mOnHeaderClickListener.onHeaderClicked(
isExpanded,
new PackageUserKey(data.mPkgItem.packageName, data.mPkgItem.user)
));
mOnHeaderClickListener.onHeaderClicked(isExpanded,
PackageUserKey.fromPackageItemInfo(data.mPkgItem)));
}
}
@@ -64,6 +64,6 @@ public final class WidgetsListSearchHeaderViewHolderBinder implements
/* isExpanded= */ data.isWidgetListShown()));
widgetsListHeader.setOnExpandChangeListener(isExpanded ->
mOnHeaderClickListener.onHeaderClicked(isExpanded,
new PackageUserKey(data.mPkgItem.packageName, data.mPkgItem.user)));
PackageUserKey.fromPackageItemInfo(data.mPkgItem)));
}
}