Optimizing some icon generation code in bubble
> Generating only one bitmap when creating appIcon > Extracting the scale during icon generation for main icon instead of running icon normalizer again > Avoiding unnecessary color extraction on main icon Bug: 238937089 Test: Verified on device Change-Id: Ia01801f15e5982593852f05c2512d0f1f737f5f7
This commit is contained in:
@@ -19,14 +19,14 @@ package com.android.wm.shell.bubbles;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.AdaptiveIconDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import com.android.launcher3.icons.BaseIconFactory;
|
||||
import com.android.launcher3.icons.BitmapInfo;
|
||||
import com.android.launcher3.icons.ShadowGenerator;
|
||||
import com.android.wm.shell.R;
|
||||
|
||||
/**
|
||||
@@ -44,78 +44,77 @@ public class BubbleBadgeIconFactory extends BaseIconFactory {
|
||||
* will include the workprofile indicator on the badge if appropriate.
|
||||
*/
|
||||
BitmapInfo getBadgeBitmap(Drawable userBadgedAppIcon, boolean isImportantConversation) {
|
||||
ShadowGenerator shadowGenerator = new ShadowGenerator(mIconBitmapSize);
|
||||
Bitmap userBadgedBitmap = createIconBitmap(userBadgedAppIcon, 1f, mIconBitmapSize);
|
||||
|
||||
if (userBadgedAppIcon instanceof AdaptiveIconDrawable) {
|
||||
userBadgedBitmap = Bitmap.createScaledBitmap(
|
||||
getCircleBitmap((AdaptiveIconDrawable) userBadgedAppIcon, /* size */
|
||||
userBadgedAppIcon.getIntrinsicWidth()),
|
||||
mIconBitmapSize, mIconBitmapSize, /* filter */ true);
|
||||
AdaptiveIconDrawable ad = (AdaptiveIconDrawable) userBadgedAppIcon;
|
||||
userBadgedAppIcon = new CircularAdaptiveIcon(ad.getBackground(), ad.getForeground());
|
||||
}
|
||||
if (isImportantConversation) {
|
||||
userBadgedAppIcon = new CircularRingDrawable(userBadgedAppIcon);
|
||||
}
|
||||
Bitmap userBadgedBitmap = createIconBitmap(
|
||||
userBadgedAppIcon, 1, BITMAP_GENERATION_MODE_WITH_SHADOW);
|
||||
return createIconBitmap(userBadgedBitmap);
|
||||
}
|
||||
|
||||
private class CircularRingDrawable extends CircularAdaptiveIcon {
|
||||
|
||||
final int mImportantConversationColor;
|
||||
final Rect mTempBounds = new Rect();
|
||||
|
||||
final Drawable mDr;
|
||||
|
||||
CircularRingDrawable(Drawable dr) {
|
||||
super(null, null);
|
||||
mDr = dr;
|
||||
mImportantConversationColor = mContext.getResources().getColor(
|
||||
R.color.important_conversation, null);
|
||||
}
|
||||
|
||||
if (isImportantConversation) {
|
||||
final float ringStrokeWidth = mContext.getResources().getDimensionPixelSize(
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
int save = canvas.save();
|
||||
canvas.clipPath(getIconMask());
|
||||
canvas.drawColor(mImportantConversationColor);
|
||||
int ringStrokeWidth = mContext.getResources().getDimensionPixelSize(
|
||||
com.android.internal.R.dimen.importance_ring_stroke_width);
|
||||
final int importantConversationColor = mContext.getResources().getColor(
|
||||
R.color.important_conversation, null);
|
||||
Bitmap badgeAndRing = Bitmap.createBitmap(userBadgedBitmap.getWidth(),
|
||||
userBadgedBitmap.getHeight(), userBadgedBitmap.getConfig());
|
||||
Canvas c = new Canvas(badgeAndRing);
|
||||
|
||||
Paint ringPaint = new Paint();
|
||||
ringPaint.setStyle(Paint.Style.FILL);
|
||||
ringPaint.setColor(importantConversationColor);
|
||||
ringPaint.setAntiAlias(true);
|
||||
c.drawCircle(c.getWidth() / 2, c.getHeight() / 2, c.getWidth() / 2, ringPaint);
|
||||
|
||||
final int bitmapTop = (int) ringStrokeWidth;
|
||||
final int bitmapLeft = (int) ringStrokeWidth;
|
||||
final int bitmapWidth = c.getWidth() - 2 * (int) ringStrokeWidth;
|
||||
final int bitmapHeight = c.getHeight() - 2 * (int) ringStrokeWidth;
|
||||
|
||||
Bitmap scaledBitmap = Bitmap.createScaledBitmap(userBadgedBitmap, bitmapWidth,
|
||||
bitmapHeight, /* filter */ true);
|
||||
c.drawBitmap(scaledBitmap, bitmapTop, bitmapLeft, /* paint */null);
|
||||
|
||||
shadowGenerator.recreateIcon(Bitmap.createBitmap(badgeAndRing), c);
|
||||
return createIconBitmap(badgeAndRing);
|
||||
} else {
|
||||
Canvas c = new Canvas();
|
||||
c.setBitmap(userBadgedBitmap);
|
||||
shadowGenerator.recreateIcon(Bitmap.createBitmap(userBadgedBitmap), c);
|
||||
return createIconBitmap(userBadgedBitmap);
|
||||
mTempBounds.set(getBounds());
|
||||
mTempBounds.inset(ringStrokeWidth, ringStrokeWidth);
|
||||
mDr.setBounds(mTempBounds);
|
||||
mDr.draw(canvas);
|
||||
canvas.restoreToCount(save);
|
||||
}
|
||||
}
|
||||
|
||||
private Bitmap getCircleBitmap(AdaptiveIconDrawable icon, int size) {
|
||||
Drawable foreground = icon.getForeground();
|
||||
Drawable background = icon.getBackground();
|
||||
Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas();
|
||||
canvas.setBitmap(bitmap);
|
||||
private static class CircularAdaptiveIcon extends AdaptiveIconDrawable {
|
||||
|
||||
// Clip canvas to circle.
|
||||
Path circlePath = new Path();
|
||||
circlePath.addCircle(/* x */ size / 2f,
|
||||
/* y */ size / 2f,
|
||||
/* radius */ size / 2f,
|
||||
Path.Direction.CW);
|
||||
canvas.clipPath(circlePath);
|
||||
final Path mPath = new Path();
|
||||
|
||||
// Draw background.
|
||||
background.setBounds(0, 0, size, size);
|
||||
background.draw(canvas);
|
||||
CircularAdaptiveIcon(Drawable bg, Drawable fg) {
|
||||
super(bg, fg);
|
||||
}
|
||||
|
||||
// Draw foreground. The foreground and background drawables are derived from adaptive icons
|
||||
// Some icon shapes fill more space than others, so adaptive icons are normalized to about
|
||||
// the same size. This size is smaller than the original bounds, so we estimate
|
||||
// the difference in this offset.
|
||||
int offset = size / 5;
|
||||
foreground.setBounds(-offset, -offset, size + offset, size + offset);
|
||||
foreground.draw(canvas);
|
||||
@Override
|
||||
public Path getIconMask() {
|
||||
mPath.reset();
|
||||
Rect bounds = getBounds();
|
||||
mPath.addOval(bounds.left, bounds.top, bounds.right, bounds.bottom, Path.Direction.CW);
|
||||
return mPath;
|
||||
}
|
||||
|
||||
canvas.setBitmap(null);
|
||||
return bitmap;
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
int save = canvas.save();
|
||||
canvas.clipPath(getIconMask());
|
||||
|
||||
canvas.drawColor(Color.BLACK);
|
||||
Drawable d;
|
||||
if ((d = getBackground()) != null) {
|
||||
d.draw(canvas);
|
||||
}
|
||||
if ((d = getForeground()) != null) {
|
||||
d.draw(canvas);
|
||||
}
|
||||
canvas.restoreToCount(save);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.LauncherApps;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.Icon;
|
||||
|
||||
@@ -65,4 +66,19 @@ public class BubbleIconFactory extends BaseIconFactory {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the bitmap for the provided drawable and returns the scale used for
|
||||
* drawing the actual drawable.
|
||||
*/
|
||||
public Bitmap createIconBitmap(@NonNull Drawable icon, float[] outScale) {
|
||||
if (outScale == null) {
|
||||
outScale = new float[1];
|
||||
}
|
||||
icon = normalizeAndWrapToAdaptiveIcon(icon,
|
||||
true /* shrinkNonAdaptiveIcons */,
|
||||
null /* outscale */,
|
||||
outScale);
|
||||
return createIconBitmap(icon, outScale[0], BITMAP_GENERATION_MODE_WITH_SHADOW);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,15 +195,18 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
|
||||
b.isImportantConversation());
|
||||
info.badgeBitmap = badgeBitmapInfo.icon;
|
||||
// Raw badge bitmap never includes the important conversation ring
|
||||
info.mRawBadgeBitmap = badgeIconFactory.getBadgeBitmap(badgedIcon, false).icon;
|
||||
info.bubbleBitmap = iconFactory.createBadgedIconBitmap(bubbleDrawable).icon;
|
||||
info.mRawBadgeBitmap = b.isImportantConversation()
|
||||
? badgeIconFactory.getBadgeBitmap(badgedIcon, false).icon
|
||||
: badgeBitmapInfo.icon;
|
||||
|
||||
float[] bubbleBitmapScale = new float[1];
|
||||
info.bubbleBitmap = iconFactory.createIconBitmap(bubbleDrawable, bubbleBitmapScale);
|
||||
|
||||
// Dot color & placement
|
||||
Path iconPath = PathParser.createPathFromPathData(
|
||||
c.getResources().getString(com.android.internal.R.string.config_icon_mask));
|
||||
Matrix matrix = new Matrix();
|
||||
float scale = iconFactory.getNormalizer().getScale(bubbleDrawable,
|
||||
null /* outBounds */, null /* path */, null /* outMaskShape */);
|
||||
float scale = bubbleBitmapScale[0];
|
||||
float radius = DEFAULT_PATH_SIZE / 2f;
|
||||
matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
|
||||
radius /* pivot y */);
|
||||
|
||||
Reference in New Issue
Block a user