Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3808a69a authored by Sunny Goyal's avatar Sunny Goyal
Browse files

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
parent 0d9752c6
Loading
Loading
Loading
Loading
+26 −13
Original line number Diff line number Diff line
@@ -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 final Bitmap icon;
    public final int color;

    public Bitmap icon;
    public int color;
    public BitmapInfo(Bitmap icon, int color) {
        this.icon = icon;
        this.color = color;
    }

    public void applyTo(BitmapInfo info) {
        info.icon = icon;
        info.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);
    }
}
+25 −23
Original line number Diff line number Diff line
@@ -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;
+3 −1
Original line number Diff line number Diff line
@@ -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
+2 −2
Original line number Diff line number Diff line
@@ -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;
+2 −3
Original line number Diff line number Diff line
@@ -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);
        }
Loading