Loading iconloaderlib/build.gradle +4 −15 Original line number Diff line number Diff line apply plugin: 'com.android.library' android { compileSdkVersion COMPILE_SDK buildToolsVersion BUILD_TOOLS_VERSION defaultConfig { minSdkVersion 26 targetSdkVersion 28 plugins { id 'sysuigradleproject.android-library-conventions' } android { sourceSets { main { java.srcDirs = ['src', 'src_full_lib'] Loading @@ -24,13 +18,8 @@ android { tasks.withType(JavaCompile) { options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } } dependencies { implementation "androidx.core:core:${ANDROID_X_VERSION}" implementation "androidx.core:core" } iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java +116 −56 Original line number Diff line number Diff line package com.android.launcher3.icons; import static android.graphics.Paint.ANTI_ALIAS_FLAG; import static android.graphics.Paint.DITHER_FLAG; import static android.graphics.Paint.FILTER_BITMAP_FLAG; import static android.graphics.drawable.AdaptiveIconDrawable.getExtraInsetFraction; Loading Loading @@ -36,6 +37,8 @@ import androidx.annotation.Nullable; import com.android.launcher3.icons.BitmapInfo.Extender; import com.android.launcher3.util.FlagOp; import java.util.Objects; /** * This class will be moved to androidx library. There shouldn't be any dependency outside * this package. Loading @@ -44,14 +47,30 @@ public class BaseIconFactory implements AutoCloseable { private static final int DEFAULT_WRAPPER_BACKGROUND = Color.WHITE; protected static final int BITMAP_GENERATION_MODE_DEFAULT = 0; protected static final int BITMAP_GENERATION_MODE_ALPHA = 1; protected static final int BITMAP_GENERATION_MODE_WITH_SHADOW = 2; private static final float ICON_BADGE_SCALE = 0.444f; @NonNull private final Rect mOldBounds = new Rect(); @NonNull private final SparseBooleanArray mIsUserBadged = new SparseBooleanArray(); @NonNull protected final Context mContext; @NonNull private final Canvas mCanvas; @NonNull private final PackageManager mPm; @NonNull private final ColorExtractor mColorExtractor; private boolean mDisableColorExtractor; protected final int mFillResIconDpi; Loading @@ -59,8 +78,12 @@ public class BaseIconFactory implements AutoCloseable { protected boolean mMonoIconEnabled; @Nullable private IconNormalizer mNormalizer; @Nullable private ShadowGenerator mShadowGenerator; private final boolean mShapeDetection; // Shadow bitmap used as background for theme icons Loading @@ -69,8 +92,6 @@ public class BaseIconFactory implements AutoCloseable { private Drawable mWrapperIcon; private int mWrapperBackgroundColor = DEFAULT_WRAPPER_BACKGROUND; private final Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); private static final float PLACEHOLDER_TEXT_SIZE = 20f; private static int PLACEHOLDER_BACKGROUND_COLOR = Color.rgb(245, 245, 245); protected BaseIconFactory(Context context, int fillResIconDpi, int iconBitmapSize, Loading @@ -85,10 +106,6 @@ public class BaseIconFactory implements AutoCloseable { mCanvas = new Canvas(); mCanvas.setDrawFilter(new PaintFlagsDrawFilter(DITHER_FLAG, FILTER_BITMAP_FLAG)); mTextPaint.setTextAlign(Paint.Align.CENTER); mTextPaint.setColor(PLACEHOLDER_BACKGROUND_COLOR); mTextPaint.setTextSize(context.getResources().getDisplayMetrics().density * PLACEHOLDER_TEXT_SIZE); clear(); } Loading @@ -101,6 +118,7 @@ public class BaseIconFactory implements AutoCloseable { mDisableColorExtractor = false; } @NonNull public ShadowGenerator getShadowGenerator() { if (mShadowGenerator == null) { mShadowGenerator = new ShadowGenerator(mIconBitmapSize); Loading @@ -108,6 +126,7 @@ public class BaseIconFactory implements AutoCloseable { return mShadowGenerator; } @NonNull public IconNormalizer getNormalizer() { if (mNormalizer == null) { mNormalizer = new IconNormalizer(mContext, mIconBitmapSize, mShapeDetection); Loading Loading @@ -138,16 +157,11 @@ public class BaseIconFactory implements AutoCloseable { * @return */ public BitmapInfo createIconBitmap(String placeholder, int color) { Bitmap placeholderBitmap = Bitmap.createBitmap(mIconBitmapSize, mIconBitmapSize, Bitmap.Config.ARGB_8888); mTextPaint.setColor(color); Canvas canvas = new Canvas(placeholderBitmap); canvas.drawText(placeholder, mIconBitmapSize / 2, mIconBitmapSize * 5 / 8, mTextPaint); AdaptiveIconDrawable drawable = new AdaptiveIconDrawable( new ColorDrawable(PLACEHOLDER_BACKGROUND_COLOR), new BitmapDrawable(mContext.getResources(), placeholderBitmap)); new CenterTextDrawable(placeholder, color)); Bitmap icon = createIconBitmap(drawable, IconNormalizer.ICON_VISIBLE_AREA_FACTOR); return BitmapInfo.of(icon, extractColor(icon)); return BitmapInfo.of(icon, color); } public BitmapInfo createIconBitmap(Bitmap icon) { Loading @@ -161,6 +175,7 @@ public class BaseIconFactory implements AutoCloseable { /** * Creates an icon from the bitmap cropped to the current device icon shape */ @NonNull public BitmapInfo createShapedIconBitmap(Bitmap icon, IconOptions options) { Drawable d = new FixedSizeBitmapDrawable(icon); float inset = getExtraInsetFraction(); Loading @@ -170,6 +185,7 @@ public class BaseIconFactory implements AutoCloseable { return createBadgedIconBitmap(d, options); } @NonNull public BitmapInfo createBadgedIconBitmap(@NonNull Drawable icon) { return createBadgedIconBitmap(icon, null); } Loading @@ -182,17 +198,13 @@ public class BaseIconFactory implements AutoCloseable { * @return a bitmap suitable for disaplaying as an icon at various system UIs. */ @TargetApi(Build.VERSION_CODES.TIRAMISU) @NonNull public BitmapInfo createBadgedIconBitmap(@NonNull Drawable icon, @Nullable IconOptions options) { boolean shrinkNonAdaptiveIcons = options == null || options.mShrinkNonAdaptiveIcons; float[] scale = new float[1]; icon = normalizeAndWrapToAdaptiveIcon(icon, shrinkNonAdaptiveIcons, null, scale); Bitmap bitmap = createIconBitmap(icon, scale[0]); if (icon instanceof AdaptiveIconDrawable) { mCanvas.setBitmap(bitmap); getShadowGenerator().recreateIcon(Bitmap.createBitmap(bitmap), mCanvas); mCanvas.setBitmap(null); } Bitmap bitmap = createIconBitmap(icon, scale[0], BITMAP_GENERATION_MODE_WITH_SHADOW); int color = extractColor(bitmap); BitmapInfo info = BitmapInfo.of(bitmap, color); Loading @@ -206,14 +218,14 @@ public class BaseIconFactory implements AutoCloseable { // Convert mono drawable to bitmap Drawable paddedMono = new ClippedMonoDrawable(mono); info.setMonoIcon( createIconBitmap(paddedMono, scale[0], mIconBitmapSize, Config.ALPHA_8), this); createIconBitmap(paddedMono, scale[0], BITMAP_GENERATION_MODE_ALPHA), this); } } info = info.withFlags(getBitmapFlagOp(options)); return info; } @NonNull public FlagOp getBitmapFlagOp(@Nullable IconOptions options) { FlagOp op = FlagOp.NO_OP; if (options != null) { Loading @@ -240,6 +252,7 @@ public class BaseIconFactory implements AutoCloseable { } /** package private */ @NonNull Bitmap getWhiteShadowLayer() { if (mWhiteShadowLayer == null) { mWhiteShadowLayer = createScaledBitmapWithShadow( Loading @@ -248,17 +261,16 @@ public class BaseIconFactory implements AutoCloseable { return mWhiteShadowLayer; } /** package private */ public Bitmap createScaledBitmapWithShadow(Drawable d) { @NonNull public Bitmap createScaledBitmapWithShadow(@NonNull final Drawable d) { float scale = getNormalizer().getScale(d, null, null, null); Bitmap bitmap = createIconBitmap(d, scale); mCanvas.setBitmap(bitmap); getShadowGenerator().recreateIcon(Bitmap.createBitmap(bitmap), mCanvas); mCanvas.setBitmap(null); return bitmap; return BitmapRenderer.createHardwareBitmap(bitmap.getWidth(), bitmap.getHeight(), canvas -> getShadowGenerator().recreateIcon(bitmap, canvas)); } public Bitmap createScaledBitmapWithoutShadow(Drawable icon) { @NonNull public Bitmap createScaledBitmapWithoutShadow(@Nullable Drawable icon) { RectF iconBounds = new RectF(); float[] scale = new float[1]; icon = normalizeAndWrapToAdaptiveIcon(icon, true, iconBounds, scale); Loading @@ -269,7 +281,7 @@ public class BaseIconFactory implements AutoCloseable { /** * Sets the background color used for wrapped adaptive icon */ public void setWrapperBackgroundColor(int color) { public void setWrapperBackgroundColor(final int color) { mWrapperBackgroundColor = (Color.alpha(color) < 255) ? DEFAULT_WRAPPER_BACKGROUND : color; } Loading @@ -280,8 +292,10 @@ public class BaseIconFactory implements AutoCloseable { mDisableColorExtractor = true; } private Drawable normalizeAndWrapToAdaptiveIcon(@NonNull Drawable icon, boolean shrinkNonAdaptiveIcons, RectF outIconBounds, float[] outScale) { @Nullable protected Drawable normalizeAndWrapToAdaptiveIcon(@Nullable Drawable icon, final boolean shrinkNonAdaptiveIcons, @Nullable final RectF outIconBounds, @NonNull final float[] outScale) { if (icon == null) { return null; } Loading Loading @@ -312,21 +326,26 @@ public class BaseIconFactory implements AutoCloseable { return icon; } private Bitmap createIconBitmap(Drawable icon, float scale) { return createIconBitmap(icon, scale, mIconBitmapSize); @NonNull protected Bitmap createIconBitmap(@Nullable final Drawable icon, final float scale) { return createIconBitmap(icon, scale, BITMAP_GENERATION_MODE_DEFAULT); } /** * @param icon drawable that should be flattened to a bitmap * @param scale the scale to apply before drawing {@param icon} on the canvas */ public Bitmap createIconBitmap(@NonNull Drawable icon, float scale, int size) { return createIconBitmap(icon, scale, size, Bitmap.Config.ARGB_8888); } @NonNull protected Bitmap createIconBitmap(@Nullable final Drawable icon, final float scale, final int bitmapGenerationMode) { final int size = mIconBitmapSize; private Bitmap createIconBitmap(@NonNull Drawable icon, float scale, int size, Bitmap.Config config) { Bitmap bitmap = Bitmap.createBitmap(size, size, config); final Bitmap bitmap; switch (bitmapGenerationMode) { case BITMAP_GENERATION_MODE_ALPHA: bitmap = Bitmap.createBitmap(size, size, Config.ALPHA_8); break; case BITMAP_GENERATION_MODE_WITH_SHADOW: default: bitmap = Bitmap.createBitmap(size, size, Config.ARGB_8888); break; } if (icon == null) { return bitmap; } Loading @@ -340,6 +359,10 @@ public class BaseIconFactory implements AutoCloseable { icon.setBounds(0, 0, size - offset - offset, size - offset - offset); int count = mCanvas.save(); mCanvas.translate(offset, offset); if (bitmapGenerationMode == BITMAP_GENERATION_MODE_WITH_SHADOW) { getShadowGenerator().addPathShadow( ((AdaptiveIconDrawable) icon).getIconMask(), mCanvas); } if (icon instanceof BitmapInfo.Extender) { ((Extender) icon).drawForPersistence(mCanvas); Loading @@ -351,7 +374,7 @@ public class BaseIconFactory implements AutoCloseable { if (icon instanceof BitmapDrawable) { BitmapDrawable bitmapDrawable = (BitmapDrawable) icon; Bitmap b = bitmapDrawable.getBitmap(); if (bitmap != null && b.getDensity() == Bitmap.DENSITY_NONE) { if (b != null && b.getDensity() == Bitmap.DENSITY_NONE) { bitmapDrawable.setTargetDensity(mContext.getResources().getDisplayMetrics()); } } Loading Loading @@ -388,36 +411,42 @@ public class BaseIconFactory implements AutoCloseable { clear(); } @NonNull public BitmapInfo makeDefaultIcon() { return createBadgedIconBitmap(getFullResDefaultActivityIcon(mFillResIconDpi)); } public static Drawable getFullResDefaultActivityIcon(int iconDpi) { return Resources.getSystem().getDrawableForDensity( android.R.drawable.sym_def_app_icon, iconDpi); @NonNull public static Drawable getFullResDefaultActivityIcon(final int iconDpi) { return Objects.requireNonNull(Resources.getSystem().getDrawableForDensity( android.R.drawable.sym_def_app_icon, iconDpi)); } private int extractColor(Bitmap bitmap) { private int extractColor(@NonNull final Bitmap bitmap) { return mDisableColorExtractor ? 0 : mColorExtractor.findDominantColorByHue(bitmap); } /** * Returns the correct badge size given an icon size */ public static int getBadgeSizeForIconSize(int iconSize) { public static int getBadgeSizeForIconSize(final int iconSize) { return (int) (ICON_BADGE_SCALE * iconSize); } public static class IconOptions { boolean mShrinkNonAdaptiveIcons = true; boolean mIsInstantApp; @Nullable UserHandle mUserHandle; /** * Set to false if non-adaptive icons should not be treated */ public IconOptions setShrinkNonAdaptiveIcons(boolean shrink) { @NonNull public IconOptions setShrinkNonAdaptiveIcons(final boolean shrink) { mShrinkNonAdaptiveIcons = shrink; return this; } Loading @@ -425,7 +454,8 @@ public class BaseIconFactory implements AutoCloseable { /** * User for this icon, in case of badging */ public IconOptions setUser(UserHandle user) { @NonNull public IconOptions setUser(@Nullable final UserHandle user) { mUserHandle = user; return this; } Loading @@ -433,7 +463,8 @@ public class BaseIconFactory implements AutoCloseable { /** * If this icon represents an instant app */ public IconOptions setInstantApp(boolean instantApp) { @NonNull public IconOptions setInstantApp(final boolean instantApp) { mIsInstantApp = instantApp; return this; } Loading @@ -446,7 +477,7 @@ public class BaseIconFactory implements AutoCloseable { */ private static class FixedSizeBitmapDrawable extends BitmapDrawable { public FixedSizeBitmapDrawable(Bitmap bitmap) { public FixedSizeBitmapDrawable(@Nullable final Bitmap bitmap) { super(null, bitmap); } Loading Loading @@ -475,9 +506,10 @@ public class BaseIconFactory implements AutoCloseable { private static class ClippedMonoDrawable extends InsetDrawable { @NonNull private final AdaptiveIconDrawable mCrop; public ClippedMonoDrawable(Drawable base) { public ClippedMonoDrawable(@Nullable final Drawable base) { super(base, -getExtraInsetFraction()); mCrop = new AdaptiveIconDrawable(new ColorDrawable(Color.BLACK), null); } Loading @@ -491,4 +523,32 @@ public class BaseIconFactory implements AutoCloseable { canvas.restoreToCount(saveCount); } } private static class CenterTextDrawable extends ColorDrawable { @NonNull private final Rect mTextBounds = new Rect(); @NonNull private final Paint mTextPaint = new Paint(ANTI_ALIAS_FLAG | FILTER_BITMAP_FLAG); @NonNull private final String mText; CenterTextDrawable(@NonNull final String text, final int color) { mText = text; mTextPaint.setColor(color); } @Override public void draw(Canvas canvas) { Rect bounds = getBounds(); mTextPaint.setTextSize(bounds.height() / 3f); mTextPaint.getTextBounds(mText, 0, mText.length(), mTextBounds); canvas.drawText(mText, bounds.exactCenterX() - mTextBounds.exactCenterX(), bounds.exactCenterY() - mTextBounds.exactCenterY(), mTextPaint); } } } iconloaderlib/src/com/android/launcher3/icons/ClockDrawableWrapper.java +10 −3 Original line number Diff line number Diff line Loading @@ -387,7 +387,8 @@ public class ClockDrawableWrapper extends AdaptiveIconDrawable implements Bitmap mBgPaint.setColorFilter(cs.mBgFilter); mThemedFgColor = cs.mThemedFgColor; mFullDrawable = (AdaptiveIconDrawable) mAnimInfo.baseDrawableState.newDrawable(); mFullDrawable = (AdaptiveIconDrawable) mAnimInfo.baseDrawableState.newDrawable().mutate(); mFG = (LayerDrawable) mFullDrawable.getForeground(); // Time needs to be applied here since drawInternal is NOT guaranteed to be called Loading @@ -396,6 +397,13 @@ public class ClockDrawableWrapper extends AdaptiveIconDrawable implements Bitmap mCanvasScale = 1 - 2 * mBoundsOffset; } @Override public void setAlpha(int alpha) { super.setAlpha(alpha); mBgPaint.setAlpha(alpha); mFG.setAlpha(alpha); } @Override protected void onBoundsChange(Rect bounds) { super.onBoundsChange(bounds); Loading Loading @@ -434,8 +442,7 @@ public class ClockDrawableWrapper extends AdaptiveIconDrawable implements Bitmap protected void updateFilter() { super.updateFilter(); int alpha = mIsDisabled ? (int) (mDisabledAlpha * FULLY_OPAQUE) : FULLY_OPAQUE; mBgPaint.setAlpha(alpha); mFG.setAlpha(alpha); setAlpha(alpha); mBgPaint.setColorFilter(mIsDisabled ? getDisabledColorFilter() : mBgFilter); mFG.setColorFilter(mIsDisabled ? getDisabledColorFilter() : null); } Loading iconloaderlib/src/com/android/launcher3/icons/ColorExtractor.java +13 −2 Original line number Diff line number Diff line Loading @@ -18,6 +18,9 @@ package com.android.launcher3.icons; import android.graphics.Bitmap; import android.graphics.Color; import android.util.SparseArray; import androidx.annotation.NonNull; import java.util.Arrays; /** Loading @@ -26,16 +29,24 @@ import java.util.Arrays; public class ColorExtractor { private final int NUM_SAMPLES = 20; @NonNull private final float[] mTmpHsv = new float[3]; @NonNull private final float[] mTmpHueScoreHistogram = new float[360]; @NonNull private final int[] mTmpPixels = new int[NUM_SAMPLES]; @NonNull private final SparseArray<Float> mTmpRgbScores = new SparseArray<>(); /** * This picks a dominant color, looking for high-saturation, high-value, repeated hues. * @param bitmap The bitmap to scan */ public int findDominantColorByHue(Bitmap bitmap) { public int findDominantColorByHue(@NonNull final Bitmap bitmap) { return findDominantColorByHue(bitmap, NUM_SAMPLES); } Loading @@ -43,7 +54,7 @@ public class ColorExtractor { * This picks a dominant color, looking for high-saturation, high-value, repeated hues. * @param bitmap The bitmap to scan */ public int findDominantColorByHue(Bitmap bitmap, int samples) { public int findDominantColorByHue(@NonNull final Bitmap bitmap, final int samples) { final int height = bitmap.getHeight(); final int width = bitmap.getWidth(); int sampleStride = (int) Math.sqrt((height * width) / samples); Loading iconloaderlib/src/com/android/launcher3/icons/ShadowGenerator.java +24 −8 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.graphics.BlurMaskFilter.Blur; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.RectF; Loading Loading @@ -58,22 +59,17 @@ public class ShadowGenerator { } public synchronized void recreateIcon(Bitmap icon, Canvas out) { recreateIcon(icon, mDefaultBlurMaskFilter, AMBIENT_SHADOW_ALPHA, KEY_SHADOW_ALPHA, out); } public synchronized void recreateIcon(Bitmap icon, BlurMaskFilter blurMaskFilter, int ambientAlpha, int keyAlpha, Canvas out) { if (ENABLE_SHADOWS) { int[] offset = new int[2]; mBlurPaint.setMaskFilter(blurMaskFilter); mBlurPaint.setMaskFilter(mDefaultBlurMaskFilter); Bitmap shadow = icon.extractAlpha(mBlurPaint, offset); // Draw ambient shadow mDrawPaint.setAlpha(ambientAlpha); mDrawPaint.setAlpha(AMBIENT_SHADOW_ALPHA); out.drawBitmap(shadow, offset[0], offset[1], mDrawPaint); // Draw key shadow mDrawPaint.setAlpha(keyAlpha); mDrawPaint.setAlpha(KEY_SHADOW_ALPHA); out.drawBitmap(shadow, offset[0], offset[1] + KEY_SHADOW_DISTANCE * mIconSize, mDrawPaint); } Loading @@ -83,6 +79,26 @@ public class ShadowGenerator { out.drawBitmap(icon, 0, 0, mDrawPaint); } /** package private **/ void addPathShadow(Path path, Canvas out) { if (ENABLE_SHADOWS) { mDrawPaint.setMaskFilter(mDefaultBlurMaskFilter); // Draw ambient shadow mDrawPaint.setAlpha(AMBIENT_SHADOW_ALPHA); out.drawPath(path, mDrawPaint); // Draw key shadow int save = out.save(); mDrawPaint.setAlpha(KEY_SHADOW_ALPHA); out.translate(0, KEY_SHADOW_DISTANCE * mIconSize); out.drawPath(path, mDrawPaint); out.restoreToCount(save); mDrawPaint.setMaskFilter(null); } } /** * Returns the minimum amount by which an icon with {@param bounds} should be scaled * so that the shadows do not get clipped. Loading Loading
iconloaderlib/build.gradle +4 −15 Original line number Diff line number Diff line apply plugin: 'com.android.library' android { compileSdkVersion COMPILE_SDK buildToolsVersion BUILD_TOOLS_VERSION defaultConfig { minSdkVersion 26 targetSdkVersion 28 plugins { id 'sysuigradleproject.android-library-conventions' } android { sourceSets { main { java.srcDirs = ['src', 'src_full_lib'] Loading @@ -24,13 +18,8 @@ android { tasks.withType(JavaCompile) { options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } } dependencies { implementation "androidx.core:core:${ANDROID_X_VERSION}" implementation "androidx.core:core" }
iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java +116 −56 Original line number Diff line number Diff line package com.android.launcher3.icons; import static android.graphics.Paint.ANTI_ALIAS_FLAG; import static android.graphics.Paint.DITHER_FLAG; import static android.graphics.Paint.FILTER_BITMAP_FLAG; import static android.graphics.drawable.AdaptiveIconDrawable.getExtraInsetFraction; Loading Loading @@ -36,6 +37,8 @@ import androidx.annotation.Nullable; import com.android.launcher3.icons.BitmapInfo.Extender; import com.android.launcher3.util.FlagOp; import java.util.Objects; /** * This class will be moved to androidx library. There shouldn't be any dependency outside * this package. Loading @@ -44,14 +47,30 @@ public class BaseIconFactory implements AutoCloseable { private static final int DEFAULT_WRAPPER_BACKGROUND = Color.WHITE; protected static final int BITMAP_GENERATION_MODE_DEFAULT = 0; protected static final int BITMAP_GENERATION_MODE_ALPHA = 1; protected static final int BITMAP_GENERATION_MODE_WITH_SHADOW = 2; private static final float ICON_BADGE_SCALE = 0.444f; @NonNull private final Rect mOldBounds = new Rect(); @NonNull private final SparseBooleanArray mIsUserBadged = new SparseBooleanArray(); @NonNull protected final Context mContext; @NonNull private final Canvas mCanvas; @NonNull private final PackageManager mPm; @NonNull private final ColorExtractor mColorExtractor; private boolean mDisableColorExtractor; protected final int mFillResIconDpi; Loading @@ -59,8 +78,12 @@ public class BaseIconFactory implements AutoCloseable { protected boolean mMonoIconEnabled; @Nullable private IconNormalizer mNormalizer; @Nullable private ShadowGenerator mShadowGenerator; private final boolean mShapeDetection; // Shadow bitmap used as background for theme icons Loading @@ -69,8 +92,6 @@ public class BaseIconFactory implements AutoCloseable { private Drawable mWrapperIcon; private int mWrapperBackgroundColor = DEFAULT_WRAPPER_BACKGROUND; private final Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); private static final float PLACEHOLDER_TEXT_SIZE = 20f; private static int PLACEHOLDER_BACKGROUND_COLOR = Color.rgb(245, 245, 245); protected BaseIconFactory(Context context, int fillResIconDpi, int iconBitmapSize, Loading @@ -85,10 +106,6 @@ public class BaseIconFactory implements AutoCloseable { mCanvas = new Canvas(); mCanvas.setDrawFilter(new PaintFlagsDrawFilter(DITHER_FLAG, FILTER_BITMAP_FLAG)); mTextPaint.setTextAlign(Paint.Align.CENTER); mTextPaint.setColor(PLACEHOLDER_BACKGROUND_COLOR); mTextPaint.setTextSize(context.getResources().getDisplayMetrics().density * PLACEHOLDER_TEXT_SIZE); clear(); } Loading @@ -101,6 +118,7 @@ public class BaseIconFactory implements AutoCloseable { mDisableColorExtractor = false; } @NonNull public ShadowGenerator getShadowGenerator() { if (mShadowGenerator == null) { mShadowGenerator = new ShadowGenerator(mIconBitmapSize); Loading @@ -108,6 +126,7 @@ public class BaseIconFactory implements AutoCloseable { return mShadowGenerator; } @NonNull public IconNormalizer getNormalizer() { if (mNormalizer == null) { mNormalizer = new IconNormalizer(mContext, mIconBitmapSize, mShapeDetection); Loading Loading @@ -138,16 +157,11 @@ public class BaseIconFactory implements AutoCloseable { * @return */ public BitmapInfo createIconBitmap(String placeholder, int color) { Bitmap placeholderBitmap = Bitmap.createBitmap(mIconBitmapSize, mIconBitmapSize, Bitmap.Config.ARGB_8888); mTextPaint.setColor(color); Canvas canvas = new Canvas(placeholderBitmap); canvas.drawText(placeholder, mIconBitmapSize / 2, mIconBitmapSize * 5 / 8, mTextPaint); AdaptiveIconDrawable drawable = new AdaptiveIconDrawable( new ColorDrawable(PLACEHOLDER_BACKGROUND_COLOR), new BitmapDrawable(mContext.getResources(), placeholderBitmap)); new CenterTextDrawable(placeholder, color)); Bitmap icon = createIconBitmap(drawable, IconNormalizer.ICON_VISIBLE_AREA_FACTOR); return BitmapInfo.of(icon, extractColor(icon)); return BitmapInfo.of(icon, color); } public BitmapInfo createIconBitmap(Bitmap icon) { Loading @@ -161,6 +175,7 @@ public class BaseIconFactory implements AutoCloseable { /** * Creates an icon from the bitmap cropped to the current device icon shape */ @NonNull public BitmapInfo createShapedIconBitmap(Bitmap icon, IconOptions options) { Drawable d = new FixedSizeBitmapDrawable(icon); float inset = getExtraInsetFraction(); Loading @@ -170,6 +185,7 @@ public class BaseIconFactory implements AutoCloseable { return createBadgedIconBitmap(d, options); } @NonNull public BitmapInfo createBadgedIconBitmap(@NonNull Drawable icon) { return createBadgedIconBitmap(icon, null); } Loading @@ -182,17 +198,13 @@ public class BaseIconFactory implements AutoCloseable { * @return a bitmap suitable for disaplaying as an icon at various system UIs. */ @TargetApi(Build.VERSION_CODES.TIRAMISU) @NonNull public BitmapInfo createBadgedIconBitmap(@NonNull Drawable icon, @Nullable IconOptions options) { boolean shrinkNonAdaptiveIcons = options == null || options.mShrinkNonAdaptiveIcons; float[] scale = new float[1]; icon = normalizeAndWrapToAdaptiveIcon(icon, shrinkNonAdaptiveIcons, null, scale); Bitmap bitmap = createIconBitmap(icon, scale[0]); if (icon instanceof AdaptiveIconDrawable) { mCanvas.setBitmap(bitmap); getShadowGenerator().recreateIcon(Bitmap.createBitmap(bitmap), mCanvas); mCanvas.setBitmap(null); } Bitmap bitmap = createIconBitmap(icon, scale[0], BITMAP_GENERATION_MODE_WITH_SHADOW); int color = extractColor(bitmap); BitmapInfo info = BitmapInfo.of(bitmap, color); Loading @@ -206,14 +218,14 @@ public class BaseIconFactory implements AutoCloseable { // Convert mono drawable to bitmap Drawable paddedMono = new ClippedMonoDrawable(mono); info.setMonoIcon( createIconBitmap(paddedMono, scale[0], mIconBitmapSize, Config.ALPHA_8), this); createIconBitmap(paddedMono, scale[0], BITMAP_GENERATION_MODE_ALPHA), this); } } info = info.withFlags(getBitmapFlagOp(options)); return info; } @NonNull public FlagOp getBitmapFlagOp(@Nullable IconOptions options) { FlagOp op = FlagOp.NO_OP; if (options != null) { Loading @@ -240,6 +252,7 @@ public class BaseIconFactory implements AutoCloseable { } /** package private */ @NonNull Bitmap getWhiteShadowLayer() { if (mWhiteShadowLayer == null) { mWhiteShadowLayer = createScaledBitmapWithShadow( Loading @@ -248,17 +261,16 @@ public class BaseIconFactory implements AutoCloseable { return mWhiteShadowLayer; } /** package private */ public Bitmap createScaledBitmapWithShadow(Drawable d) { @NonNull public Bitmap createScaledBitmapWithShadow(@NonNull final Drawable d) { float scale = getNormalizer().getScale(d, null, null, null); Bitmap bitmap = createIconBitmap(d, scale); mCanvas.setBitmap(bitmap); getShadowGenerator().recreateIcon(Bitmap.createBitmap(bitmap), mCanvas); mCanvas.setBitmap(null); return bitmap; return BitmapRenderer.createHardwareBitmap(bitmap.getWidth(), bitmap.getHeight(), canvas -> getShadowGenerator().recreateIcon(bitmap, canvas)); } public Bitmap createScaledBitmapWithoutShadow(Drawable icon) { @NonNull public Bitmap createScaledBitmapWithoutShadow(@Nullable Drawable icon) { RectF iconBounds = new RectF(); float[] scale = new float[1]; icon = normalizeAndWrapToAdaptiveIcon(icon, true, iconBounds, scale); Loading @@ -269,7 +281,7 @@ public class BaseIconFactory implements AutoCloseable { /** * Sets the background color used for wrapped adaptive icon */ public void setWrapperBackgroundColor(int color) { public void setWrapperBackgroundColor(final int color) { mWrapperBackgroundColor = (Color.alpha(color) < 255) ? DEFAULT_WRAPPER_BACKGROUND : color; } Loading @@ -280,8 +292,10 @@ public class BaseIconFactory implements AutoCloseable { mDisableColorExtractor = true; } private Drawable normalizeAndWrapToAdaptiveIcon(@NonNull Drawable icon, boolean shrinkNonAdaptiveIcons, RectF outIconBounds, float[] outScale) { @Nullable protected Drawable normalizeAndWrapToAdaptiveIcon(@Nullable Drawable icon, final boolean shrinkNonAdaptiveIcons, @Nullable final RectF outIconBounds, @NonNull final float[] outScale) { if (icon == null) { return null; } Loading Loading @@ -312,21 +326,26 @@ public class BaseIconFactory implements AutoCloseable { return icon; } private Bitmap createIconBitmap(Drawable icon, float scale) { return createIconBitmap(icon, scale, mIconBitmapSize); @NonNull protected Bitmap createIconBitmap(@Nullable final Drawable icon, final float scale) { return createIconBitmap(icon, scale, BITMAP_GENERATION_MODE_DEFAULT); } /** * @param icon drawable that should be flattened to a bitmap * @param scale the scale to apply before drawing {@param icon} on the canvas */ public Bitmap createIconBitmap(@NonNull Drawable icon, float scale, int size) { return createIconBitmap(icon, scale, size, Bitmap.Config.ARGB_8888); } @NonNull protected Bitmap createIconBitmap(@Nullable final Drawable icon, final float scale, final int bitmapGenerationMode) { final int size = mIconBitmapSize; private Bitmap createIconBitmap(@NonNull Drawable icon, float scale, int size, Bitmap.Config config) { Bitmap bitmap = Bitmap.createBitmap(size, size, config); final Bitmap bitmap; switch (bitmapGenerationMode) { case BITMAP_GENERATION_MODE_ALPHA: bitmap = Bitmap.createBitmap(size, size, Config.ALPHA_8); break; case BITMAP_GENERATION_MODE_WITH_SHADOW: default: bitmap = Bitmap.createBitmap(size, size, Config.ARGB_8888); break; } if (icon == null) { return bitmap; } Loading @@ -340,6 +359,10 @@ public class BaseIconFactory implements AutoCloseable { icon.setBounds(0, 0, size - offset - offset, size - offset - offset); int count = mCanvas.save(); mCanvas.translate(offset, offset); if (bitmapGenerationMode == BITMAP_GENERATION_MODE_WITH_SHADOW) { getShadowGenerator().addPathShadow( ((AdaptiveIconDrawable) icon).getIconMask(), mCanvas); } if (icon instanceof BitmapInfo.Extender) { ((Extender) icon).drawForPersistence(mCanvas); Loading @@ -351,7 +374,7 @@ public class BaseIconFactory implements AutoCloseable { if (icon instanceof BitmapDrawable) { BitmapDrawable bitmapDrawable = (BitmapDrawable) icon; Bitmap b = bitmapDrawable.getBitmap(); if (bitmap != null && b.getDensity() == Bitmap.DENSITY_NONE) { if (b != null && b.getDensity() == Bitmap.DENSITY_NONE) { bitmapDrawable.setTargetDensity(mContext.getResources().getDisplayMetrics()); } } Loading Loading @@ -388,36 +411,42 @@ public class BaseIconFactory implements AutoCloseable { clear(); } @NonNull public BitmapInfo makeDefaultIcon() { return createBadgedIconBitmap(getFullResDefaultActivityIcon(mFillResIconDpi)); } public static Drawable getFullResDefaultActivityIcon(int iconDpi) { return Resources.getSystem().getDrawableForDensity( android.R.drawable.sym_def_app_icon, iconDpi); @NonNull public static Drawable getFullResDefaultActivityIcon(final int iconDpi) { return Objects.requireNonNull(Resources.getSystem().getDrawableForDensity( android.R.drawable.sym_def_app_icon, iconDpi)); } private int extractColor(Bitmap bitmap) { private int extractColor(@NonNull final Bitmap bitmap) { return mDisableColorExtractor ? 0 : mColorExtractor.findDominantColorByHue(bitmap); } /** * Returns the correct badge size given an icon size */ public static int getBadgeSizeForIconSize(int iconSize) { public static int getBadgeSizeForIconSize(final int iconSize) { return (int) (ICON_BADGE_SCALE * iconSize); } public static class IconOptions { boolean mShrinkNonAdaptiveIcons = true; boolean mIsInstantApp; @Nullable UserHandle mUserHandle; /** * Set to false if non-adaptive icons should not be treated */ public IconOptions setShrinkNonAdaptiveIcons(boolean shrink) { @NonNull public IconOptions setShrinkNonAdaptiveIcons(final boolean shrink) { mShrinkNonAdaptiveIcons = shrink; return this; } Loading @@ -425,7 +454,8 @@ public class BaseIconFactory implements AutoCloseable { /** * User for this icon, in case of badging */ public IconOptions setUser(UserHandle user) { @NonNull public IconOptions setUser(@Nullable final UserHandle user) { mUserHandle = user; return this; } Loading @@ -433,7 +463,8 @@ public class BaseIconFactory implements AutoCloseable { /** * If this icon represents an instant app */ public IconOptions setInstantApp(boolean instantApp) { @NonNull public IconOptions setInstantApp(final boolean instantApp) { mIsInstantApp = instantApp; return this; } Loading @@ -446,7 +477,7 @@ public class BaseIconFactory implements AutoCloseable { */ private static class FixedSizeBitmapDrawable extends BitmapDrawable { public FixedSizeBitmapDrawable(Bitmap bitmap) { public FixedSizeBitmapDrawable(@Nullable final Bitmap bitmap) { super(null, bitmap); } Loading Loading @@ -475,9 +506,10 @@ public class BaseIconFactory implements AutoCloseable { private static class ClippedMonoDrawable extends InsetDrawable { @NonNull private final AdaptiveIconDrawable mCrop; public ClippedMonoDrawable(Drawable base) { public ClippedMonoDrawable(@Nullable final Drawable base) { super(base, -getExtraInsetFraction()); mCrop = new AdaptiveIconDrawable(new ColorDrawable(Color.BLACK), null); } Loading @@ -491,4 +523,32 @@ public class BaseIconFactory implements AutoCloseable { canvas.restoreToCount(saveCount); } } private static class CenterTextDrawable extends ColorDrawable { @NonNull private final Rect mTextBounds = new Rect(); @NonNull private final Paint mTextPaint = new Paint(ANTI_ALIAS_FLAG | FILTER_BITMAP_FLAG); @NonNull private final String mText; CenterTextDrawable(@NonNull final String text, final int color) { mText = text; mTextPaint.setColor(color); } @Override public void draw(Canvas canvas) { Rect bounds = getBounds(); mTextPaint.setTextSize(bounds.height() / 3f); mTextPaint.getTextBounds(mText, 0, mText.length(), mTextBounds); canvas.drawText(mText, bounds.exactCenterX() - mTextBounds.exactCenterX(), bounds.exactCenterY() - mTextBounds.exactCenterY(), mTextPaint); } } }
iconloaderlib/src/com/android/launcher3/icons/ClockDrawableWrapper.java +10 −3 Original line number Diff line number Diff line Loading @@ -387,7 +387,8 @@ public class ClockDrawableWrapper extends AdaptiveIconDrawable implements Bitmap mBgPaint.setColorFilter(cs.mBgFilter); mThemedFgColor = cs.mThemedFgColor; mFullDrawable = (AdaptiveIconDrawable) mAnimInfo.baseDrawableState.newDrawable(); mFullDrawable = (AdaptiveIconDrawable) mAnimInfo.baseDrawableState.newDrawable().mutate(); mFG = (LayerDrawable) mFullDrawable.getForeground(); // Time needs to be applied here since drawInternal is NOT guaranteed to be called Loading @@ -396,6 +397,13 @@ public class ClockDrawableWrapper extends AdaptiveIconDrawable implements Bitmap mCanvasScale = 1 - 2 * mBoundsOffset; } @Override public void setAlpha(int alpha) { super.setAlpha(alpha); mBgPaint.setAlpha(alpha); mFG.setAlpha(alpha); } @Override protected void onBoundsChange(Rect bounds) { super.onBoundsChange(bounds); Loading Loading @@ -434,8 +442,7 @@ public class ClockDrawableWrapper extends AdaptiveIconDrawable implements Bitmap protected void updateFilter() { super.updateFilter(); int alpha = mIsDisabled ? (int) (mDisabledAlpha * FULLY_OPAQUE) : FULLY_OPAQUE; mBgPaint.setAlpha(alpha); mFG.setAlpha(alpha); setAlpha(alpha); mBgPaint.setColorFilter(mIsDisabled ? getDisabledColorFilter() : mBgFilter); mFG.setColorFilter(mIsDisabled ? getDisabledColorFilter() : null); } Loading
iconloaderlib/src/com/android/launcher3/icons/ColorExtractor.java +13 −2 Original line number Diff line number Diff line Loading @@ -18,6 +18,9 @@ package com.android.launcher3.icons; import android.graphics.Bitmap; import android.graphics.Color; import android.util.SparseArray; import androidx.annotation.NonNull; import java.util.Arrays; /** Loading @@ -26,16 +29,24 @@ import java.util.Arrays; public class ColorExtractor { private final int NUM_SAMPLES = 20; @NonNull private final float[] mTmpHsv = new float[3]; @NonNull private final float[] mTmpHueScoreHistogram = new float[360]; @NonNull private final int[] mTmpPixels = new int[NUM_SAMPLES]; @NonNull private final SparseArray<Float> mTmpRgbScores = new SparseArray<>(); /** * This picks a dominant color, looking for high-saturation, high-value, repeated hues. * @param bitmap The bitmap to scan */ public int findDominantColorByHue(Bitmap bitmap) { public int findDominantColorByHue(@NonNull final Bitmap bitmap) { return findDominantColorByHue(bitmap, NUM_SAMPLES); } Loading @@ -43,7 +54,7 @@ public class ColorExtractor { * This picks a dominant color, looking for high-saturation, high-value, repeated hues. * @param bitmap The bitmap to scan */ public int findDominantColorByHue(Bitmap bitmap, int samples) { public int findDominantColorByHue(@NonNull final Bitmap bitmap, final int samples) { final int height = bitmap.getHeight(); final int width = bitmap.getWidth(); int sampleStride = (int) Math.sqrt((height * width) / samples); Loading
iconloaderlib/src/com/android/launcher3/icons/ShadowGenerator.java +24 −8 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.graphics.BlurMaskFilter.Blur; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.RectF; Loading Loading @@ -58,22 +59,17 @@ public class ShadowGenerator { } public synchronized void recreateIcon(Bitmap icon, Canvas out) { recreateIcon(icon, mDefaultBlurMaskFilter, AMBIENT_SHADOW_ALPHA, KEY_SHADOW_ALPHA, out); } public synchronized void recreateIcon(Bitmap icon, BlurMaskFilter blurMaskFilter, int ambientAlpha, int keyAlpha, Canvas out) { if (ENABLE_SHADOWS) { int[] offset = new int[2]; mBlurPaint.setMaskFilter(blurMaskFilter); mBlurPaint.setMaskFilter(mDefaultBlurMaskFilter); Bitmap shadow = icon.extractAlpha(mBlurPaint, offset); // Draw ambient shadow mDrawPaint.setAlpha(ambientAlpha); mDrawPaint.setAlpha(AMBIENT_SHADOW_ALPHA); out.drawBitmap(shadow, offset[0], offset[1], mDrawPaint); // Draw key shadow mDrawPaint.setAlpha(keyAlpha); mDrawPaint.setAlpha(KEY_SHADOW_ALPHA); out.drawBitmap(shadow, offset[0], offset[1] + KEY_SHADOW_DISTANCE * mIconSize, mDrawPaint); } Loading @@ -83,6 +79,26 @@ public class ShadowGenerator { out.drawBitmap(icon, 0, 0, mDrawPaint); } /** package private **/ void addPathShadow(Path path, Canvas out) { if (ENABLE_SHADOWS) { mDrawPaint.setMaskFilter(mDefaultBlurMaskFilter); // Draw ambient shadow mDrawPaint.setAlpha(AMBIENT_SHADOW_ALPHA); out.drawPath(path, mDrawPaint); // Draw key shadow int save = out.save(); mDrawPaint.setAlpha(KEY_SHADOW_ALPHA); out.translate(0, KEY_SHADOW_DISTANCE * mIconSize); out.drawPath(path, mDrawPaint); out.restoreToCount(save); mDrawPaint.setMaskFilter(null); } } /** * Returns the minimum amount by which an icon with {@param bounds} should be scaled * so that the shadows do not get clipped. Loading