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

Commit d1dc910e authored by Dan Sandler's avatar Dan Sandler Committed by Android (Google) Code Review
Browse files

Merge "Reduce RAM requirements of grayscale icon testing" into lmp-dev

parents 6704cb98 05c362d5
Loading
Loading
Loading
Loading
+5 −5
Original line number Diff line number Diff line
@@ -1917,7 +1917,7 @@ public class Notification implements Parcelable
            mPeople = new ArrayList<String>();

            mColorUtil = context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.L ?
                    NotificationColorUtil.getInstance() : null;
                    NotificationColorUtil.getInstance(mContext) : null;
        }

        /**
@@ -2890,7 +2890,7 @@ public class Notification implements Parcelable
        }

        private void processLegacyAction(Action action, RemoteViews button) {
            if (!isLegacy() || mColorUtil.isGrayscale(mContext, action.icon)) {
            if (!isLegacy() || mColorUtil.isGrayscaleIcon(mContext, action.icon)) {
                button.setTextViewCompoundDrawablesRelativeColorFilter(R.id.action0, 0,
                        mContext.getResources().getColor(R.color.notification_action_color_filter),
                        PorterDuff.Mode.MULTIPLY);
@@ -2909,7 +2909,7 @@ public class Notification implements Parcelable
         * Apply any necessary background to smallIcons being used in the largeIcon spot.
         */
        private void processSmallIconAsLarge(int largeIconId, RemoteViews contentView) {
            if (!isLegacy() || mColorUtil.isGrayscale(mContext, largeIconId)) {
            if (!isLegacy() || mColorUtil.isGrayscaleIcon(mContext, largeIconId)) {
                applyLargeIconBackground(contentView);
            }
        }
@@ -2920,7 +2920,7 @@ public class Notification implements Parcelable
         */
        // TODO: also check bounds, transparency, that sort of thing.
        private void processLargeLegacyIcon(Bitmap largeIcon, RemoteViews contentView) {
            if (isLegacy() && mColorUtil.isGrayscale(largeIcon)) {
            if (isLegacy() && mColorUtil.isGrayscaleIcon(largeIcon)) {
                applyLargeIconBackground(contentView);
            } else {
                removeLargeIconBackground(contentView);
@@ -2956,7 +2956,7 @@ public class Notification implements Parcelable
         */
        private void processSmallRightIcon(int smallIconDrawableId,
                RemoteViews contentView) {
            if (!isLegacy() || mColorUtil.isGrayscale(mContext, smallIconDrawableId)) {
            if (!isLegacy() || mColorUtil.isGrayscaleIcon(mContext, smallIconDrawableId)) {
                contentView.setDrawableParameters(R.id.right_icon, false, -1,
                        0xFFFFFFFF,
                        PorterDuff.Mode.SRC_ATOP, -1);
+39 −3
Original line number Diff line number Diff line
@@ -17,6 +17,10 @@
package com.android.internal.util;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;

/**
 * Utility class for image analysis and processing.
@@ -31,17 +35,49 @@ public class ImageUtils {
    // Alpha amount for which values below are considered transparent.
    private static final int ALPHA_TOLERANCE = 50;

    // Size of the smaller bitmap we're actually going to scan.
    private static final int COMPACT_BITMAP_SIZE = 64; // pixels

    private int[] mTempBuffer;
    private Bitmap mTempCompactBitmap;
    private Canvas mTempCompactBitmapCanvas;
    private Paint mTempCompactBitmapPaint;
    private final Matrix mTempMatrix = new Matrix();

    /**
     * Checks whether a bitmap is grayscale. Grayscale here means "very close to a perfect
     * gray".
     *
     * Instead of scanning every pixel in the bitmap, we first resize the bitmap to no more than
     * COMPACT_BITMAP_SIZE^2 pixels using filtering. The hope is that any non-gray color elements
     * will survive the squeezing process, contaminating the result with color.
     */
    public boolean isGrayscale(Bitmap bitmap) {
        final int height = bitmap.getHeight();
        final int width = bitmap.getWidth();
        int size = height*width;
        int height = bitmap.getHeight();
        int width = bitmap.getWidth();

        // shrink to a more manageable (yet hopefully no more or less colorful) size
        if (height > COMPACT_BITMAP_SIZE || width > COMPACT_BITMAP_SIZE) {
            if (mTempCompactBitmap == null) {
                mTempCompactBitmap = Bitmap.createBitmap(
                        COMPACT_BITMAP_SIZE, COMPACT_BITMAP_SIZE, Bitmap.Config.ARGB_8888
                );
                mTempCompactBitmapCanvas = new Canvas(mTempCompactBitmap);
                mTempCompactBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
                mTempCompactBitmapPaint.setFilterBitmap(true);
            }
            mTempMatrix.reset();
            mTempMatrix.setScale(
                    (float) COMPACT_BITMAP_SIZE / width,
                    (float) COMPACT_BITMAP_SIZE / height,
                    0, 0);
            mTempCompactBitmapCanvas.drawColor(0, PorterDuff.Mode.SRC); // select all, erase
            mTempCompactBitmapCanvas.drawBitmap(bitmap, mTempMatrix, mTempCompactBitmapPaint);
            bitmap = mTempCompactBitmap;
            width = height = COMPACT_BITMAP_SIZE;
        }

        final int size = height*width;
        ensureBufferSize(size);
        bitmap.getPixels(mTempBuffer, 0, width, 0, 0, width, height);
        for (int i = 0; i < size; i++) {
+30 −17
Original line number Diff line number Diff line
@@ -50,23 +50,36 @@ public class NotificationColorUtil {
    private final WeakHashMap<Bitmap, Pair<Boolean, Integer>> mGrayscaleBitmapCache =
            new WeakHashMap<Bitmap, Pair<Boolean, Integer>>();

    public static NotificationColorUtil getInstance() {
    private final int mGrayscaleIconMaxSize; // @dimen/notification_large_icon_width (64dp)

    public static NotificationColorUtil getInstance(Context context) {
        synchronized (sLock) {
            if (sInstance == null) {
                sInstance = new NotificationColorUtil();
                sInstance = new NotificationColorUtil(context);
            }
            return sInstance;
        }
    }

    private NotificationColorUtil(Context context) {
        mGrayscaleIconMaxSize = context.getResources().getDimensionPixelSize(
                com.android.internal.R.dimen.notification_large_icon_width);
    }

    /**
     * Checks whether a bitmap is grayscale. Grayscale here means "very close to a perfect
     * gray".
     * Checks whether a Bitmap is a small grayscale icon.
     * Grayscale here means "very close to a perfect gray"; icon means "no larger than 64dp".
     *
     * @param bitmap The bitmap to test.
     * @return Whether the bitmap is grayscale.
     * @return True if the bitmap is grayscale; false if it is color or too large to examine.
     */
    public boolean isGrayscale(Bitmap bitmap) {
    public boolean isGrayscaleIcon(Bitmap bitmap) {
        // quick test: reject large bitmaps
        if (bitmap.getWidth() > mGrayscaleIconMaxSize
                || bitmap.getHeight() > mGrayscaleIconMaxSize) {
            return false;
        }

        synchronized (sLock) {
            Pair<Boolean, Integer> cached = mGrayscaleBitmapCache.get(bitmap);
            if (cached != null) {
@@ -92,22 +105,22 @@ public class NotificationColorUtil {
    }

    /**
     * Checks whether a drawable is grayscale. Grayscale here means "very close to a perfect
     * gray".
     * Checks whether a Drawable is a small grayscale icon.
     * Grayscale here means "very close to a perfect gray"; icon means "no larger than 64dp".
     *
     * @param d The drawable to test.
     * @return Whether the drawable is grayscale.
     * @return True if the bitmap is grayscale; false if it is color or too large to examine.
     */
    public boolean isGrayscale(Drawable d) {
    public boolean isGrayscaleIcon(Drawable d) {
        if (d == null) {
            return false;
        } else if (d instanceof BitmapDrawable) {
            BitmapDrawable bd = (BitmapDrawable) d;
            return bd.getBitmap() != null && isGrayscale(bd.getBitmap());
            return bd.getBitmap() != null && isGrayscaleIcon(bd.getBitmap());
        } else if (d instanceof AnimationDrawable) {
            AnimationDrawable ad = (AnimationDrawable) d;
            int count = ad.getNumberOfFrames();
            return count > 0 && isGrayscale(ad.getFrame(0));
            return count > 0 && isGrayscaleIcon(ad.getFrame(0));
        } else if (d instanceof VectorDrawable) {
            // We just assume you're doing the right thing if using vectors
            return true;
@@ -117,16 +130,16 @@ public class NotificationColorUtil {
    }

    /**
     * Checks whether a drawable with a resoure id is grayscale. Grayscale here means "very close
     * to a perfect gray".
     * Checks whether a drawable with a resoure id is a small grayscale icon.
     * Grayscale here means "very close to a perfect gray"; icon means "no larger than 64dp".
     *
     * @param context The context to load the drawable from.
     * @return Whether the drawable is grayscale.
     * @return True if the bitmap is grayscale; false if it is color or too large to examine.
     */
    public boolean isGrayscale(Context context, int drawableResId) {
    public boolean isGrayscaleIcon(Context context, int drawableResId) {
        if (drawableResId != 0) {
            try {
                return isGrayscale(context.getDrawable(drawableResId));
                return isGrayscaleIcon(context.getDrawable(drawableResId));
            } catch (Resources.NotFoundException ex) {
                Log.e(TAG, "Drawable not found: " + drawableResId);
                return false;
+4 −5
Original line number Diff line number Diff line
@@ -39,9 +39,6 @@ import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Build;
@@ -179,7 +176,7 @@ public abstract class BaseStatusBar extends SystemUI implements
    // public mode, private notifications, etc
    private boolean mLockscreenPublicMode = false;
    private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
    private NotificationColorUtil mNotificationColorUtil = NotificationColorUtil.getInstance();
    private NotificationColorUtil mNotificationColorUtil;

    private UserManager mUserManager;

@@ -437,6 +434,8 @@ public abstract class BaseStatusBar extends SystemUI implements
        mDevicePolicyManager = (DevicePolicyManager)mContext.getSystemService(
                Context.DEVICE_POLICY_SERVICE);

        mNotificationColorUtil = NotificationColorUtil.getInstance(mContext);

        mNotificationData = new NotificationData(this);

        mDreamManager = IDreamManager.Stub.asInterface(
@@ -1348,7 +1347,7 @@ public abstract class BaseStatusBar extends SystemUI implements

            Drawable iconDrawable = StatusBarIconView.getIcon(mContext, ic);
            icon.setImageDrawable(iconDrawable);
            if (mNotificationColorUtil.isGrayscale(iconDrawable)) {
            if (mNotificationColorUtil.isGrayscaleIcon(iconDrawable)) {
                icon.setBackgroundResource(
                        com.android.internal.R.drawable.notification_icon_legacy_bg);
                int padding = mContext.getResources().getDimensionPixelSize(
+2 −1
Original line number Diff line number Diff line
@@ -36,7 +36,7 @@ public class NotificationOverflowIconsView extends IconMerger {
    private TextView mMoreText;
    private int mTintColor;
    private int mIconSize;
    private NotificationColorUtil mNotificationColorUtil = new NotificationColorUtil();
    private NotificationColorUtil mNotificationColorUtil;

    public NotificationOverflowIconsView(Context context, AttributeSet attrs) {
        super(context, attrs);
@@ -45,6 +45,7 @@ public class NotificationOverflowIconsView extends IconMerger {
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mNotificationColorUtil = NotificationColorUtil.getInstance(getContext());
        mTintColor = getResources().getColor(R.color.keyguard_overflow_content_color);
        mIconSize = getResources().getDimensionPixelSize(
                com.android.internal.R.dimen.status_bar_icon_size);