blob: a4ac8129cd95dacb0e28f169d5b3cc6302060665 [file] [log] [blame]
package com.android.launcher3.icons;
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.Canvas;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* Factory for creating normalized bubble icons and app badges.
*/
public class BubbleIconFactory extends BaseIconFactory {
private final int mRingColor;
private final int mRingWidth;
private final BaseIconFactory mBadgeFactory;
/**
* Creates a bubble icon factory.
*
* @param context the context for the factory.
* @param iconSize the size of the bubble icon (i.e. the large icon for the bubble).
* @param badgeSize the size of the badge (i.e. smaller icon shown on top of the large icon).
* @param ringColor the color of the ring optionally shown around the badge.
* @param ringWidth the width of the ring optionally shown around the badge.
*/
public BubbleIconFactory(Context context, int iconSize, int badgeSize, int ringColor,
int ringWidth) {
super(context, context.getResources().getConfiguration().densityDpi, iconSize);
mRingColor = ringColor;
mRingWidth = ringWidth;
mBadgeFactory = new BaseIconFactory(context,
context.getResources().getConfiguration().densityDpi,
badgeSize);
}
/**
* Returns the drawable that the developer has provided to display in the bubble.
*/
public Drawable getBubbleDrawable(@NonNull final Context context,
@Nullable final ShortcutInfo shortcutInfo, @Nullable final Icon ic) {
if (shortcutInfo != null) {
LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
int density = context.getResources().getConfiguration().densityDpi;
return launcherApps.getShortcutIconDrawable(shortcutInfo, density);
} else {
if (ic != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
if (ic.getType() == Icon.TYPE_URI
|| ic.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP) {
context.grantUriPermission(context.getPackageName(),
ic.getUri(),
Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
return ic.loadDrawable(context);
}
return null;
}
}
/**
* Creates the bitmap for the provided drawable and returns the scale used for
* drawing the actual drawable. This is used for the larger icon shown for the bubble.
*/
public Bitmap getBubbleBitmap(@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], MODE_WITH_SHADOW);
}
/**
* Returns a {@link BitmapInfo} for the app-badge that is shown on top of each bubble. This
* will include the workprofile indicator on the badge if appropriate.
*/
public BitmapInfo getBadgeBitmap(Drawable userBadgedAppIcon, boolean isImportantConversation) {
if (userBadgedAppIcon instanceof AdaptiveIconDrawable) {
AdaptiveIconDrawable ad = (AdaptiveIconDrawable) userBadgedAppIcon;
userBadgedAppIcon = new CircularAdaptiveIcon(ad.getBackground(),
ad.getForeground());
}
if (isImportantConversation) {
userBadgedAppIcon = new CircularRingDrawable(userBadgedAppIcon);
}
Bitmap userBadgedBitmap = mBadgeFactory.createIconBitmap(
userBadgedAppIcon, 1, MODE_WITH_SHADOW);
return mBadgeFactory.createIconBitmap(userBadgedBitmap);
}
private class CircularRingDrawable extends CircularAdaptiveIcon {
final Rect mInnerBounds = new Rect();
final Drawable mDr;
CircularRingDrawable(Drawable dr) {
super(null, null);
mDr = dr;
}
@Override
public void draw(Canvas canvas) {
int save = canvas.save();
canvas.clipPath(getIconMask());
canvas.drawColor(mRingColor);
mInnerBounds.set(getBounds());
mInnerBounds.inset(mRingWidth, mRingWidth);
canvas.translate(mInnerBounds.left, mInnerBounds.top);
mDr.setBounds(0, 0, mInnerBounds.width(), mInnerBounds.height());
mDr.draw(canvas);
canvas.restoreToCount(save);
}
}
private static class CircularAdaptiveIcon extends AdaptiveIconDrawable {
final Path mPath = new Path();
CircularAdaptiveIcon(Drawable bg, Drawable fg) {
super(bg, fg);
}
@Override
public Path getIconMask() {
mPath.reset();
Rect bounds = getBounds();
mPath.addOval(bounds.left, bounds.top, bounds.right, bounds.bottom, Path.Direction.CW);
return mPath;
}
@Override
public void draw(Canvas canvas) {
int save = canvas.save();
canvas.clipPath(getIconMask());
Drawable d;
if ((d = getBackground()) != null) {
d.draw(canvas);
}
if ((d = getForeground()) != null) {
d.draw(canvas);
}
canvas.restoreToCount(save);
}
}
}