Storing BitmapInfo instead of icon and color directly in itemInfo

This will allow subclassing BitmapInfo to support custom icon/dynamic
icons which can be loaded on the background thread instead of going
through IconFactory which runs on UiThread

Change-Id: Ieced6e91330bdff1b505826d097a8df711dfe967
This commit is contained in:
Sunny Goyal 2019-10-25 13:41:28 -07:00
parent 0d9752c647
commit 3808a69a6c
35 changed files with 135 additions and 149 deletions

View File

@ -18,32 +18,45 @@ package com.android.launcher3.icons;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class BitmapInfo {
public static final Bitmap LOW_RES_ICON = Bitmap.createBitmap(1, 1, Config.ALPHA_8);
public static final BitmapInfo LOW_RES_INFO = fromBitmap(LOW_RES_ICON, null);
public Bitmap icon;
public int color;
public final Bitmap icon;
public final int color;
public void applyTo(BitmapInfo info) {
info.icon = icon;
info.color = color;
public BitmapInfo(Bitmap icon, int color) {
this.icon = icon;
this.color = color;
}
/**
* Ideally icon should not be null, except in cases when generating hardware bitmap failed
*/
public final boolean isNullOrLowRes() {
return icon == null || icon == LOW_RES_ICON;
}
public final boolean isLowRes() {
return LOW_RES_ICON == icon;
}
public static BitmapInfo fromBitmap(Bitmap bitmap) {
return fromBitmap(bitmap, null);
public static BitmapInfo fromBitmap(@NonNull Bitmap bitmap) {
return of(bitmap, 0);
}
public static BitmapInfo fromBitmap(Bitmap bitmap, ColorExtractor dominantColorExtractor) {
BitmapInfo info = new BitmapInfo();
info.icon = bitmap;
info.color = dominantColorExtractor != null
public static BitmapInfo fromBitmap(@NonNull Bitmap bitmap,
@Nullable ColorExtractor dominantColorExtractor) {
return of(bitmap, dominantColorExtractor != null
? dominantColorExtractor.findDominantColorByHue(bitmap)
: 0;
return info;
: 0);
}
public static BitmapInfo of(@NonNull Bitmap bitmap, int color) {
return new BitmapInfo(bitmap, color);
}
}

View File

@ -71,7 +71,10 @@ public abstract class BaseIconCache {
// Empty class name is used for storing package default entry.
public static final String EMPTY_CLASS_NAME = ".";
public static class CacheEntry extends BitmapInfo {
public static class CacheEntry {
@NonNull
public BitmapInfo bitmap = BitmapInfo.LOW_RES_INFO;
public CharSequence title = "";
public CharSequence contentDescription = "";
}
@ -259,23 +262,23 @@ public abstract class BaseIconCache {
if (!replaceExisting) {
entry = mCache.get(key);
// We can't reuse the entry if the high-res icon is not present.
if (entry == null || entry.icon == null || entry.isLowRes()) {
if (entry == null || entry.bitmap.isNullOrLowRes()) {
entry = null;
}
}
if (entry == null) {
entry = new CacheEntry();
cachingLogic.loadIcon(mContext, object, entry);
entry.bitmap = cachingLogic.loadIcon(mContext, object);
}
// Icon can't be loaded from cachingLogic, which implies alternative icon was loaded
// (e.g. fallback icon, default icon). So we drop here since there's no point in caching
// an empty entry.
if (entry.icon == null) return;
if (entry.bitmap.isNullOrLowRes()) return;
entry.title = cachingLogic.getLabel(object);
entry.contentDescription = mPackageManager.getUserBadgedLabel(entry.title, user);
if (cachingLogic.addToMemCache()) mCache.put(key, entry);
ContentValues values = newContentValues(entry, entry.title.toString(),
ContentValues values = newContentValues(entry.bitmap, entry.title.toString(),
componentName.getPackageName(), cachingLogic.getKeywords(object, mLocaleList));
addIconToDB(values, componentName, info, userSerial);
}
@ -300,8 +303,8 @@ public abstract class BaseIconCache {
return mDefaultIcons.get(user);
}
public boolean isDefaultIcon(Bitmap icon, UserHandle user) {
return getDefaultIcon(user).icon == icon;
public boolean isDefaultIcon(BitmapInfo icon, UserHandle user) {
return getDefaultIcon(user).icon == icon.icon;
}
/**
@ -315,7 +318,7 @@ public abstract class BaseIconCache {
assertWorkerThread();
ComponentKey cacheKey = new ComponentKey(componentName, user);
CacheEntry entry = mCache.get(cacheKey);
if (entry == null || (entry.isLowRes() && !useLowResIcon)) {
if (entry == null || (entry.bitmap.isLowRes() && !useLowResIcon)) {
entry = new CacheEntry();
if (cachingLogic.addToMemCache()) {
mCache.put(cacheKey, entry);
@ -330,7 +333,7 @@ public abstract class BaseIconCache {
providerFetchedOnce = true;
if (object != null) {
cachingLogic.loadIcon(mContext, object, entry);
entry.bitmap = cachingLogic.loadIcon(mContext, object);
} else {
if (usePackageIcon) {
CacheEntry packageEntry = getEntryForPackageLocked(
@ -338,15 +341,15 @@ public abstract class BaseIconCache {
if (packageEntry != null) {
if (DEBUG) Log.d(TAG, "using package default icon for " +
componentName.toShortString());
packageEntry.applyTo(entry);
entry.bitmap = packageEntry.bitmap;
entry.title = packageEntry.title;
entry.contentDescription = packageEntry.contentDescription;
}
}
if (entry.icon == null) {
if (entry.bitmap == null) {
if (DEBUG) Log.d(TAG, "using default icon for " +
componentName.toShortString());
getDefaultIcon(user).applyTo(entry);
entry.bitmap = getDefaultIcon(user);
}
}
}
@ -390,10 +393,10 @@ public abstract class BaseIconCache {
}
if (icon != null) {
BaseIconFactory li = getIconFactory();
li.createIconBitmap(icon).applyTo(entry);
entry.bitmap = li.createIconBitmap(icon);
li.close();
}
if (!TextUtils.isEmpty(title) && entry.icon != null) {
if (!TextUtils.isEmpty(title) && entry.bitmap.icon != null) {
mCache.put(cacheKey, entry);
}
}
@ -413,7 +416,7 @@ public abstract class BaseIconCache {
ComponentKey cacheKey = getPackageKey(packageName, user);
CacheEntry entry = mCache.get(cacheKey);
if (entry == null || (entry.isLowRes() && !useLowResIcon)) {
if (entry == null || (entry.bitmap.isLowRes() && !useLowResIcon)) {
entry = new CacheEntry();
boolean entryUpdated = true;
@ -438,8 +441,8 @@ public abstract class BaseIconCache {
entry.title = appInfo.loadLabel(mPackageManager);
entry.contentDescription = mPackageManager.getUserBadgedLabel(entry.title, user);
entry.icon = useLowResIcon ? LOW_RES_ICON : iconInfo.icon;
entry.color = iconInfo.color;
entry.bitmap = BitmapInfo.of(
useLowResIcon ? LOW_RES_ICON : iconInfo.icon, iconInfo.color);
// Add the icon in the DB here, since these do not get written during
// package updates.
@ -472,7 +475,7 @@ public abstract class BaseIconCache {
Long.toString(getSerialNumberForUser(cacheKey.user))});
if (c.moveToNext()) {
// Set the alpha to be 255, so that we never have a wrong color
entry.color = setColorAlphaBound(c.getInt(0), 255);
entry.bitmap = BitmapInfo.of(LOW_RES_ICON, setColorAlphaBound(c.getInt(0), 255));
entry.title = c.getString(1);
if (entry.title == null) {
entry.title = "";
@ -482,13 +485,12 @@ public abstract class BaseIconCache {
entry.title, cacheKey.user);
}
if (lowRes) {
entry.icon = LOW_RES_ICON;
} else {
if (!lowRes) {
byte[] data = c.getBlob(2);
try {
entry.icon = BitmapFactory.decodeByteArray(data, 0, data.length,
mDecodeOptions);
entry.bitmap = BitmapInfo.of(
BitmapFactory.decodeByteArray(data, 0, data.length, mDecodeOptions),
entry.bitmap.color);
} catch (Exception e) { }
}
return true;

View File

@ -20,6 +20,7 @@ import android.content.Context;
import android.os.LocaleList;
import android.os.UserHandle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.icons.BitmapInfo;
@ -32,7 +33,8 @@ public interface CachingLogic<T> {
CharSequence getLabel(T object);
void loadIcon(Context context, T object, BitmapInfo target);
@NonNull
BitmapInfo loadIcon(Context context, T object);
/**
* Provides a option list of keywords to associate with this object

View File

@ -170,7 +170,7 @@ public class DynamicItemCache {
if (!details.isEmpty()) {
WorkspaceItemInfo si = new WorkspaceItemInfo(details.get(0), mContext);
try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
si.applyFrom(li.createShortcutIcon(details.get(0), true /* badged */, null));
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());
@ -209,7 +209,7 @@ public class DynamicItemCache {
InstantAppItemInfo info = new InstantAppItemInfo(intent, pkgName);
IconCache iconCache = LauncherAppState.getInstance(mContext).getIconCache();
iconCache.getTitleAndIcon(info, false);
if (info.iconBitmap == null || iconCache.isDefaultIcon(info.iconBitmap, info.user)) {
if (info.bitmap.icon == null || iconCache.isDefaultIcon(info.bitmap, info.user)) {
return null;
}
return info;

View File

@ -202,15 +202,14 @@ public class BaseModelUpdateTaskTestCase {
CacheEntry entry = mCache.get(new ComponentKey(componentName, user));
if (entry == null) {
entry = new CacheEntry();
getDefaultIcon(user).applyTo(entry);
entry.bitmap = getDefaultIcon(user);
}
return entry;
}
public void addCache(ComponentName key, String title) {
CacheEntry entry = new CacheEntry();
entry.icon = newIcon();
entry.color = Color.RED;
entry.bitmap = BitmapInfo.of(newIcon(), Color.RED);
entry.title = title;
mCache.put(new ComponentKey(key, Process.myUserHandle()), entry);
}

View File

@ -2,13 +2,13 @@ package com.android.launcher3.model;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import com.android.launcher3.AppInfo;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.icons.BitmapInfo;
import org.junit.Before;
import org.junit.Test;
@ -43,7 +43,7 @@ public class CacheDataUpdatedTaskTest extends BaseModelUpdateTaskTestCase {
public void testCacheUpdate_update_apps() throws Exception {
// Clear all icons from apps list so that its easy to check what was updated
for (AppInfo info : allAppsList.data) {
info.iconBitmap = null;
info.bitmap = BitmapInfo.LOW_RES_INFO;
}
executeTaskForTest(newTask(CacheDataUpdatedTask.OP_CACHE_UPDATE, "app1"));
@ -56,9 +56,9 @@ public class CacheDataUpdatedTaskTest extends BaseModelUpdateTaskTestCase {
assertFalse(allAppsList.data.isEmpty());
for (AppInfo info : allAppsList.data) {
if (info.componentName.getPackageName().equals("app1")) {
assertNotNull(info.iconBitmap);
assertFalse(info.bitmap.isNullOrLowRes());
} else {
assertNull(info.iconBitmap);
assertTrue(info.bitmap.isNullOrLowRes());
}
}
}
@ -85,10 +85,10 @@ public class CacheDataUpdatedTaskTest extends BaseModelUpdateTaskTestCase {
for (ItemInfo info : bgDataModel.itemsIdMap) {
if (updates.contains(info.id)) {
assertEquals(NEW_LABEL_PREFIX + info.id, info.title);
assertNotNull(((WorkspaceItemInfo) info).iconBitmap);
assertFalse(((WorkspaceItemInfo) info).bitmap.isNullOrLowRes());
} else {
assertNotSame(NEW_LABEL_PREFIX + info.id, info.title);
assertNull(((WorkspaceItemInfo) info).iconBitmap);
assertTrue(((WorkspaceItemInfo) info).bitmap.isNullOrLowRes());
}
}
}

View File

@ -289,7 +289,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
private void applyIconAndLabel(ItemInfoWithIcon info) {
FastBitmapDrawable iconDrawable = DrawableFactory.INSTANCE.get(getContext())
.newIcon(getContext(), info);
mDotParams.color = IconPalette.getMutedColor(info.iconColor, 0.54f);
mDotParams.color = IconPalette.getMutedColor(info.bitmap.color, 0.54f);
setIcon(iconDrawable);
setText(info.title);
@ -665,7 +665,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
mDisableRelayout = true;
// Optimization: Starting in N, pre-uploads the bitmap to RenderThread.
info.iconBitmap.prepareToDraw();
info.bitmap.icon.prepareToDraw();
if (info instanceof AppInfo) {
applyFromApplicationInfo((AppInfo) info);

View File

@ -98,10 +98,6 @@ public class FastBitmapDrawable extends Drawable {
this(info.icon, info.color);
}
public FastBitmapDrawable(ItemInfoWithIcon info) {
this(info.iconBitmap, info.iconColor);
}
protected FastBitmapDrawable(Bitmap b, int iconColor) {
this(b, iconColor, false);
}

View File

@ -485,7 +485,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
} else if (shortcutInfo != null) {
WorkspaceItemInfo itemInfo = new WorkspaceItemInfo(shortcutInfo, mContext);
LauncherIcons li = LauncherIcons.obtain(mContext);
itemInfo.applyFrom(li.createShortcutIcon(shortcutInfo));
itemInfo.bitmap = li.createShortcutIcon(shortcutInfo);
li.recycle();
return Pair.create(itemInfo, shortcutInfo);
} else if (providerInfo != null) {
@ -656,7 +656,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
if (iconInfo == null) {
iconInfo = app.getIconCache().getDefaultIcon(info.user);
}
info.applyFrom(iconInfo);
info.bitmap = iconInfo;
info.title = Utilities.trim(name);
info.contentDescription = app.getContext().getPackageManager()

View File

@ -16,10 +16,6 @@
package com.android.launcher3;
import static com.android.launcher3.icons.BitmapInfo.LOW_RES_ICON;
import android.graphics.Bitmap;
import com.android.launcher3.icons.BitmapInfo;
/**
@ -30,14 +26,9 @@ public abstract class ItemInfoWithIcon extends ItemInfo {
public static final String TAG = "ItemInfoDebug";
/**
* A bitmap version of the application icon.
* The bitmap for the application icon
*/
public Bitmap iconBitmap;
/**
* Dominant color in the {@link #iconBitmap}.
*/
public int iconColor;
public BitmapInfo bitmap = BitmapInfo.LOW_RES_INFO;
/**
* Indicates that the icon is disabled due to safe mode restrictions.
@ -106,8 +97,7 @@ public abstract class ItemInfoWithIcon extends ItemInfo {
protected ItemInfoWithIcon(ItemInfoWithIcon info) {
super(info);
iconBitmap = info.iconBitmap;
iconColor = info.iconColor;
bitmap = info.bitmap;
runtimeStatusFlags = info.runtimeStatusFlags;
}
@ -120,12 +110,7 @@ public abstract class ItemInfoWithIcon extends ItemInfo {
* Indicates whether we're using a low res icon
*/
public boolean usingLowResIcon() {
return iconBitmap == LOW_RES_ICON;
}
public void applyFrom(BitmapInfo info) {
iconBitmap = info.icon;
iconColor = info.color;
return bitmap.isLowRes();
}
/**

View File

@ -510,7 +510,7 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi
updateAndBindWorkspaceItem(() -> {
si.updateFromDeepShortcutInfo(info, mApp.getContext());
LauncherIcons li = LauncherIcons.obtain(mApp.getContext());
si.applyFrom(li.createShortcutIcon(info));
si.bitmap = li.createShortcutIcon(info);
li.recycle();
return si;
});
@ -546,7 +546,8 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi
if (args.length > 0 && TextUtils.equals(args[0], "--all")) {
writer.println(prefix + "All apps list: size=" + mBgAllAppsList.data.size());
for (AppInfo info : mBgAllAppsList.data) {
writer.println(prefix + " title=\"" + info.title + "\" iconBitmap=" + info.iconBitmap
writer.println(prefix + " title=\"" + info.title
+ "\" bitmapIcon=" + info.bitmap.icon
+ " componentName=" + info.componentName.getPackageName());
}
}

View File

@ -582,7 +582,7 @@ public final class Utilities {
}
ShortcutInfo si = (ShortcutInfo) obj;
LauncherIcons li = LauncherIcons.obtain(appState.getContext());
Bitmap badge = li.getShortcutInfoBadge(si, appState.getIconCache()).iconBitmap;
Bitmap badge = li.getShortcutInfoBadge(si, appState.getIconCache()).bitmap.icon;
li.recycle();
float badgeSize = iconSize * LauncherIcons.getBadgeSizeForIconSize(iconSize);
float insetFraction = (iconSize - badgeSize) / iconSize;

View File

@ -140,7 +140,7 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon {
.put(Favorites.RESTORED, status);
if (!usingLowResIcon()) {
writer.putIcon(iconBitmap, user);
writer.putIcon(bitmap, user);
}
if (iconResource != null) {
writer.put(Favorites.ICON_PACKAGE, iconResource.packageName)

View File

@ -57,8 +57,8 @@ public class DrawableFactory implements ResourceBasedOverride {
*/
public FastBitmapDrawable newIcon(Context context, ItemInfoWithIcon info) {
FastBitmapDrawable drawable = info.usingLowResIcon()
? new PlaceHolderIconDrawable(info, getShapePath(), context)
: new FastBitmapDrawable(info);
? new PlaceHolderIconDrawable(info.bitmap, getShapePath(), context)
: new FastBitmapDrawable(info.bitmap);
drawable.setIsDisabled(info.isDisabled());
return drawable;
}

View File

@ -50,8 +50,8 @@ import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.R;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.WorkspaceLayoutManager;
import com.android.launcher3.allapps.SearchUiManager;
import com.android.launcher3.config.FeatureFlags;
@ -105,7 +105,7 @@ public class LauncherPreviewRenderer implements Callable<Bitmap> {
Build.VERSION.SDK_INT);
mWorkspaceItemInfo = new WorkspaceItemInfo();
mWorkspaceItemInfo.applyFrom(iconInfo);
mWorkspaceItemInfo.bitmap = iconInfo;
mWorkspaceItemInfo.intent = new Intent();
mWorkspaceItemInfo.contentDescription = mWorkspaceItemInfo.title =
context.getString(R.string.label_application);

View File

@ -24,7 +24,6 @@ import android.graphics.Path;
import android.graphics.Rect;
import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.R;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.util.Themes;
@ -41,10 +40,6 @@ public class PlaceHolderIconDrawable extends FastBitmapDrawable {
this(info.icon, info.color, progressPath, context);
}
public PlaceHolderIconDrawable(ItemInfoWithIcon info, Path progressPath, Context context) {
this(info.iconBitmap, info.iconColor, progressPath, context);
}
protected PlaceHolderIconDrawable(Bitmap b, int iconColor, Path progressPath, Context context) {
super(b, iconColor);

View File

@ -105,7 +105,7 @@ public class PreloadIconDrawable extends FastBitmapDrawable {
* @param progressPath fixed path in the bounds [0, 0, 100, 100] representing a progress bar.
*/
public PreloadIconDrawable(ItemInfoWithIcon info, Path progressPath, Context context) {
super(info);
super(info.bitmap);
mItem = info;
mProgressPath = progressPath;
mScaledTrackPath = new Path();

View File

@ -57,10 +57,8 @@ public interface ComponentWithLabel {
}
@Override
public void loadIcon(Context context,
ComponentWithLabel object, BitmapInfo target) {
// Do not load icon.
target.icon = BitmapInfo.LOW_RES_ICON;
public BitmapInfo loadIcon(Context context, ComponentWithLabel object) {
return BitmapInfo.LOW_RES_INFO;
}
@Override

View File

@ -161,7 +161,7 @@ public class IconCache extends BaseIconCache {
CacheEntry entry = cacheLocked(application.componentName,
application.user, () -> null, mLauncherActivityInfoCachingLogic,
false, application.usingLowResIcon());
if (entry.icon != null && !isDefaultIcon(entry.icon, application.user)) {
if (entry.bitmap != null && !isDefaultIcon(entry.bitmap, application.user)) {
applyCacheEntry(entry, application);
}
}
@ -183,7 +183,7 @@ public class IconCache extends BaseIconCache {
// null info means not installed, but if we have a component from the intent then
// we should still look in the cache for restored app icons.
if (info.getTargetComponent() == null) {
info.applyFrom(getDefaultIcon(info.user));
info.bitmap = getDefaultIcon(info.user);
info.title = "";
info.contentDescription = "";
} else {
@ -226,7 +226,7 @@ public class IconCache extends BaseIconCache {
protected void applyCacheEntry(CacheEntry entry, ItemInfoWithIcon info) {
info.title = Utilities.trim(entry.title);
info.contentDescription = entry.contentDescription;
info.applyFrom((entry.icon == null) ? getDefaultIcon(info.user) : entry);
info.bitmap = (entry.bitmap == null) ? getDefaultIcon(info.user) : entry.bitmap;
}
public Drawable getFullResIcon(LauncherActivityInfo info) {

View File

@ -55,13 +55,12 @@ public class LauncherActivityCachingLogic
}
@Override
public void loadIcon(Context context, LauncherActivityInfo object,
BitmapInfo target) {
LauncherIcons li = LauncherIcons.obtain(context);
li.createBadgedIconBitmap(
IconProvider.INSTANCE.get(context)
.getIcon(object, li.mFillResIconDpi, true /* flattenDrawable */),
object.getUser(), object.getApplicationInfo().targetSdkVersion).applyTo(target);
li.recycle();
public BitmapInfo loadIcon(Context context, LauncherActivityInfo object) {
try (LauncherIcons li = LauncherIcons.obtain(context)) {
return li.createBadgedIconBitmap(
IconProvider.INSTANCE.get(context)
.getIcon(object, li.mFillResIconDpi, true /* flattenDrawable */),
object.getUser(), object.getApplicationInfo().targetSdkVersion);
}
}
}

View File

@ -137,32 +137,25 @@ public class LauncherIcons extends BaseIconFactory implements AutoCloseable {
if (fallbackIconProvider != null) {
// Fallback icons are already badged and with appropriate shadow
ItemInfoWithIcon fullIcon = fallbackIconProvider.get();
if (fullIcon != null && fullIcon.iconBitmap != null) {
BitmapInfo result = new BitmapInfo();
result.icon = fullIcon.iconBitmap;
result.color = fullIcon.iconColor;
return result;
if (fullIcon != null && fullIcon.bitmap != null) {
return fullIcon.bitmap;
}
}
unbadgedBitmap = cache.getDefaultIcon(Process.myUserHandle()).icon;
}
BitmapInfo result = new BitmapInfo();
if (!badged) {
result.color = Themes.getColorAccent(mContext);
result.icon = unbadgedBitmap;
return result;
return BitmapInfo.of(unbadgedBitmap, Themes.getColorAccent(mContext));
}
final Bitmap unbadgedfinal = unbadgedBitmap;
final ItemInfoWithIcon badge = getShortcutInfoBadge(shortcutInfo, cache);
result.color = badge.iconColor;
result.icon = BitmapRenderer.createHardwareBitmap(mIconBitmapSize, mIconBitmapSize, (c) -> {
Bitmap icon = BitmapRenderer.createHardwareBitmap(mIconBitmapSize, mIconBitmapSize, (c) -> {
getShadowGenerator().recreateIcon(unbadgedfinal, c);
badgeWithDrawable(c, new FastBitmapDrawable(badge));
badgeWithDrawable(c, new FastBitmapDrawable(badge.bitmap));
});
return result;
return BitmapInfo.of(icon, badge.bitmap.color);
}
public ItemInfoWithIcon getShortcutInfoBadge(ShortcutInfo shortcutInfo, IconCache cache) {

View File

@ -137,7 +137,7 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask {
.makeWorkspaceItem();
WorkspaceItemInfo wii = (WorkspaceItemInfo) itemInfo;
wii.title = "";
wii.applyFrom(app.getIconCache().getDefaultIcon(item.user));
wii.bitmap = app.getIconCache().getDefaultIcon(item.user);
app.getIconCache().getTitleAndIcon(wii,
((WorkspaceItemInfo) itemInfo).usingLowResIcon());
}

View File

@ -153,7 +153,7 @@ public class LoaderCursor extends CursorWrapper {
info.title = getTitle();
// the fallback icon
if (!loadIcon(info)) {
info.applyFrom(mIconCache.getDefaultIcon(info.user));
info.bitmap = mIconCache.getDefaultIcon(info.user);
}
// TODO: If there's an explicit component and we can't install that, delete it.
@ -183,7 +183,7 @@ public class LoaderCursor extends CursorWrapper {
info.iconResource.resourceName = resourceName;
BitmapInfo iconInfo = li.createIconBitmap(info.iconResource);
if (iconInfo != null) {
info.applyFrom(iconInfo);
info.bitmap = iconInfo;
return true;
}
}
@ -192,7 +192,7 @@ public class LoaderCursor extends CursorWrapper {
// Failed to load from resource, try loading from DB.
byte[] data = getBlob(iconIndex);
try {
info.applyFrom(li.createIconBitmap(BitmapFactory.decodeByteArray(data, 0, data.length)));
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);
@ -273,7 +273,7 @@ public class LoaderCursor extends CursorWrapper {
info.intent = newIntent;
mIconCache.getTitleAndIcon(info, lai, useLowResIcon);
if (mIconCache.isDefaultIcon(info.iconBitmap, user)) {
if (mIconCache.isDefaultIcon(info.bitmap, user)) {
loadIcon(info);
}

View File

@ -503,8 +503,9 @@ public class LoaderTask implements Runnable {
// use the last saved icon instead of the default.
Supplier<ItemInfoWithIcon> fallbackIconProvider = () ->
c.loadIcon(finalInfo, li) ? finalInfo : null;
info.applyFrom(li.createShortcutIcon(pinnedShortcut,
true /* badged */, fallbackIconProvider));
info.bitmap = li.createShortcutIcon(
pinnedShortcut, true /* badged */,
fallbackIconProvider);
li.recycle();
if (pmHelper.isAppSuspended(
pinnedShortcut.getPackage(), info.user)) {

View File

@ -189,7 +189,7 @@ public class PackageUpdatedTask extends BaseModelUpdateTask {
BitmapInfo iconInfo = li.createIconBitmap(si.iconResource);
li.recycle();
if (iconInfo != null) {
si.applyFrom(iconInfo);
si.bitmap = iconInfo;
infoUpdated = true;
}
}

View File

@ -93,8 +93,8 @@ public class ShortcutsChangedTask extends BaseModelUpdateTask {
// 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.applyFrom(li.createShortcutIcon(fullDetails, true,
() -> workspaceItemInfo));
workspaceItemInfo.bitmap = li.createShortcutIcon(
fullDetails, true, () -> workspaceItemInfo);
li.recycle();
updatedWorkspaceItemInfos.add(workspaceItemInfo);
}

View File

@ -92,7 +92,7 @@ public class UserLockStateChangedTask extends BaseModelUpdateTask {
// 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.applyFrom(li.createShortcutIcon(shortcut, true, () -> si));
si.bitmap = li.createShortcutIcon(shortcut, true, () -> si);
li.recycle();
} else {
si.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;

View File

@ -82,7 +82,7 @@ public class PinRequestHelper {
WorkspaceItemInfo info = new WorkspaceItemInfo(si, context);
// Apply the unbadged icon and fetch the actual icon asynchronously.
LauncherIcons li = LauncherIcons.obtain(context);
info.applyFrom(li.createShortcutIcon(si, false /* badged */));
info.bitmap = li.createShortcutIcon(si, false /* badged */);
li.recycle();
LauncherAppState.getInstance(context).getModel()
.updateAndBindWorkspaceItem(info, si);

View File

@ -507,7 +507,7 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
DotInfo dotInfo = mLauncher.getDotInfoForItem(itemInfo);
if (mNotificationItemView != null && dotInfo != null) {
mNotificationItemView.updateHeader(
dotInfo.getNotificationCount(), itemInfo.iconColor);
dotInfo.getNotificationCount(), itemInfo.bitmap.color);
}
}

View File

@ -154,7 +154,7 @@ public class PopupPopulator {
final WorkspaceItemInfo si = new WorkspaceItemInfo(shortcut, launcher);
// Use unbadged icon for the menu.
LauncherIcons li = LauncherIcons.obtain(launcher);
si.applyFrom(li.createShortcutIcon(shortcut, false /* badged */));
si.bitmap = li.createShortcutIcon(shortcut, false /* badged */);
li.recycle();
si.rank = i;

View File

@ -26,6 +26,7 @@ import android.os.UserHandle;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.GraphicsUtils;
/**
@ -37,7 +38,7 @@ public class ContentWriter {
private final Context mContext;
private CommitParams mCommitParams;
private Bitmap mIcon;
private BitmapInfo mIcon;
private UserHandle mUser;
public ContentWriter(Context context, CommitParams commitParams) {
@ -79,7 +80,7 @@ public class ContentWriter {
return this;
}
public ContentWriter putIcon(Bitmap value, UserHandle user) {
public ContentWriter putIcon(BitmapInfo value, UserHandle user) {
mIcon = value;
mUser = user;
return this;
@ -97,7 +98,7 @@ public class ContentWriter {
Preconditions.assertNonUiThread();
if (mIcon != null && !LauncherAppState.getInstance(context).getIconCache()
.isDefaultIcon(mIcon, mUser)) {
mValues.put(LauncherSettings.Favorites.ICON, GraphicsUtils.flattenBitmap(mIcon));
mValues.put(LauncherSettings.Favorites.ICON, GraphicsUtils.flattenBitmap(mIcon.icon));
mIcon = null;
}
return mValues;

View File

@ -33,12 +33,12 @@ import android.view.View.OnClickListener;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.R;
import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.util.Themes;
@ -128,7 +128,7 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
mCenterDrawable.setCallback(null);
mCenterDrawable = null;
}
if (info.iconBitmap != null) {
if (info.bitmap.icon != null) {
// The view displays three modes,
// 1) App icon in the center
// 2) Preload icon in the center
@ -142,7 +142,7 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
} else if (isReadyForClickSetup()) {
mCenterDrawable = drawableFactory.newIcon(getContext(), info);
mSettingIconDrawable = getResources().getDrawable(R.drawable.ic_setting).mutate();
updateSettingColor(info.iconColor);
updateSettingColor(info.bitmap.color);
} else {
mCenterDrawable = DrawableFactory.INSTANCE.get(getContext())
.newPendingIcon(getContext(), info);

View File

@ -18,6 +18,8 @@ package com.android.launcher3.widget;
import android.util.Log;
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.widget.WidgetsListAdapter.WidgetListRowEntryComparator;
@ -25,8 +27,6 @@ import com.android.launcher3.widget.WidgetsListAdapter.WidgetListRowEntryCompara
import java.util.ArrayList;
import java.util.Iterator;
import androidx.recyclerview.widget.RecyclerView;
/**
* Do diff on widget's tray list items and call the {@link RecyclerView.Adapter}
* methods accordingly.
@ -137,7 +137,7 @@ public class WidgetsDiffReporter {
}
private boolean isSamePackageItemInfo(PackageItemInfo curInfo, PackageItemInfo newInfo) {
return curInfo.iconBitmap.equals(newInfo.iconBitmap) &&
!mIconCache.isDefaultIcon(curInfo.iconBitmap, curInfo.user);
return curInfo.bitmap.icon.equals(newInfo.bitmap.icon)
&& !mIconCache.isDefaultIcon(curInfo.bitmap, curInfo.user);
}
}

View File

@ -142,7 +142,7 @@ public class LoaderCursorTest {
when(mMockIconCache.getDefaultIcon(eq(mLoaderCursor.user)))
.thenReturn(BitmapInfo.fromBitmap(icon));
WorkspaceItemInfo info = mLoaderCursor.loadSimpleWorkspaceItem();
assertEquals(icon, info.iconBitmap);
assertEquals(icon, info.bitmap.icon);
assertEquals("my-shortcut", info.title);
assertEquals(ITEM_TYPE_SHORTCUT, info.itemType);
}

View File

@ -23,16 +23,19 @@ import static org.mockito.Mockito.verify;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.graphics.Bitmap;
import android.view.LayoutInflater;
import androidx.recyclerview.widget.RecyclerView;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import android.view.LayoutInflater;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.WidgetPreviewLoader;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.util.MultiHashMap;
@ -46,8 +49,6 @@ import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.Map;
import androidx.recyclerview.widget.RecyclerView;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class WidgetsListAdapterTest {
@ -136,7 +137,7 @@ public class WidgetsListAdapterTest {
PackageItemInfo pInfo = new PackageItemInfo(wi.componentName.getPackageName());
pInfo.title = pInfo.packageName;
pInfo.user = wi.user;
pInfo.iconBitmap = Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8);
pInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
newMap.addToList(pInfo, wi);
if (newMap.size() == num) {
break;