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

This commit is contained in:
Dan Sandler
2014-09-03 22:15:37 +00:00
committed by Android (Google) Code Review
5 changed files with 80 additions and 31 deletions

View File

@@ -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);

View File

@@ -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++) {

View File

@@ -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;

View File

@@ -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(

View File

@@ -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);