Various icon cache fixes

> Multiple instances of LauncherIcon created when
    LauncherIcons refers IconCache which in turn creates new LauncherIcons
> Widget icons are never cached as they were using low res icons
> Shortcut drag icons are not loaded synchronously
    when using PinItemRequest flow
> Wrong lastUpdatedTime is used in iconCache for shortcuts
> IconUpdateHandler does not ignore managedUser promise icons

Change-Id: Ie7eed68a30fad11d1861b6c70c380953a15ae1cf
This commit is contained in:
Sunny Goyal 2020-02-06 11:28:01 -08:00
parent a0912d502a
commit 18204e4eea
24 changed files with 250 additions and 281 deletions

View File

@ -22,7 +22,7 @@ import android.os.UserHandle;
import androidx.annotation.Nullable;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.icons.ComponentWithLabel;
import com.android.launcher3.icons.ComponentWithLabelAndIcon;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.widget.WidgetListRowEntry;
@ -59,7 +59,7 @@ public class WidgetsModel {
* @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
* only widgets and shortcuts associated with the package/user are.
*/
public List<ComponentWithLabel> update(LauncherAppState app,
public List<ComponentWithLabelAndIcon> update(LauncherAppState app,
@Nullable PackageUserKey packageUser) {
return Collections.emptyList();
}

View File

@ -350,6 +350,17 @@ public class BaseIconFactory implements AutoCloseable {
iconDpi);
}
/**
* Badges the provided source with the badge info
*/
public BitmapInfo badgeBitmap(Bitmap source, BitmapInfo badgeInfo) {
Bitmap icon = BitmapRenderer.createHardwareBitmap(mIconBitmapSize, mIconBitmapSize, (c) -> {
getShadowGenerator().recreateIcon(source, c);
badgeWithDrawable(c, new FixedSizeBitmapDrawable(badgeInfo.icon));
});
return BitmapInfo.of(icon, badgeInfo.color);
}
private int extractColor(Bitmap bitmap) {
return mDisableColorExtractor ? 0 : mColorExtractor.findDominantColorByHue(bitmap);
}

View File

@ -281,7 +281,8 @@ public abstract class BaseIconCache {
ContentValues values = newContentValues(entry.bitmap, entry.title.toString(),
componentName.getPackageName(), cachingLogic.getKeywords(object, mLocaleList));
addIconToDB(values, componentName, info, userSerial);
addIconToDB(values, componentName, info, userSerial,
cachingLogic.getLastUpdatedTime(object, info));
}
/**
@ -289,10 +290,10 @@ public abstract class BaseIconCache {
* @param values {@link ContentValues} containing icon & title
*/
private void addIconToDB(ContentValues values, ComponentName key,
PackageInfo info, long userSerial) {
PackageInfo info, long userSerial, long lastUpdateTime) {
values.put(IconDB.COLUMN_COMPONENT, key.flattenToString());
values.put(IconDB.COLUMN_USER, userSerial);
values.put(IconDB.COLUMN_LAST_UPDATED, info.lastUpdateTime);
values.put(IconDB.COLUMN_LAST_UPDATED, lastUpdateTime);
values.put(IconDB.COLUMN_VERSION, info.versionCode);
mIconDb.insertOrReplace(values);
}
@ -362,7 +363,8 @@ public abstract class BaseIconCache {
}
if (object != null) {
entry.title = cachingLogic.getLabel(object);
entry.contentDescription = mPackageManager.getUserBadgedLabel(entry.title, user);
entry.contentDescription = mPackageManager.getUserBadgedLabel(
cachingLogic.getDescription(object, entry.title), user);
}
}
}
@ -449,7 +451,8 @@ public abstract class BaseIconCache {
// package updates.
ContentValues values = newContentValues(
iconInfo, entry.title.toString(), packageName, null);
addIconToDB(values, cacheKey.componentName, info, getSerialNumberForUser(user));
addIconToDB(values, cacheKey.componentName, info, getSerialNumberForUser(user),
info.lastUpdateTime);
} catch (NameNotFoundException e) {
if (DEBUG) Log.d(TAG, "Application not installed " + packageName);

View File

@ -34,6 +34,10 @@ public interface CachingLogic<T> {
CharSequence getLabel(T object);
default CharSequence getDescription(T object, CharSequence fallback) {
return fallback;
}
@NonNull
BitmapInfo loadIcon(Context context, T object);

View File

@ -24,6 +24,7 @@ import android.database.sqlite.SQLiteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseBooleanArray;
@ -61,7 +62,7 @@ public class IconCacheUpdateHandler {
private final HashMap<String, PackageInfo> mPkgInfoMap;
private final BaseIconCache mIconCache;
private final HashMap<UserHandle, Set<String>> mPackagesToIgnore = new HashMap<>();
private final ArrayMap<UserHandle, Set<String>> mPackagesToIgnore = new ArrayMap<>();
private final SparseBooleanArray mItemsToDelete = new SparseBooleanArray();
private boolean mFilterMode = MODE_SET_INVALID_ITEMS;
@ -77,8 +78,16 @@ public class IconCacheUpdateHandler {
createPackageInfoMap();
}
public void setPackagesToIgnore(UserHandle userHandle, Set<String> packages) {
mPackagesToIgnore.put(userHandle, packages);
/**
* Sets a package to ignore for processing
*/
public void addPackagesToIgnore(UserHandle userHandle, String packageName) {
Set<String> packages = mPackagesToIgnore.get(userHandle);
if (packages == null) {
packages = new HashSet<>();
mPackagesToIgnore.put(userHandle, packages);
}
packages.add(packageName);
}
private void createPackageInfoMap() {
@ -181,6 +190,7 @@ public class IconCacheUpdateHandler {
}
continue;
}
if (app == null) {
if (mFilterMode == MODE_SET_INVALID_ITEMS) {
mIconCache.remove(component, user);
@ -263,6 +273,7 @@ public class IconCacheUpdateHandler {
T app = mAppsToUpdate.pop();
String pkg = mCachingLogic.getComponent(app).getPackageName();
PackageInfo info = mPkgInfoMap.get(pkg);
mIconCache.addIconToDBAndMemCache(
app, mCachingLogic, info, mUserSerial, true /*replace existing*/);
mUpdatedPackages.add(pkg);

View File

@ -43,7 +43,6 @@ import com.android.launcher3.LauncherAppState;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.util.InstantAppResolver;
@ -170,14 +169,7 @@ public class DynamicItemCache {
List<ShortcutInfo> details = shortcutKey.buildRequest(mContext).query(ShortcutRequest.ALL);
if (!details.isEmpty()) {
WorkspaceItemInfo si = new WorkspaceItemInfo(details.get(0), mContext);
try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
si.bitmap = li.createShortcutIcon(details.get(0), true /* badged */, null);
} catch (Exception e) {
if (DEBUG) {
Log.e(TAG, "Error loading shortcut icon for " + shortcutKey.toString());
}
return null;
}
mIconCache.getShortcutIcon(si, details.get(0));
return si;
}
if (DEBUG) {

View File

@ -17,7 +17,6 @@
package com.android.launcher3;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.ShortcutUtil.fetchAndUpdateShortcutIconAsync;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
@ -44,7 +43,6 @@ import android.util.Pair;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.GraphicsUtils;
import com.android.launcher3.icons.LauncherIcons;
@ -61,7 +59,6 @@ import org.json.JSONStringer;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
@ -491,13 +488,8 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
return Pair.create(si, null);
} else if (shortcutInfo != null) {
WorkspaceItemInfo itemInfo = new WorkspaceItemInfo(shortcutInfo, mContext);
if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
fetchAndUpdateShortcutIconAsync(mContext, itemInfo, shortcutInfo, true);
} else {
LauncherIcons li = LauncherIcons.obtain(mContext);
itemInfo.bitmap = li.createShortcutIcon(shortcutInfo);
li.recycle();
}
LauncherAppState.getInstance(mContext).getIconCache().getShortcutIcon(
itemInfo, shortcutInfo);
return Pair.create(itemInfo, shortcutInfo);
} else if (providerInfo != null) {
LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo

View File

@ -7,10 +7,12 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.UserHandle;
import com.android.launcher3.icons.ComponentWithLabel;
import com.android.launcher3.icons.ComponentWithLabelAndIcon;
import com.android.launcher3.icons.IconCache;
/**
* This class is a thin wrapper around the framework AppWidgetProviderInfo class. This class affords
@ -19,7 +21,7 @@ import com.android.launcher3.icons.ComponentWithLabel;
* as opposed to a widget instance, and so should not be confused with {@link LauncherAppWidgetInfo}
*/
public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo
implements ComponentWithLabel {
implements ComponentWithLabelAndIcon {
public static final String CLS_CUSTOM_WIDGET_PREFIX = "#custom-widget-";
@ -111,4 +113,9 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo
public final UserHandle getUser() {
return getProfile();
}
@Override
public Drawable getFullResIcon(IconCache cache) {
return cache.getFullResIcon(provider.getPackageName(), icon);
}
}

View File

@ -37,7 +37,6 @@ import androidx.annotation.WorkerThread;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.AddWorkspaceItemsTask;
import com.android.launcher3.model.AllAppsList;
@ -573,9 +572,7 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi
public void updateAndBindWorkspaceItem(WorkspaceItemInfo si, ShortcutInfo info) {
updateAndBindWorkspaceItem(() -> {
si.updateFromDeepShortcutInfo(info, mApp.getContext());
LauncherIcons li = LauncherIcons.obtain(mApp.getContext());
si.bitmap = li.createShortcutIcon(info);
li.recycle();
mApp.getIconCache().getShortcutIcon(si, info);
return si;
});
}

View File

@ -584,9 +584,8 @@ public final class Utilities {
return new FixedSizeEmptyDrawable(iconSize);
}
ShortcutInfo si = (ShortcutInfo) obj;
LauncherIcons li = LauncherIcons.obtain(appState.getContext());
Bitmap badge = li.getShortcutInfoBadge(si, appState.getIconCache()).bitmap.icon;
li.recycle();
Bitmap badge = LauncherAppState.getInstance(appState.getContext())
.getIconCache().getShortcutInfoBadge(si).icon;
float badgeSize = LauncherIcons.getBadgeSizeForIconSize(iconSize);
float insetFraction = (iconSize - badgeSize) / iconSize;
return new InsetDrawable(new FastBitmapDrawable(badge),

View File

@ -31,7 +31,7 @@ public interface ComponentWithLabel {
CharSequence getLabel(PackageManager pm);
class ComponentCachingLogic implements CachingLogic<ComponentWithLabel> {
class ComponentCachingLogic<T extends ComponentWithLabel> implements CachingLogic<T> {
private final PackageManager mPackageManager;
private final boolean mAddToMemCache;
@ -42,22 +42,22 @@ public interface ComponentWithLabel {
}
@Override
public ComponentName getComponent(ComponentWithLabel object) {
public ComponentName getComponent(T object) {
return object.getComponent();
}
@Override
public UserHandle getUser(ComponentWithLabel object) {
public UserHandle getUser(T object) {
return object.getUser();
}
@Override
public CharSequence getLabel(ComponentWithLabel object) {
public CharSequence getLabel(T object) {
return object.getLabel(mPackageManager);
}
@Override
public BitmapInfo loadIcon(Context context, ComponentWithLabel object) {
public BitmapInfo loadIcon(Context context, T object) {
return BitmapInfo.LOW_RES_INFO;
}

View File

@ -0,0 +1,54 @@
/*
* Copyright (C) 2020 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.icons;
import android.content.Context;
import android.graphics.drawable.Drawable;
import androidx.annotation.NonNull;
import com.android.launcher3.LauncherAppState;
/**
* Extension of ComponentWithLabel to also support loading icons
*/
public interface ComponentWithLabelAndIcon extends ComponentWithLabel {
/**
* Provide an icon for this object
*/
Drawable getFullResIcon(IconCache cache);
class ComponentWithIconCachingLogic extends ComponentCachingLogic<ComponentWithLabelAndIcon> {
public ComponentWithIconCachingLogic(Context context, boolean addToMemCache) {
super(context, addToMemCache);
}
@NonNull
@Override
public BitmapInfo loadIcon(Context context, ComponentWithLabelAndIcon object) {
Drawable d = object.getFullResIcon(LauncherAppState.getInstance(context)
.getIconCache());
if (d == null) {
return super.loadIcon(context, object);
}
try (LauncherIcons li = LauncherIcons.obtain(context)) {
return li.createBadgedIconBitmap(d, object.getUser(), 0);
}
}
}
}

View File

@ -19,6 +19,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 android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@ -56,6 +57,7 @@ import com.android.launcher3.util.InstantAppResolver;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions;
import java.util.function.Predicate;
import java.util.function.Supplier;
/**
@ -65,6 +67,9 @@ public class IconCache extends BaseIconCache {
private static final String TAG = "Launcher.IconCache";
private final Predicate<ItemInfoWithIcon> mIsUsingFallbackIconCheck = w -> w.bitmap != null
&& w.bitmap.isNullOrLowRes() && !isDefaultIcon(w.bitmap, w.user);
private final CachingLogic<ComponentWithLabel> mComponentWithLabelCachingLogic;
private final CachingLogic<LauncherActivityInfo> mLauncherActivityInfoCachingLogic;
private final CachingLogic<ShortcutInfo> mShortcutCachingLogic;
@ -179,6 +184,77 @@ public class IconCache extends BaseIconCache {
getTitleAndIcon(info, () -> activityInfo, false, useLowResIcon);
}
/**
* Fill in {@param info} with the icon for {@param si}
*/
public void getShortcutIcon(ItemInfoWithIcon info, ShortcutInfo si) {
getShortcutIcon(info, si, true, mIsUsingFallbackIconCheck);
}
/**
* Fill in {@param info} with an unbadged icon for {@param si}
*/
public void getUnbadgedShortcutIcon(ItemInfoWithIcon info, ShortcutInfo si) {
getShortcutIcon(info, si, false, mIsUsingFallbackIconCheck);
}
/**
* Fill in {@param info} with the icon and label for {@param si}. If the icon is not
* available, and fallback check returns true, it keeps the old icon.
*/
public <T extends ItemInfoWithIcon> void getShortcutIcon(T info, ShortcutInfo si,
@NonNull Predicate<T> fallbackIconCheck) {
getShortcutIcon(info, si, true /* use badged */, fallbackIconCheck);
}
private synchronized <T extends ItemInfoWithIcon> void getShortcutIcon(T info, ShortcutInfo si,
boolean useBadged, @NonNull Predicate<T> fallbackIconCheck) {
BitmapInfo bitmapInfo;
if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
bitmapInfo = cacheLocked(ShortcutKey.fromInfo(si).componentName, si.getUserHandle(),
() -> si, mShortcutCachingLogic, false, false).bitmap;
} else {
// If caching is disabled, load the full icon
bitmapInfo = mShortcutCachingLogic.loadIcon(mContext, si);
}
if (bitmapInfo.isNullOrLowRes()) {
bitmapInfo = getDefaultIcon(si.getUserHandle());
}
if (isDefaultIcon(bitmapInfo, si.getUserHandle()) && fallbackIconCheck.test(info)) {
return;
}
info.bitmap = bitmapInfo;
if (useBadged) {
BitmapInfo badgeInfo = getShortcutInfoBadge(si);
try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
info.bitmap = li.badgeBitmap(info.bitmap.icon, badgeInfo);
}
}
}
/**
* Returns the badging info for the shortcut
*/
public BitmapInfo getShortcutInfoBadge(ShortcutInfo shortcutInfo) {
ComponentName cn = shortcutInfo.getActivity();
if (cn != null) {
// Get the app info for the source activity.
AppInfo appInfo = new AppInfo();
appInfo.user = shortcutInfo.getUserHandle();
appInfo.componentName = cn;
appInfo.intent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_LAUNCHER)
.setComponent(cn);
getTitleAndIcon(appInfo, false);
return appInfo.bitmap;
} else {
PackageItemInfo pkgInfo = new PackageItemInfo(shortcutInfo.getPackage());
getTitleAndIconForApp(pkgInfo, false);
return pkgInfo.bitmap;
}
}
/**
* Fill in info with the icon and label for deep shortcut.
*/

View File

@ -16,28 +16,11 @@
package com.android.launcher3.icons;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Process;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import com.android.launcher3.AppInfo;
import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.util.Themes;
import java.util.function.Supplier;
/**
* Wrapper class to provide access to {@link BaseIconFactory} and also to provide pool of this class
@ -111,114 +94,4 @@ public class LauncherIcons extends BaseIconFactory implements AutoCloseable {
public void close() {
recycle();
}
// below methods should also migrate to BaseIconFactory
public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo) {
return createShortcutIcon(shortcutInfo, true /* badged */);
}
public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo, boolean badged) {
return createShortcutIcon(shortcutInfo, badged, null);
}
public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo, boolean badged,
@Nullable Supplier<ItemInfoWithIcon> fallbackIconProvider) {
if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
return createShortcutIconCached(shortcutInfo, badged, true, fallbackIconProvider);
} else {
return createShortcutIconLegacy(shortcutInfo, badged, fallbackIconProvider);
}
}
public BitmapInfo createShortcutIconLegacy(ShortcutInfo shortcutInfo, boolean badged,
@Nullable Supplier<ItemInfoWithIcon> fallbackIconProvider) {
Drawable unbadgedDrawable = ShortcutCachingLogic.getIcon(
mContext, shortcutInfo, mFillResIconDpi);
IconCache cache = LauncherAppState.getInstance(mContext).getIconCache();
final Bitmap unbadgedBitmap;
if (unbadgedDrawable != null) {
unbadgedBitmap = createScaledBitmapWithoutShadow(unbadgedDrawable, 0);
} else {
if (fallbackIconProvider != null) {
// Fallback icons are already badged and with appropriate shadow
ItemInfoWithIcon fullIcon = fallbackIconProvider.get();
if (fullIcon != null && fullIcon.bitmap != null) {
return fullIcon.bitmap;
}
}
unbadgedBitmap = cache.getDefaultIcon(Process.myUserHandle()).icon;
}
if (!badged) {
return BitmapInfo.of(unbadgedBitmap, Themes.getColorAccent(mContext));
}
final Bitmap unbadgedfinal = unbadgedBitmap;
final ItemInfoWithIcon badge = getShortcutInfoBadge(shortcutInfo, cache);
Bitmap icon = BitmapRenderer.createHardwareBitmap(mIconBitmapSize, mIconBitmapSize, (c) -> {
getShadowGenerator().recreateIcon(unbadgedfinal, c);
badgeWithDrawable(c, new FastBitmapDrawable(badge.bitmap));
});
return BitmapInfo.of(icon, badge.bitmap.color);
}
@WorkerThread
public BitmapInfo createShortcutIconCached(ShortcutInfo shortcutInfo, boolean badged,
boolean useCache, @Nullable Supplier<ItemInfoWithIcon> fallbackIconProvider) {
IconCache cache = LauncherAppState.getInstance(mContext).getIconCache();
final BitmapInfo bitmapInfo;
if (useCache) {
bitmapInfo = cache.getDeepShortcutTitleAndIcon(shortcutInfo).bitmap;
} else {
bitmapInfo = new ShortcutCachingLogic().loadIcon(mContext, shortcutInfo);
}
final Bitmap unbadgedBitmap;
if (bitmapInfo.icon != null) {
unbadgedBitmap = bitmapInfo.icon;
} else {
if (fallbackIconProvider != null) {
// Fallback icons are already badged and with appropriate shadow
ItemInfoWithIcon fullIcon = fallbackIconProvider.get();
if (fullIcon != null && fullIcon.bitmap != null) {
return fullIcon.bitmap;
}
}
unbadgedBitmap = cache.getDefaultIcon(Process.myUserHandle()).icon;
}
if (!badged) {
return BitmapInfo.of(unbadgedBitmap, Themes.getColorAccent(mContext));
}
final Bitmap unbadgedfinal = unbadgedBitmap;
final ItemInfoWithIcon badge = getShortcutInfoBadge(shortcutInfo, cache);
Bitmap icon = BitmapRenderer.createHardwareBitmap(mIconBitmapSize, mIconBitmapSize, (c) -> {
getShadowGenerator().recreateIcon(unbadgedfinal, c);
badgeWithDrawable(c, new FastBitmapDrawable(badge.bitmap));
});
return BitmapInfo.of(icon, badge.bitmap.color);
}
public ItemInfoWithIcon getShortcutInfoBadge(ShortcutInfo shortcutInfo, IconCache cache) {
ComponentName cn = shortcutInfo.getActivity();
String badgePkg = shortcutInfo.getPackage();
boolean hasBadgePkgSet = !badgePkg.equals(shortcutInfo.getPackage());
if (cn != null && !hasBadgePkgSet) {
// Get the app info for the source activity.
AppInfo appInfo = new AppInfo();
appInfo.user = shortcutInfo.getUserHandle();
appInfo.componentName = cn;
appInfo.intent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_LAUNCHER)
.setComponent(cn);
cache.getTitleAndIcon(appInfo, false);
return appInfo;
} else {
PackageItemInfo pkgInfo = new PackageItemInfo(badgePkg);
cache.getTitleAndIconForApp(pkgInfo, false);
return pkgInfo;
}
}
}

View File

@ -25,6 +25,7 @@ import android.content.pm.PackageInfo;
import android.content.pm.ShortcutInfo;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
@ -57,6 +58,12 @@ public class ShortcutCachingLogic implements CachingLogic<ShortcutInfo> {
return info.getShortLabel();
}
@Override
public CharSequence getDescription(ShortcutInfo object, CharSequence fallback) {
CharSequence label = object.getLongLabel();
return TextUtils.isEmpty(label) ? fallback : label;
}
@NonNull
@Override
public BitmapInfo loadIcon(Context context, ShortcutInfo info) {

View File

@ -16,6 +16,8 @@
package com.android.launcher3.model;
import static android.graphics.BitmapFactory.decodeByteArray;
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
@ -26,7 +28,6 @@ import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.CursorWrapper;
import android.graphics.BitmapFactory;
import android.os.UserHandle;
import android.provider.BaseColumns;
import android.text.TextUtils;
@ -166,37 +167,30 @@ public class LoaderCursor extends CursorWrapper {
*/
protected boolean loadIcon(WorkspaceItemInfo info) {
try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
return loadIcon(info, li);
}
}
/**
* Loads the icon from the cursor and updates the {@param info} if the icon is an app resource.
*/
protected boolean loadIcon(WorkspaceItemInfo info, LauncherIcons li) {
if (itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
String packageName = getString(iconPackageIndex);
String resourceName = getString(iconResourceIndex);
if (!TextUtils.isEmpty(packageName) || !TextUtils.isEmpty(resourceName)) {
info.iconResource = new ShortcutIconResource();
info.iconResource.packageName = packageName;
info.iconResource.resourceName = resourceName;
BitmapInfo iconInfo = li.createIconBitmap(info.iconResource);
if (iconInfo != null) {
info.bitmap = iconInfo;
return true;
if (itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
String packageName = getString(iconPackageIndex);
String resourceName = getString(iconResourceIndex);
if (!TextUtils.isEmpty(packageName) || !TextUtils.isEmpty(resourceName)) {
info.iconResource = new ShortcutIconResource();
info.iconResource.packageName = packageName;
info.iconResource.resourceName = resourceName;
BitmapInfo iconInfo = li.createIconBitmap(info.iconResource);
if (iconInfo != null) {
info.bitmap = iconInfo;
return true;
}
}
}
}
// Failed to load from resource, try loading from DB.
byte[] data = getBlob(iconIndex);
try {
info.bitmap = li.createIconBitmap(BitmapFactory.decodeByteArray(data, 0, data.length));
return true;
} catch (Exception e) {
Log.e(TAG, "Failed to decode byte array for info " + info, e);
return false;
// Failed to load from resource, try loading from DB.
byte[] data = getBlob(iconIndex);
try {
info.bitmap = li.createIconBitmap(decodeByteArray(data, 0, data.length));
return true;
} catch (Exception e) {
Log.e(TAG, "Failed to decode byte array for info " + info, e);
return false;
}
}
}

View File

@ -35,7 +35,6 @@ import android.content.pm.LauncherApps;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.ShortcutInfo;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
@ -50,7 +49,6 @@ import com.android.launcher3.AppInfo;
import com.android.launcher3.FolderInfo;
import com.android.launcher3.InstallShortcutReceiver;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherModel;
@ -61,11 +59,10 @@ import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderGridOrganizer;
import com.android.launcher3.folder.FolderNameProvider;
import com.android.launcher3.icons.ComponentWithLabel;
import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
import com.android.launcher3.icons.ComponentWithLabelAndIcon;
import com.android.launcher3.icons.ComponentWithLabelAndIcon.ComponentWithIconCachingLogic;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.LauncherActivityCachingLogic;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.icons.ShortcutCachingLogic;
import com.android.launcher3.icons.cache.IconCacheUpdateHandler;
import com.android.launcher3.logging.FileLog;
@ -94,7 +91,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CancellationException;
import java.util.function.Supplier;
/**
* Runnable for the thread that loads the contents of the launcher:
@ -263,7 +259,8 @@ public class LoaderTask implements Runnable {
verifyNotStopped();
// fourth step
List<ComponentWithLabel> allWidgetsList = mBgDataModel.widgetsModel.update(mApp, null);
List<ComponentWithLabelAndIcon> allWidgetsList =
mBgDataModel.widgetsModel.update(mApp, null);
logger.addSplit("load widgets");
verifyNotStopped();
@ -271,8 +268,9 @@ public class LoaderTask implements Runnable {
logger.addSplit("bindWidgets");
verifyNotStopped();
updateHandler.updateIcons(allWidgetsList, new ComponentCachingLogic(
mApp.getContext(), true), mApp.getModel()::onWidgetLabelsUpdated);
updateHandler.updateIcons(allWidgetsList,
new ComponentWithIconCachingLogic(mApp.getContext(), true),
mApp.getModel()::onWidgetLabelsUpdated);
logger.addSplit("save widgets in icon cache");
// fifth step
@ -544,17 +542,10 @@ public class LoaderTask implements Runnable {
continue;
}
info = new WorkspaceItemInfo(pinnedShortcut, context);
final WorkspaceItemInfo finalInfo = info;
LauncherIcons li = LauncherIcons.obtain(context);
// If the pinned deep shortcut is no longer published,
// use the last saved icon instead of the default.
Supplier<ItemInfoWithIcon> fallbackIconProvider = () ->
c.loadIcon(finalInfo, li) ? finalInfo : null;
info.bitmap = li.createShortcutIcon(
pinnedShortcut, true /* badged */,
fallbackIconProvider);
li.recycle();
mIconCache.getShortcutIcon(info, pinnedShortcut, c::loadIcon);
if (pmHelper.isAppSuspended(
pinnedShortcut.getPackage(), info.user)) {
info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED;
@ -854,23 +845,23 @@ public class LoaderTask implements Runnable {
private void setIgnorePackages(IconCacheUpdateHandler updateHandler) {
// Ignore packages which have a promise icon.
HashSet<String> packagesToIgnore = new HashSet<>();
synchronized (mBgDataModel) {
for (ItemInfo info : mBgDataModel.itemsIdMap) {
if (info instanceof WorkspaceItemInfo) {
WorkspaceItemInfo si = (WorkspaceItemInfo) info;
if (si.isPromise() && si.getTargetComponent() != null) {
packagesToIgnore.add(si.getTargetComponent().getPackageName());
updateHandler.addPackagesToIgnore(
si.user, si.getTargetComponent().getPackageName());
}
} else if (info instanceof LauncherAppWidgetInfo) {
LauncherAppWidgetInfo lawi = (LauncherAppWidgetInfo) info;
if (lawi.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) {
packagesToIgnore.add(lawi.providerName.getPackageName());
updateHandler.addPackagesToIgnore(
lawi.user, lawi.providerName.getPackageName());
}
}
}
}
updateHandler.setPackagesToIgnore(Process.myUserHandle(), packagesToIgnore);
}
private List<LauncherActivityInfo> loadAllApps() {

View File

@ -23,7 +23,6 @@ import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.util.ItemInfoMatcher;
@ -89,12 +88,7 @@ public class ShortcutsChangedTask extends BaseModelUpdateTask {
}
for (final WorkspaceItemInfo workspaceItemInfo : workspaceItemInfos) {
workspaceItemInfo.updateFromDeepShortcutInfo(fullDetails, context);
// If the shortcut is pinned but no longer has an icon in the system,
// keep the current icon instead of reverting to the default icon.
LauncherIcons li = LauncherIcons.obtain(context);
workspaceItemInfo.bitmap = li.createShortcutIcon(
fullDetails, true, () -> workspaceItemInfo);
li.recycle();
app.getIconCache().getShortcutIcon(workspaceItemInfo, fullDetails);
updatedWorkspaceItemInfos.add(workspaceItemInfo);
}
}

View File

@ -26,7 +26,6 @@ import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.shortcuts.ShortcutRequest.QueryResult;
@ -89,11 +88,7 @@ public class UserLockStateChangedTask extends BaseModelUpdateTask {
}
si.runtimeStatusFlags &= ~FLAG_DISABLED_LOCKED_USER;
si.updateFromDeepShortcutInfo(shortcut, context);
// If the shortcut is pinned but no longer has an icon in the system,
// keep the current icon instead of reverting to the default icon.
LauncherIcons li = LauncherIcons.obtain(context);
si.bitmap = li.createShortcutIcon(shortcut, true, () -> si);
li.recycle();
app.getIconCache().getShortcutIcon(si, shortcut);
} else {
si.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
}

View File

@ -17,7 +17,6 @@
package com.android.launcher3.pm;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.ShortcutUtil.fetchAndUpdateShortcutIconAsync;
import android.annotation.TargetApi;
import android.content.Context;
@ -32,8 +31,7 @@ import androidx.annotation.Nullable;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.icons.ShortcutCachingLogic;
public class PinRequestHelper {
@ -82,16 +80,10 @@ public class PinRequestHelper {
ShortcutInfo si = request.getShortcutInfo();
WorkspaceItemInfo info = new WorkspaceItemInfo(si, context);
// Apply the unbadged icon and fetch the actual icon asynchronously.
if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
fetchAndUpdateShortcutIconAsync(context, info, si, false);
} else {
LauncherIcons li = LauncherIcons.obtain(context);
info.bitmap = li.createShortcutIcon(si, false /* badged */);
li.recycle();
LauncherAppState.getInstance(context).getModel()
.updateAndBindWorkspaceItem(info, si);
}
// Apply the unbadged icon synchronously using the caching logic directly and
// fetch the actual icon asynchronously.
info.bitmap = new ShortcutCachingLogic().loadIcon(context, si);
LauncherAppState.getInstance(context).getModel().updateAndBindWorkspaceItem(info, si);
return info;
} else {
return null;

View File

@ -41,7 +41,7 @@ import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.icons.ComponentWithLabel;
import com.android.launcher3.icons.ComponentWithLabelAndIcon;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.util.PackageUserKey;
@ -51,7 +51,7 @@ import java.util.List;
/**
* Wrapper class for representing a shortcut configure activity.
*/
public abstract class ShortcutConfigActivityInfo implements ComponentWithLabel {
public abstract class ShortcutConfigActivityInfo implements ComponentWithLabelAndIcon {
private static final String TAG = "SCActivityInfo";
@ -77,6 +77,7 @@ public abstract class ShortcutConfigActivityInfo implements ComponentWithLabel {
return LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
}
@Override
public abstract Drawable getFullResIcon(IconCache cache);
/**

View File

@ -26,8 +26,9 @@ import androidx.annotation.VisibleForTesting;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.notification.NotificationInfo;
import com.android.launcher3.notification.NotificationKeyData;
import com.android.launcher3.notification.NotificationListener;
@ -153,13 +154,11 @@ public class PopupPopulator {
String shortcutIdToDeDupe = notificationKeys.isEmpty() ? null
: notificationKeys.get(0).shortcutId;
shortcuts = PopupPopulator.sortAndFilterShortcuts(shortcuts, shortcutIdToDeDupe);
IconCache cache = LauncherAppState.getInstance(launcher).getIconCache();
for (int i = 0; i < shortcuts.size() && i < shortcutViews.size(); i++) {
final ShortcutInfo shortcut = shortcuts.get(i);
final WorkspaceItemInfo si = new WorkspaceItemInfo(shortcut, launcher);
// Use unbadged icon for the menu.
LauncherIcons li = LauncherIcons.obtain(launcher);
si.bitmap = li.createShortcutIcon(shortcut, false /* badged */);
li.recycle();
cache.getUnbadgedShortcutIcon(si, shortcut);
si.rank = i;
final DeepShortcutView view = shortcutViews.get(i);

View File

@ -15,19 +15,10 @@
*/
package com.android.launcher3.util;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.content.Context;
import android.content.pm.ShortcutInfo;
import androidx.annotation.NonNull;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.Utilities;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.shortcuts.ShortcutKey;
@ -70,21 +61,6 @@ public class ShortcutUtil {
&& info instanceof WorkspaceItemInfo;
}
/**
* Fetch the shortcut icon in background, then update the UI.
*/
public static void fetchAndUpdateShortcutIconAsync(
@NonNull Context context, @NonNull WorkspaceItemInfo info, @NonNull ShortcutInfo si,
boolean badged) {
MODEL_EXECUTOR.execute(() -> {
try (LauncherIcons li = LauncherIcons.obtain(context)) {
info.bitmap = li.createShortcutIcon(si, badged, null);
LauncherAppState.getInstance(context).getModel()
.updateAndBindWorkspaceItem(info, si);
}
});
}
private static boolean isActive(ItemInfo info) {
boolean isLoading = info instanceof WorkspaceItemInfo
&& ((WorkspaceItemInfo) info).hasPromiseIconUi();

View File

@ -21,7 +21,7 @@ import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.AlphabeticIndexCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.ComponentWithLabel;
import com.android.launcher3.icons.ComponentWithLabelAndIcon;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.pm.ShortcutConfigActivityInfo;
import com.android.launcher3.util.MultiHashMap;
@ -85,12 +85,13 @@ public class WidgetsModel {
* @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
* only widgets and shortcuts associated with the package/user are.
*/
public List<ComponentWithLabel> update(LauncherAppState app, @Nullable PackageUserKey packageUser) {
public List<ComponentWithLabelAndIcon> update(
LauncherAppState app, @Nullable PackageUserKey packageUser) {
Preconditions.assertWorkerThread();
Context context = app.getContext();
final ArrayList<WidgetItem> widgetsAndShortcuts = new ArrayList<>();
List<ComponentWithLabel> updatedItems = new ArrayList<>();
List<ComponentWithLabelAndIcon> updatedItems = new ArrayList<>();
try {
InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
PackageManager pm = app.getContext().getPackageManager();