diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-06-15 21:47:39 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-06-15 21:47:39 +0000 |
commit | 7e748d889172e563097a26e3b9aa3c8369b3b1ca (patch) | |
tree | 5b4c37764e65ff784b2c0a7f4dbef3f46635eaef | |
parent | b9ec1978d63d4bf217c4064df830c01f73b989ac (diff) | |
parent | 22bd6262f60ee07dd4540f42d0f7dad3dcdd4e0b (diff) | |
download | systemui-aml_tz3_314012010.tar.gz |
Snap for 8730993 from 22bd6262f60ee07dd4540f42d0f7dad3dcdd4e0b to mainline-tzdata3-releaseaml_tz3_314012070aml_tz3_314012050aml_tz3_314012010aml_tz3_313110000aml_tz3_312511020aml_tz3_312511010aml_tz3_312410020aml_tz3_312410010android12-mainline-tzdata3-releaseaml_tz3_314012010
Change-Id: I0f2f2f3bf48818774e76c1d630939a48c2890fd0
16 files changed, 753 insertions, 1064 deletions
diff --git a/iconloaderlib/build.gradle b/iconloaderlib/build.gradle index 10ec889..8410275 100644 --- a/iconloaderlib/build.gradle +++ b/iconloaderlib/build.gradle @@ -7,6 +7,8 @@ android { defaultConfig { minSdkVersion 26 targetSdkVersion 28 + versionCode 1 + versionName "1.0" } sourceSets { diff --git a/iconloaderlib/res/drawable/ic_work_app_badge.xml b/iconloaderlib/res/drawable/ic_work_app_badge.xml deleted file mode 100644 index 1599489..0000000 --- a/iconloaderlib/res/drawable/ic_work_app_badge.xml +++ /dev/null @@ -1,39 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2021 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="@dimen/profile_badge_size" - android:height="@dimen/profile_badge_size" - android:viewportWidth="24" - android:viewportHeight="24"> - - <path - android:fillColor="#11000000" - android:pathData="M.5,12.25 - A11.5,11.5 0 1,1 23.5,12.25 - A11.5,11.5 0 1,1 .5,12.25" /> - - <path - android:fillColor="@android:color/white" - android:pathData="M1,12 - A11,11 0 1,1 23,12 - A11,11 0 1,1 1,12" /> - - <group android:scaleX=".6" android:scaleY=".6" android:pivotX="12" android:pivotY="12"> - <path - android:fillColor="#1A73E8" - android:pathData="M20,6h-4L16,4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2L4,6c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM14,6h-4L10,4h4v2z" /> - </group> -</vector> diff --git a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java index c0be55d..9ce9975 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java +++ b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java @@ -2,19 +2,14 @@ package com.android.launcher3.icons; import static android.graphics.Paint.DITHER_FLAG; import static android.graphics.Paint.FILTER_BITMAP_FLAG; -import static android.graphics.drawable.AdaptiveIconDrawable.getExtraInsetFraction; -import static com.android.launcher3.icons.BitmapInfo.FLAG_INSTANT; -import static com.android.launcher3.icons.BitmapInfo.FLAG_WORK; import static com.android.launcher3.icons.ShadowGenerator.BLUR_FACTOR; -import android.annotation.TargetApi; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.Bitmap; -import android.graphics.Bitmap.Config; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; @@ -27,14 +22,12 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.InsetDrawable; import android.os.Build; +import android.os.Process; import android.os.UserHandle; -import android.util.SparseBooleanArray; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import com.android.launcher3.icons.BitmapInfo.Extender; -import com.android.launcher3.util.FlagOp; /** * This class will be moved to androidx library. There shouldn't be any dependency outside @@ -42,36 +35,35 @@ import com.android.launcher3.util.FlagOp; */ public class BaseIconFactory implements AutoCloseable { + private static final String TAG = "BaseIconFactory"; private static final int DEFAULT_WRAPPER_BACKGROUND = Color.WHITE; + static final boolean ATLEAST_OREO = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; + static final boolean ATLEAST_P = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P; private static final float ICON_BADGE_SCALE = 0.444f; private final Rect mOldBounds = new Rect(); - private final SparseBooleanArray mIsUserBadged = new SparseBooleanArray(); protected final Context mContext; private final Canvas mCanvas; private final PackageManager mPm; private final ColorExtractor mColorExtractor; private boolean mDisableColorExtractor; + private boolean mBadgeOnLeft = false; protected final int mFillResIconDpi; protected final int mIconBitmapSize; - protected boolean mMonoIconEnabled; - private IconNormalizer mNormalizer; private ShadowGenerator mShadowGenerator; private final boolean mShapeDetection; - // Shadow bitmap used as background for theme icons - private Bitmap mWhiteShadowLayer; - private Drawable mWrapperIcon; private int mWrapperBackgroundColor = DEFAULT_WRAPPER_BACKGROUND; + private Bitmap mUserBadgeBitmap; 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); + private static int PLACEHOLDER_BACKGROUND_COLOR = Color.rgb(240, 240, 240); protected BaseIconFactory(Context context, int fillResIconDpi, int iconBitmapSize, boolean shapeDetection) { @@ -99,6 +91,7 @@ public class BaseIconFactory implements AutoCloseable { protected void clear() { mWrapperBackgroundColor = DEFAULT_WRAPPER_BACKGROUND; mDisableColorExtractor = false; + mBadgeOnLeft = false; } public ShadowGenerator getShadowGenerator() { @@ -122,7 +115,10 @@ public class BaseIconFactory implements AutoCloseable { if (resources != null) { final int id = resources.getIdentifier(iconRes.resourceName, null, null); // do not stamp old legacy shortcuts as the app may have already forgotten about it - return createBadgedIconBitmap(resources.getDrawableForDensity(id, mFillResIconDpi)); + return createBadgedIconBitmap( + resources.getDrawableForDensity(id, mFillResIconDpi), + Process.myUserHandle() /* only available on primary user */, + false /* do not apply legacy treatment */); } } catch (Exception e) { // Icon not found. @@ -138,6 +134,8 @@ public class BaseIconFactory implements AutoCloseable { * @return */ public BitmapInfo createIconBitmap(String placeholder, int color) { + if (!ATLEAST_OREO) return null; + Bitmap placeholderBitmap = Bitmap.createBitmap(mIconBitmapSize, mIconBitmapSize, Bitmap.Config.ARGB_8888); mTextPaint.setColor(color); @@ -146,7 +144,7 @@ public class BaseIconFactory implements AutoCloseable { AdaptiveIconDrawable drawable = new AdaptiveIconDrawable( new ColorDrawable(PLACEHOLDER_BACKGROUND_COLOR), new BitmapDrawable(mContext.getResources(), placeholderBitmap)); - Bitmap icon = createIconBitmap(drawable, IconNormalizer.ICON_VISIBLE_AREA_FACTOR); + Bitmap icon = createIconBitmap(drawable, 1f); return BitmapInfo.of(icon, extractColor(icon)); } @@ -161,17 +159,43 @@ public class BaseIconFactory implements AutoCloseable { /** * Creates an icon from the bitmap cropped to the current device icon shape */ - public BitmapInfo createShapedIconBitmap(Bitmap icon, IconOptions options) { + public BitmapInfo createShapedIconBitmap(Bitmap icon, UserHandle user) { Drawable d = new FixedSizeBitmapDrawable(icon); - float inset = getExtraInsetFraction(); - inset = inset / (1 + 2 * inset); - d = new AdaptiveIconDrawable(new ColorDrawable(Color.BLACK), - new InsetDrawable(d, inset, inset, inset, inset)); - return createBadgedIconBitmap(d, options); + if (ATLEAST_OREO) { + float inset = AdaptiveIconDrawable.getExtraInsetFraction(); + inset = inset / (1 + 2 * inset); + d = new AdaptiveIconDrawable(new ColorDrawable(Color.BLACK), + new InsetDrawable(d, inset, inset, inset, inset)); + } + return createBadgedIconBitmap(d, user, true); + } + + public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user, + boolean shrinkNonAdaptiveIcons) { + return createBadgedIconBitmap(icon, user, shrinkNonAdaptiveIcons, false, null); + } + + public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user, + int iconAppTargetSdk) { + return createBadgedIconBitmap(icon, user, iconAppTargetSdk, false); + } + + public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user, + int iconAppTargetSdk, boolean isInstantApp) { + return createBadgedIconBitmap(icon, user, iconAppTargetSdk, isInstantApp, null); } - public BitmapInfo createBadgedIconBitmap(@NonNull Drawable icon) { - return createBadgedIconBitmap(icon, null); + public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user, + int iconAppTargetSdk, boolean isInstantApp, float[] scale) { + boolean shrinkNonAdaptiveIcons = ATLEAST_P || + (ATLEAST_OREO && iconAppTargetSdk >= Build.VERSION_CODES.O); + return createBadgedIconBitmap(icon, user, shrinkNonAdaptiveIcons, isInstantApp, scale); + } + + public Bitmap createScaledBitmapWithoutShadow(Drawable icon, int iconAppTargetSdk) { + boolean shrinkNonAdaptiveIcons = ATLEAST_P || + (ATLEAST_OREO && iconAppTargetSdk >= Build.VERSION_CODES.O); + return createScaledBitmapWithoutShadow(icon, shrinkNonAdaptiveIcons); } /** @@ -179,94 +203,76 @@ public class BaseIconFactory implements AutoCloseable { * The bitmap is visually normalized with other icons and has enough spacing to add shadow. * * @param icon source of the icon + * @param user info can be used for a badge + * @param shrinkNonAdaptiveIcons {@code true} if non adaptive icons should be treated + * @param isInstantApp info can be used for a badge + * @param scale returns the scale result from normalization * @return a bitmap suitable for disaplaying as an icon at various system UIs. */ - @TargetApi(Build.VERSION_CODES.TIRAMISU) - public BitmapInfo createBadgedIconBitmap(@NonNull Drawable icon, - @Nullable IconOptions options) { - boolean shrinkNonAdaptiveIcons = options == null || options.mShrinkNonAdaptiveIcons; - float[] scale = new float[1]; + public BitmapInfo createBadgedIconBitmap(@NonNull Drawable icon, UserHandle user, + boolean shrinkNonAdaptiveIcons, boolean isInstantApp, float[] scale) { + if (scale == null) { + scale = new float[1]; + } icon = normalizeAndWrapToAdaptiveIcon(icon, shrinkNonAdaptiveIcons, null, scale); Bitmap bitmap = createIconBitmap(icon, scale[0]); - if (icon instanceof AdaptiveIconDrawable) { + if (ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) { mCanvas.setBitmap(bitmap); getShadowGenerator().recreateIcon(Bitmap.createBitmap(bitmap), mCanvas); mCanvas.setBitmap(null); } - int color = extractColor(bitmap); - BitmapInfo info = BitmapInfo.of(bitmap, color); - - if (icon instanceof BitmapInfo.Extender) { - info = ((BitmapInfo.Extender) icon).getExtendedInfo(bitmap, color, this, scale[0]); - } else if (mMonoIconEnabled && IconProvider.ATLEAST_T - && icon instanceof AdaptiveIconDrawable) { - Drawable mono = ((AdaptiveIconDrawable) icon).getMonochrome(); - if (mono != null) { - // Convert mono drawable to bitmap - Drawable paddedMono = new ClippedMonoDrawable(mono); - info.setMonoIcon( - createIconBitmap(paddedMono, scale[0], mIconBitmapSize, Config.ALPHA_8), - this); - } + if (isInstantApp) { + badgeWithDrawable(bitmap, mContext.getDrawable(R.drawable.ic_instant_app_badge)); } - info = info.withFlags(getBitmapFlagOp(options)); - return info; - } - - public FlagOp getBitmapFlagOp(@Nullable IconOptions options) { - FlagOp op = FlagOp.NO_OP; - if (options != null) { - if (options.mIsInstantApp) { - op = op.addFlag(FLAG_INSTANT); - } - - if (options.mUserHandle != null) { - int key = options.mUserHandle.hashCode(); - boolean isBadged; - int index; - if ((index = mIsUserBadged.indexOfKey(key)) >= 0) { - isBadged = mIsUserBadged.valueAt(index); - } else { - // Check packageManager if the provided user needs a badge - NoopDrawable d = new NoopDrawable(); - isBadged = (d != mPm.getUserBadgedIcon(d, options.mUserHandle)); - mIsUserBadged.put(key, isBadged); - } - op = op.setFlag(FLAG_WORK, isBadged); + if (user != null) { + BitmapDrawable drawable = new FixedSizeBitmapDrawable(bitmap); + Drawable badged = mPm.getUserBadgedIcon(drawable, user); + if (badged instanceof BitmapDrawable) { + bitmap = ((BitmapDrawable) badged).getBitmap(); + } else { + bitmap = createIconBitmap(badged, 1f); } } - return op; + int color = extractColor(bitmap); + return icon instanceof BitmapInfo.Extender + ? ((BitmapInfo.Extender) icon).getExtendedInfo(bitmap, color, this, scale[0], user) + : BitmapInfo.of(bitmap, color); } - /** package private */ - Bitmap getWhiteShadowLayer() { - if (mWhiteShadowLayer == null) { - mWhiteShadowLayer = createScaledBitmapWithShadow( - new AdaptiveIconDrawable(new ColorDrawable(Color.WHITE), null)); + public Bitmap getUserBadgeBitmap(UserHandle user) { + if (mUserBadgeBitmap == null) { + Bitmap bitmap = Bitmap.createBitmap( + mIconBitmapSize, mIconBitmapSize, Bitmap.Config.ARGB_8888); + Drawable badgedDrawable = mPm.getUserBadgedIcon( + new FixedSizeBitmapDrawable(bitmap), user); + if (badgedDrawable instanceof BitmapDrawable) { + mUserBadgeBitmap = ((BitmapDrawable) badgedDrawable).getBitmap(); + } else { + badgedDrawable.setBounds(0, 0, mIconBitmapSize, mIconBitmapSize); + mUserBadgeBitmap = BitmapRenderer.createSoftwareBitmap( + mIconBitmapSize, mIconBitmapSize, badgedDrawable::draw); + } } - return mWhiteShadowLayer; - } - - /** package private */ - public Bitmap createScaledBitmapWithShadow(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 mUserBadgeBitmap; } - public Bitmap createScaledBitmapWithoutShadow(Drawable icon) { + public Bitmap createScaledBitmapWithoutShadow(Drawable icon, boolean shrinkNonAdaptiveIcons) { RectF iconBounds = new RectF(); float[] scale = new float[1]; - icon = normalizeAndWrapToAdaptiveIcon(icon, true, iconBounds, scale); + icon = normalizeAndWrapToAdaptiveIcon(icon, shrinkNonAdaptiveIcons, iconBounds, scale); return createIconBitmap(icon, Math.min(scale[0], ShadowGenerator.getScaleForBounds(iconBounds))); } /** + * Switches badging to left/right + */ + public void setBadgeOnLeft(boolean badgeOnLeft) { + mBadgeOnLeft = badgeOnLeft; + } + + /** * Sets the background color used for wrapped adaptive icon */ public void setWrapperBackgroundColor(int color) { @@ -287,7 +293,7 @@ public class BaseIconFactory implements AutoCloseable { } float scale = 1f; - if (shrinkNonAdaptiveIcons && !(icon instanceof AdaptiveIconDrawable)) { + if (shrinkNonAdaptiveIcons && ATLEAST_OREO) { if (mWrapperIcon == null) { mWrapperIcon = mContext.getDrawable(R.drawable.adaptive_icon_drawable_wrapper) .mutate(); @@ -296,12 +302,13 @@ public class BaseIconFactory implements AutoCloseable { dr.setBounds(0, 0, 1, 1); boolean[] outShape = new boolean[1]; scale = getNormalizer().getScale(icon, outIconBounds, dr.getIconMask(), outShape); - if (!outShape[0]) { + if (!(icon instanceof AdaptiveIconDrawable) && !outShape[0]) { FixedScaleDrawable fsd = ((FixedScaleDrawable) dr.getForeground()); fsd.setDrawable(icon); fsd.setScale(scale); icon = dr; scale = getNormalizer().getScale(icon, outIconBounds, null, null); + ((ColorDrawable) dr.getBackground()).setColor(mWrapperBackgroundColor); } } else { @@ -312,6 +319,29 @@ public class BaseIconFactory implements AutoCloseable { return icon; } + /** + * Adds the {@param badge} on top of {@param target} using the badge dimensions. + */ + public void badgeWithDrawable(Bitmap target, Drawable badge) { + mCanvas.setBitmap(target); + badgeWithDrawable(mCanvas, badge); + mCanvas.setBitmap(null); + } + + /** + * Adds the {@param badge} on top of {@param target} using the badge dimensions. + */ + public void badgeWithDrawable(Canvas target, Drawable badge) { + int badgeSize = getBadgeSizeForIconSize(mIconBitmapSize); + if (mBadgeOnLeft) { + badge.setBounds(0, mIconBitmapSize - badgeSize, badgeSize, mIconBitmapSize); + } else { + badge.setBounds(mIconBitmapSize - badgeSize, mIconBitmapSize - badgeSize, + mIconBitmapSize, mIconBitmapSize); + } + badge.draw(target); + } + private Bitmap createIconBitmap(Drawable icon, float scale) { return createIconBitmap(icon, scale, mIconBitmapSize); } @@ -321,32 +351,22 @@ public class BaseIconFactory implements AutoCloseable { * @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); - } - - private Bitmap createIconBitmap(@NonNull Drawable icon, float scale, int size, - Bitmap.Config config) { - Bitmap bitmap = Bitmap.createBitmap(size, size, config); + Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); if (icon == null) { return bitmap; } mCanvas.setBitmap(bitmap); mOldBounds.set(icon.getBounds()); - if (icon instanceof AdaptiveIconDrawable) { + if (ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) { int offset = Math.max((int) Math.ceil(BLUR_FACTOR * size), Math.round(size * (1 - scale) / 2 )); - // b/211896569: AdaptiveIconDrawable do not work properly for non top-left bounds - icon.setBounds(0, 0, size - offset - offset, size - offset - offset); - int count = mCanvas.save(); - mCanvas.translate(offset, offset); - + icon.setBounds(offset, offset, size - offset, size - offset); if (icon instanceof BitmapInfo.Extender) { ((Extender) icon).drawForPersistence(mCanvas); } else { icon.draw(mCanvas); } - mCanvas.restoreToCount(count); } else { if (icon instanceof BitmapDrawable) { BitmapDrawable bitmapDrawable = (BitmapDrawable) icon; @@ -388,13 +408,27 @@ public class BaseIconFactory implements AutoCloseable { clear(); } - public BitmapInfo makeDefaultIcon() { - return createBadgedIconBitmap(getFullResDefaultActivityIcon(mFillResIconDpi)); + public BitmapInfo makeDefaultIcon(UserHandle user) { + return createBadgedIconBitmap(getFullResDefaultActivityIcon(mFillResIconDpi), + user, Build.VERSION.SDK_INT); } public static Drawable getFullResDefaultActivityIcon(int iconDpi) { return Resources.getSystem().getDrawableForDensity( - android.R.drawable.sym_def_app_icon, iconDpi); + Build.VERSION.SDK_INT >= Build.VERSION_CODES.O + ? android.R.drawable.sym_def_app_icon : android.R.mipmap.sym_def_app_icon, + iconDpi); + } + + /** + * Badges the provided source with the badge info + */ + public BitmapInfo badgeBitmap(Bitmap source, BitmapInfo badgeInfo) { + Bitmap icon = BitmapRenderer.createHardwareBitmap(mIconBitmapSize, mIconBitmapSize, (c) -> { + getShadowGenerator().recreateIcon(source, c); + badgeWithDrawable(c, new FixedSizeBitmapDrawable(badgeInfo.icon)); + }); + return BitmapInfo.of(icon, badgeInfo.color); } private int extractColor(Bitmap bitmap) { @@ -408,37 +442,6 @@ public class BaseIconFactory implements AutoCloseable { return (int) (ICON_BADGE_SCALE * iconSize); } - public static class IconOptions { - - boolean mShrinkNonAdaptiveIcons = true; - boolean mIsInstantApp; - UserHandle mUserHandle; - - /** - * Set to false if non-adaptive icons should not be treated - */ - public IconOptions setShrinkNonAdaptiveIcons(boolean shrink) { - mShrinkNonAdaptiveIcons = shrink; - return this; - } - - /** - * User for this icon, in case of badging - */ - public IconOptions setUser(UserHandle user) { - mUserHandle = user; - return this; - } - - /** - * If this icon represents an instant app - */ - public IconOptions setInstantApp(boolean instantApp) { - mIsInstantApp = instantApp; - return this; - } - } - /** * An extension of {@link BitmapDrawable} which returns the bitmap pixel size as intrinsic size. * This allows the badging to be done based on the action bitmap size rather than @@ -460,35 +463,4 @@ public class BaseIconFactory implements AutoCloseable { return getBitmap().getWidth(); } } - - private static class NoopDrawable extends ColorDrawable { - @Override - public int getIntrinsicHeight() { - return 1; - } - - @Override - public int getIntrinsicWidth() { - return 1; - } - } - - private static class ClippedMonoDrawable extends InsetDrawable { - - private final AdaptiveIconDrawable mCrop; - - public ClippedMonoDrawable(Drawable base) { - super(base, -getExtraInsetFraction()); - mCrop = new AdaptiveIconDrawable(new ColorDrawable(Color.BLACK), null); - } - - @Override - public void draw(Canvas canvas) { - mCrop.setBounds(getBounds()); - int saveCount = canvas.save(); - canvas.clipPath(mCrop.getIconMask()); - super.draw(canvas); - canvas.restoreToCount(saveCount); - } - } } diff --git a/iconloaderlib/src/com/android/launcher3/icons/BitmapInfo.java b/iconloaderlib/src/com/android/launcher3/icons/BitmapInfo.java index c3ca42e..06b39b8 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/BitmapInfo.java +++ b/iconloaderlib/src/com/android/launcher3/icons/BitmapInfo.java @@ -15,91 +15,45 @@ */ package com.android.launcher3.icons; +import static com.android.launcher3.icons.GraphicsUtils.getExpectedBitmapSize; + import android.content.Context; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; +import android.graphics.BitmapFactory; import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.os.UserHandle; +import android.util.Log; -import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.launcher3.util.FlagOp; +import com.android.launcher3.icons.ThemedIconDrawable.ThemedBitmapInfo; +import com.android.launcher3.icons.cache.BaseIconCache; -public class BitmapInfo { +import java.io.ByteArrayOutputStream; +import java.io.IOException; - static final int FLAG_WORK = 1 << 0; - static final int FLAG_INSTANT = 1 << 1; - @IntDef(flag = true, value = { - FLAG_WORK, - FLAG_INSTANT, - }) - @interface BitmapInfoFlags {} - - public static final int FLAG_THEMED = 1 << 0; - public static final int FLAG_NO_BADGE = 1 << 1; - @IntDef(flag = true, value = { - FLAG_THEMED, - FLAG_NO_BADGE, - }) - public @interface DrawableCreationFlags {} +public class BitmapInfo { public static final Bitmap LOW_RES_ICON = Bitmap.createBitmap(1, 1, Config.ALPHA_8); public static final BitmapInfo LOW_RES_INFO = fromBitmap(LOW_RES_ICON); public static final String TAG = "BitmapInfo"; + protected static final byte TYPE_DEFAULT = 1; + protected static final byte TYPE_THEMED = 2; + public final Bitmap icon; public final int color; - @Nullable - protected Bitmap mMono; - protected Bitmap mWhiteShadowLayer; - - public @BitmapInfoFlags int flags; - private BitmapInfo badgeInfo; - public BitmapInfo(Bitmap icon, int color) { this.icon = icon; this.color = color; } - public BitmapInfo withBadgeInfo(BitmapInfo badgeInfo) { - BitmapInfo result = clone(); - result.badgeInfo = badgeInfo; - return result; - } - - /** - * Returns a bitmapInfo with the flagOP applied - */ - public BitmapInfo withFlags(@NonNull FlagOp op) { - if (op == FlagOp.NO_OP) { - return this; - } - BitmapInfo result = clone(); - result.flags = op.apply(result.flags); - return result; - } - - protected BitmapInfo copyInternalsTo(BitmapInfo target) { - target.mMono = mMono; - target.mWhiteShadowLayer = mWhiteShadowLayer; - target.flags = flags; - target.badgeInfo = badgeInfo; - return target; - } - - @Override - public BitmapInfo clone() { - return copyInternalsTo(new BitmapInfo(icon, color)); - } - - public void setMonoIcon(Bitmap mono, BaseIconFactory iconFactory) { - mMono = mono; - mWhiteShadowLayer = iconFactory.getWhiteShadowLayer(); - } - /** * Ideally icon should not be null, except in cases when generating hardware bitmap failed */ @@ -112,50 +66,68 @@ public class BitmapInfo { } /** - * BitmapInfo can be stored on disk or other persistent storage + * Returns a serialized version of BitmapInfo */ - public boolean canPersist() { - return !isNullOrLowRes(); + @Nullable + public byte[] toByteArray() { + if (isNullOrLowRes()) { + return null; + } + ByteArrayOutputStream out = new ByteArrayOutputStream(getExpectedBitmapSize(icon) + 1); + try { + out.write(TYPE_DEFAULT); + icon.compress(Bitmap.CompressFormat.PNG, 100, out); + out.flush(); + out.close(); + return out.toByteArray(); + } catch (IOException e) { + Log.w(TAG, "Could not write bitmap"); + return null; + } } - public Bitmap getMono() { - return mMono; + /** + * Returns a new icon based on the theme of the context + */ + public FastBitmapDrawable newThemedIcon(Context context) { + return newIcon(context); } /** * Creates a drawable for the provided BitmapInfo */ public FastBitmapDrawable newIcon(Context context) { - return newIcon(context, 0); + FastBitmapDrawable drawable = isLowRes() + ? new PlaceHolderIconDrawable(this, context) + : new FastBitmapDrawable(this); + drawable.mDisabledAlpha = GraphicsUtils.getFloat(context, R.attr.disabledIconAlpha, 1f); + return drawable; } /** - * Creates a drawable for the provided BitmapInfo + * Returns a BitmapInfo previously serialized using {@link #toByteArray()}; */ - public FastBitmapDrawable newIcon(Context context, @DrawableCreationFlags int creationFlags) { - FastBitmapDrawable drawable; - if (isLowRes()) { - drawable = new PlaceHolderIconDrawable(this, context); - } else if ((creationFlags & FLAG_THEMED) != 0 && mMono != null) { - drawable = ThemedIconDrawable.newDrawable(this, context); + @NonNull + public static BitmapInfo fromByteArray(byte[] data, int color, UserHandle user, + BaseIconCache iconCache, Context context) { + if (data == null) { + return null; + } + BitmapFactory.Options decodeOptions; + if (BitmapRenderer.USE_HARDWARE_BITMAP && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + decodeOptions = new BitmapFactory.Options(); + decodeOptions.inPreferredConfig = Bitmap.Config.HARDWARE; } else { - drawable = new FastBitmapDrawable(this); + decodeOptions = null; } - applyFlags(context, drawable, creationFlags); - return drawable; - } - - protected void applyFlags(Context context, FastBitmapDrawable drawable, - @DrawableCreationFlags int creationFlags) { - drawable.mDisabledAlpha = GraphicsUtils.getFloat(context, R.attr.disabledIconAlpha, 1f); - if ((creationFlags & FLAG_NO_BADGE) == 0) { - if (badgeInfo != null) { - drawable.setBadge(badgeInfo.newIcon(context, creationFlags)); - } else if ((flags & FLAG_INSTANT) != 0) { - drawable.setBadge(context.getDrawable(R.drawable.ic_instant_app_badge)); - } else if ((flags & FLAG_WORK) != 0) { - drawable.setBadge(context.getDrawable(R.drawable.ic_work_app_badge)); - } + if (data[0] == TYPE_DEFAULT) { + return BitmapInfo.of( + BitmapFactory.decodeByteArray(data, 1, data.length - 1, decodeOptions), + color); + } else if (data[0] == TYPE_THEMED) { + return ThemedBitmapInfo.decode(data, color, decodeOptions, user, iconCache, context); + } else { + return null; } } @@ -176,11 +148,16 @@ public class BitmapInfo { * Called for creating a custom BitmapInfo */ BitmapInfo getExtendedInfo(Bitmap bitmap, int color, - BaseIconFactory iconFactory, float normalizationScale); + BaseIconFactory iconFactory, float normalizationScale, UserHandle user); /** * Called to draw the UI independent of any runtime configurations like time or theme */ void drawForPersistence(Canvas canvas); + + /** + * Returns a new icon with theme applied + */ + Drawable getThemedDrawable(Context context); } } diff --git a/iconloaderlib/src/com/android/launcher3/icons/ClockDrawableWrapper.java b/iconloaderlib/src/com/android/launcher3/icons/ClockDrawableWrapper.java index d624805..a7894c9 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/ClockDrawableWrapper.java +++ b/iconloaderlib/src/com/android/launcher3/icons/ClockDrawableWrapper.java @@ -15,7 +15,7 @@ */ package com.android.launcher3.icons; -import static com.android.launcher3.icons.IconProvider.ATLEAST_T; +import static com.android.launcher3.icons.ThemedIconDrawable.getColors; import android.annotation.TargetApi; import android.content.Context; @@ -24,12 +24,13 @@ import android.content.pm.PackageManager; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; -import android.graphics.BlendMode; -import android.graphics.BlendModeColorFilter; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuff.Mode; +import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; import android.graphics.drawable.AdaptiveIconDrawable; import android.graphics.drawable.ColorDrawable; @@ -37,13 +38,15 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.os.Build; import android.os.Bundle; +import android.os.Process; import android.os.SystemClock; +import android.os.UserHandle; import android.util.Log; import android.util.TypedValue; import androidx.annotation.Nullable; -import com.android.launcher3.icons.IconProvider.ThemeData; +import com.android.launcher3.icons.ThemedIconDrawable.ThemeData; import java.util.Calendar; import java.util.concurrent.TimeUnit; @@ -59,7 +62,6 @@ public class ClockDrawableWrapper extends AdaptiveIconDrawable implements Bitmap private static final String TAG = "ClockDrawableWrapper"; private static final boolean DISABLE_SECONDS = true; - private static final int NO_COLOR = -1; // Time after which the clock icon should check for an update. The actual invalidate // will only happen in case of any change. @@ -86,16 +88,32 @@ public class ClockDrawableWrapper extends AdaptiveIconDrawable implements Bitmap public static final int INVALID_VALUE = -1; private final AnimationInfo mAnimationInfo = new AnimationInfo(); - private AnimationInfo mThemeInfo = null; + private int mTargetSdkVersion; + protected ThemeData mThemeData; - private ClockDrawableWrapper(AdaptiveIconDrawable base) { + public ClockDrawableWrapper(AdaptiveIconDrawable base) { super(base.getBackground(), base.getForeground()); } - private void applyThemeData(ThemeData themeData) { - if (!IconProvider.ATLEAST_T || mThemeInfo != null) { - return; + /** + * Loads and returns the wrapper from the provided package, or returns null + * if it is unable to load. + */ + public static ClockDrawableWrapper forPackage(Context context, String pkg, int iconDpi) { + try { + PackageManager pm = context.getPackageManager(); + ApplicationInfo appInfo = pm.getApplicationInfo(pkg, + PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.GET_META_DATA); + Resources res = pm.getResourcesForApplication(appInfo); + return forExtras(appInfo, appInfo.metaData, + resId -> res.getDrawableForDensity(resId, iconDpi)); + } catch (Exception e) { + Log.d(TAG, "Unable to load clock drawable info", e); } + return null; + } + + private static ClockDrawableWrapper fromThemeData(Context context, ThemeData themeData) { try { TypedArray ta = themeData.mResources.obtainTypedArray(themeData.mResID); int count = ta.length(); @@ -107,59 +125,25 @@ public class ClockDrawableWrapper extends AdaptiveIconDrawable implements Bitmap ? v.data : v.resourceId); } ta.recycle(); - ClockDrawableWrapper drawable = ClockDrawableWrapper.forExtras(extras, resId -> { - Drawable bg = new ColorDrawable(Color.WHITE); - Drawable fg = themeData.mResources.getDrawable(resId).mutate(); - return new AdaptiveIconDrawable(bg, fg); - }); + ClockDrawableWrapper drawable = ClockDrawableWrapper.forExtras( + context.getApplicationInfo(), extras, resId -> { + int[] colors = getColors(context); + Drawable bg = new ColorDrawable(colors[0]); + Drawable fg = themeData.mResources.getDrawable(resId).mutate(); + fg.setTint(colors[1]); + return new AdaptiveIconDrawable(bg, fg); + }); if (drawable != null) { - mThemeInfo = drawable.mAnimationInfo; + return drawable; } } catch (Exception e) { Log.e(TAG, "Error loading themed clock", e); } - } - - @Override - public Drawable getMonochrome() { - if (mThemeInfo == null) { - return null; - } - Drawable d = mThemeInfo.baseDrawableState.newDrawable().mutate(); - if (d instanceof AdaptiveIconDrawable) { - Drawable mono = ((AdaptiveIconDrawable) d).getForeground(); - mThemeInfo.applyTime(Calendar.getInstance(), (LayerDrawable) mono); - return mono; - } return null; } - /** - * Loads and returns the wrapper from the provided package, or returns null - * if it is unable to load. - */ - public static ClockDrawableWrapper forPackage(Context context, String pkg, int iconDpi, - @Nullable ThemeData themeData) { - try { - PackageManager pm = context.getPackageManager(); - ApplicationInfo appInfo = pm.getApplicationInfo(pkg, - PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.GET_META_DATA); - Resources res = pm.getResourcesForApplication(appInfo); - ClockDrawableWrapper wrapper = forExtras(appInfo.metaData, - resId -> res.getDrawableForDensity(resId, iconDpi)); - if (wrapper != null && themeData != null) { - wrapper.applyThemeData(themeData); - } - return wrapper; - } catch (Exception e) { - Log.d(TAG, "Unable to load clock drawable info", e); - } - return null; - } - - @TargetApi(Build.VERSION_CODES.TIRAMISU) - private static ClockDrawableWrapper forExtras( - Bundle metadata, IntFunction<Drawable> drawableProvider) { + private static ClockDrawableWrapper forExtras(ApplicationInfo appInfo, Bundle metadata, + IntFunction<Drawable> drawableProvider) { if (metadata == null) { return null; } @@ -172,12 +156,14 @@ public class ClockDrawableWrapper extends AdaptiveIconDrawable implements Bitmap if (!(drawable instanceof AdaptiveIconDrawable)) { return null; } - AdaptiveIconDrawable aid = (AdaptiveIconDrawable) drawable; - ClockDrawableWrapper wrapper = new ClockDrawableWrapper(aid); + ClockDrawableWrapper wrapper = + new ClockDrawableWrapper((AdaptiveIconDrawable) drawable); + wrapper.mTargetSdkVersion = appInfo.targetSdkVersion; AnimationInfo info = wrapper.mAnimationInfo; info.baseDrawableState = drawable.getConstantState(); + info.hourLayerIndex = metadata.getInt(HOUR_INDEX_METADATA_KEY, INVALID_VALUE); info.minuteLayerIndex = metadata.getInt(MINUTE_INDEX_METADATA_KEY, INVALID_VALUE); info.secondLayerIndex = metadata.getInt(SECOND_INDEX_METADATA_KEY, INVALID_VALUE); @@ -200,27 +186,21 @@ public class ClockDrawableWrapper extends AdaptiveIconDrawable implements Bitmap foreground.setDrawable(info.secondLayerIndex, null); info.secondLayerIndex = INVALID_VALUE; } - - if (ATLEAST_T && aid.getMonochrome() instanceof LayerDrawable) { - wrapper.mThemeInfo = info.copyForIcon(new AdaptiveIconDrawable( - new ColorDrawable(Color.WHITE), aid.getMonochrome().mutate())); - } info.applyTime(Calendar.getInstance(), foreground); return wrapper; } @Override public ClockBitmapInfo getExtendedInfo(Bitmap bitmap, int color, - BaseIconFactory iconFactory, float normalizationScale) { + BaseIconFactory iconFactory, float normalizationScale, UserHandle user) { + iconFactory.disableColorExtraction(); AdaptiveIconDrawable background = new AdaptiveIconDrawable( getBackground().getConstantState().newDrawable(), null); - Bitmap flattenBG = iconFactory.createScaledBitmapWithShadow(background); + BitmapInfo bitmapInfo = iconFactory.createBadgedIconBitmap(background, + Process.myUserHandle(), mTargetSdkVersion, false); - // Only pass theme info if mono-icon is enabled - AnimationInfo themeInfo = iconFactory.mMonoIconEnabled ? mThemeInfo : null; - Bitmap themeBG = themeInfo == null ? null : iconFactory.getWhiteShadowLayer(); return new ClockBitmapInfo(bitmap, color, normalizationScale, - mAnimationInfo, flattenBG, themeInfo, themeBG); + mAnimationInfo, bitmapInfo.icon, mThemeData); } @Override @@ -233,6 +213,15 @@ public class ClockDrawableWrapper extends AdaptiveIconDrawable implements Bitmap mAnimationInfo.applyTime(Calendar.getInstance(), (LayerDrawable) getForeground()); } + @Override + public Drawable getThemedDrawable(Context context) { + if (mThemeData != null) { + ClockDrawableWrapper drawable = fromThemeData(context, mThemeData); + return drawable == null ? this : drawable; + } + return this; + } + private void resetLevel(LayerDrawable drawable, int index) { if (index != INVALID_VALUE) { drawable.getDrawable(index).setLevel(0); @@ -250,18 +239,6 @@ public class ClockDrawableWrapper extends AdaptiveIconDrawable implements Bitmap public int defaultMinute; public int defaultSecond; - public AnimationInfo copyForIcon(Drawable icon) { - AnimationInfo result = new AnimationInfo(); - result.baseDrawableState = icon.getConstantState(); - result.defaultHour = defaultHour; - result.defaultMinute = defaultMinute; - result.defaultSecond = defaultSecond; - result.hourLayerIndex = hourLayerIndex; - result.minuteLayerIndex = minuteLayerIndex; - result.secondLayerIndex = secondLayerIndex; - return result; - } - boolean applyTime(Calendar time, LayerDrawable foregroundDrawable) { time.setTimeInMillis(System.currentTimeMillis()); @@ -298,66 +275,66 @@ public class ClockDrawableWrapper extends AdaptiveIconDrawable implements Bitmap static class ClockBitmapInfo extends BitmapInfo { - public final float boundsOffset; - + public final float scale; + public final int offset; public final AnimationInfo animInfo; public final Bitmap mFlattenedBackground; - public final AnimationInfo themeData; - public final Bitmap themeBackground; + public final ThemeData themeData; + public final ColorFilter bgFilter; + + ClockBitmapInfo(Bitmap icon, int color, float scale, AnimationInfo animInfo, + Bitmap background, ThemeData themeData) { + this(icon, color, scale, animInfo, background, themeData, null); + } - ClockBitmapInfo(Bitmap icon, int color, float scale, - AnimationInfo animInfo, Bitmap background, - AnimationInfo themeInfo, Bitmap themeBackground) { + ClockBitmapInfo(Bitmap icon, int color, float scale, AnimationInfo animInfo, + Bitmap background, ThemeData themeData, ColorFilter bgFilter) { super(icon, color); - this.boundsOffset = Math.max(ShadowGenerator.BLUR_FACTOR, (1 - scale) / 2); + this.scale = scale; this.animInfo = animInfo; + this.offset = (int) Math.ceil(ShadowGenerator.BLUR_FACTOR * icon.getWidth()); this.mFlattenedBackground = background; - this.themeData = themeInfo; - this.themeBackground = themeBackground; + this.themeData = themeData; + this.bgFilter = bgFilter; } @Override - @TargetApi(Build.VERSION_CODES.TIRAMISU) - public FastBitmapDrawable newIcon(Context context, - @DrawableCreationFlags int creationFlags) { - AnimationInfo info; - Bitmap bg; - int themedFgColor; - ColorFilter bgFilter; - if ((creationFlags & FLAG_THEMED) != 0 && themeData != null) { - int[] colors = ThemedIconDrawable.getColors(context); - Drawable tintedDrawable = themeData.baseDrawableState.newDrawable().mutate(); - themedFgColor = colors[1]; - tintedDrawable.setTint(colors[1]); - info = themeData.copyForIcon(tintedDrawable); - bg = themeBackground; - bgFilter = new BlendModeColorFilter(colors[0], BlendMode.SRC_IN); - } else { - info = animInfo; - themedFgColor = NO_COLOR; - bg = mFlattenedBackground; - bgFilter = null; - } - if (info == null) { - return super.newIcon(context, creationFlags); + public FastBitmapDrawable newThemedIcon(Context context) { + if (themeData != null) { + ClockDrawableWrapper wrapper = fromThemeData(context, themeData); + if (wrapper != null) { + int[] colors = getColors(context); + ColorFilter bgFilter = new PorterDuffColorFilter(colors[0], Mode.SRC_ATOP); + return new ClockBitmapInfo(icon, colors[1], scale, + wrapper.mAnimationInfo, mFlattenedBackground, themeData, bgFilter) + .newIcon(context); + } } - ClockIconDrawable.ClockConstantState cs = new ClockIconDrawable.ClockConstantState( - icon, color, themedFgColor, boundsOffset, info, bg, bgFilter); - FastBitmapDrawable d = cs.newDrawable(); - applyFlags(context, d, creationFlags); - return d; + return super.newThemedIcon(context); } @Override - public boolean canPersist() { - return false; + public FastBitmapDrawable newIcon(Context context) { + ClockIconDrawable d = new ClockIconDrawable(this); + d.mDisabledAlpha = GraphicsUtils.getFloat(context, R.attr.disabledIconAlpha, 1f); + return d; } + @Nullable @Override - public BitmapInfo clone() { - return copyInternalsTo(new ClockBitmapInfo(icon, color, 1 - 2 * boundsOffset, animInfo, - mFlattenedBackground, themeData, themeBackground)); + public byte[] toByteArray() { + return null; + } + + void drawBackground(Canvas canvas, Rect bounds, Paint paint) { + // draw the background that is already flattened to a bitmap + ColorFilter oldFilter = paint.getColorFilter(); + if (bgFilter != null) { + paint.setColorFilter(bgFilter); + } + canvas.drawBitmap(mFlattenedBackground, null, bounds, paint); + paint.setColorFilter(oldFilter); } } @@ -365,89 +342,59 @@ public class ClockDrawableWrapper extends AdaptiveIconDrawable implements Bitmap private final Calendar mTime = Calendar.getInstance(); - private final float mBoundsOffset; - private final AnimationInfo mAnimInfo; - - private final Bitmap mBG; - private final Paint mBgPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG); - private final ColorFilter mBgFilter; - private final int mThemedFgColor; + private final ClockBitmapInfo mInfo; private final AdaptiveIconDrawable mFullDrawable; - private final LayerDrawable mFG; - private final float mCanvasScale; - - ClockIconDrawable(ClockConstantState cs) { - super(cs.mBitmap, cs.mIconColor); - mBoundsOffset = cs.mBoundsOffset; - mAnimInfo = cs.mAnimInfo; - - mBG = cs.mBG; - mBgFilter = cs.mBgFilter; - mBgPaint.setColorFilter(cs.mBgFilter); - mThemedFgColor = cs.mThemedFgColor; - - mFullDrawable = (AdaptiveIconDrawable) mAnimInfo.baseDrawableState.newDrawable(); - mFG = (LayerDrawable) mFullDrawable.getForeground(); - - // Time needs to be applied here since drawInternal is NOT guaranteed to be called - // before this foreground drawable is shown on the screen. - mAnimInfo.applyTime(mTime, mFG); - mCanvasScale = 1 - 2 * mBoundsOffset; + private final LayerDrawable mForeground; + + ClockIconDrawable(ClockBitmapInfo clockInfo) { + super(clockInfo); + + mInfo = clockInfo; + mFullDrawable = (AdaptiveIconDrawable) mInfo.animInfo.baseDrawableState + .newDrawable().mutate(); + mForeground = (LayerDrawable) mFullDrawable.getForeground(); } @Override protected void onBoundsChange(Rect bounds) { super.onBoundsChange(bounds); - - // b/211896569 AdaptiveIcon does not work properly when bounds - // are not aligned to top/left corner - mFullDrawable.setBounds(0, 0, bounds.width(), bounds.height()); + mFullDrawable.setBounds(bounds); } @Override public void drawInternal(Canvas canvas, Rect bounds) { - if (mAnimInfo == null) { + if (mInfo == null) { super.drawInternal(canvas, bounds); return; } - canvas.drawBitmap(mBG, null, bounds, mBgPaint); + mInfo.drawBackground(canvas, bounds, mPaint); // prepare and draw the foreground - mAnimInfo.applyTime(mTime, mFG); - int saveCount = canvas.save(); - canvas.translate(bounds.left, bounds.top); - canvas.scale(mCanvasScale, mCanvasScale, bounds.width() / 2, bounds.height() / 2); + mInfo.animInfo.applyTime(mTime, mForeground); + + canvas.scale(mInfo.scale, mInfo.scale, + bounds.exactCenterX() + mInfo.offset, bounds.exactCenterY() + mInfo.offset); canvas.clipPath(mFullDrawable.getIconMask()); - mFG.draw(canvas); - canvas.restoreToCount(saveCount); + mForeground.draw(canvas); reschedule(); } @Override public boolean isThemed() { - return mBgPaint.getColorFilter() != null; + return mInfo.bgFilter != null; } @Override protected void updateFilter() { super.updateFilter(); - int alpha = mIsDisabled ? (int) (mDisabledAlpha * FULLY_OPAQUE) : FULLY_OPAQUE; - mBgPaint.setAlpha(alpha); - mFG.setAlpha(alpha); - mBgPaint.setColorFilter(mIsDisabled ? getDisabledColorFilter() : mBgFilter); - mFG.setColorFilter(mIsDisabled ? getDisabledColorFilter() : null); - } - - @Override - public int getIconColor() { - return isThemed() ? mThemedFgColor : super.getIconColor(); + mFullDrawable.setColorFilter(mPaint.getColorFilter()); } @Override public void run() { - if (mAnimInfo.applyTime(mTime, mFG)) { + if (mInfo.animInfo.applyTime(mTime, mForeground)) { invalidateSelf(); } else { reschedule(); @@ -477,32 +424,24 @@ public class ClockDrawableWrapper extends AdaptiveIconDrawable implements Bitmap } @Override - public FastBitmapConstantState newConstantState() { - return new ClockConstantState(mBitmap, mIconColor, mThemedFgColor, mBoundsOffset, - mAnimInfo, mBG, mBgPaint.getColorFilter()); + public ConstantState getConstantState() { + return new ClockConstantState(mInfo, isDisabled()); } private static class ClockConstantState extends FastBitmapConstantState { - private final float mBoundsOffset; - private final AnimationInfo mAnimInfo; - private final Bitmap mBG; - private final ColorFilter mBgFilter; - private final int mThemedFgColor; - - ClockConstantState(Bitmap bitmap, int color, int themedFgColor, - float boundsOffset, AnimationInfo animInfo, Bitmap bg, ColorFilter bgFilter) { - super(bitmap, color); - mBoundsOffset = boundsOffset; - mAnimInfo = animInfo; - mBG = bg; - mBgFilter = bgFilter; - mThemedFgColor = themedFgColor; + private final ClockBitmapInfo mInfo; + + ClockConstantState(ClockBitmapInfo info, boolean isDisabled) { + super(info.icon, info.color, isDisabled); + mInfo = info; } @Override - public FastBitmapDrawable createDrawable() { - return new ClockIconDrawable(this); + public FastBitmapDrawable newDrawable() { + ClockIconDrawable drawable = new ClockIconDrawable(mInfo); + drawable.setIsDisabled(mIsDisabled); + return drawable; } } } diff --git a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java index a42232e..97a0fd3 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java +++ b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java @@ -50,12 +50,8 @@ public class DotRenderer { private final float[] mRightDotPosition; private final float[] mLeftDotPosition; - private static final int MIN_DOT_SIZE = 1; public DotRenderer(int iconSizePx, Path iconShapePath, int pathSize) { int size = Math.round(SIZE_PERCENTAGE * iconSizePx); - if (size <= 0) { - size = MIN_DOT_SIZE; - } ShadowGenerator.Builder builder = new ShadowGenerator.Builder(Color.TRANSPARENT); builder.ambientShadowAlpha = 88; mBackgroundWithShadow = builder.setupBlurForSize(size).createPill(size, size); @@ -125,7 +121,7 @@ public class DotRenderer { mCirclePaint.setColor(Color.BLACK); canvas.drawBitmap(mBackgroundWithShadow, mBitmapOffset, mBitmapOffset, mCirclePaint); - mCirclePaint.setColor(params.dotColor); + mCirclePaint.setColor(params.color); canvas.drawCircle(0, 0, mCircleRadius, mCirclePaint); canvas.restore(); } @@ -133,10 +129,7 @@ public class DotRenderer { public static class DrawParams { /** The color (possibly based on the icon) to use for the dot. */ @ViewDebug.ExportedProperty(category = "notification dot", formatToHexString = true) - public int dotColor; - /** The color (possibly based on the icon) to use for a predicted app. */ - @ViewDebug.ExportedProperty(category = "notification dot", formatToHexString = true) - public int appColor; + public int color; /** The bounds of the icon that the dot is drawn on top of. */ @ViewDebug.ExportedProperty(category = "notification dot") public Rect iconBounds = new Rect(); diff --git a/iconloaderlib/src/com/android/launcher3/icons/FastBitmapDrawable.java b/iconloaderlib/src/com/android/launcher3/icons/FastBitmapDrawable.java index 513a75d..4aa2846 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/FastBitmapDrawable.java +++ b/iconloaderlib/src/com/android/launcher3/icons/FastBitmapDrawable.java @@ -16,9 +16,6 @@ package com.android.launcher3.icons; -import static com.android.launcher3.icons.BaseIconFactory.getBadgeSizeForIconSize; -import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound; - import android.animation.ObjectAnimator; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -30,30 +27,29 @@ import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Drawable; -import android.util.FloatProperty; +import android.util.Property; import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import androidx.annotation.Nullable; -import androidx.core.graphics.ColorUtils; -public class FastBitmapDrawable extends Drawable implements Drawable.Callback { +public class FastBitmapDrawable extends Drawable { private static final Interpolator ACCEL = new AccelerateInterpolator(); private static final Interpolator DEACCEL = new DecelerateInterpolator(); private static final float PRESSED_SCALE = 1.1f; - public static final int WHITE_SCRIM_ALPHA = 138; private static final float DISABLED_DESATURATION = 1f; private static final float DISABLED_BRIGHTNESS = 0.5f; - protected static final int FULLY_OPAQUE = 255; public static final int CLICK_FEEDBACK_DURATION = 200; + private static ColorFilter sDisabledFColorFilter; + protected final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG); - protected final Bitmap mBitmap; + protected Bitmap mBitmap; protected final int mIconColor; @Nullable private ColorFilter mColorFilter; @@ -63,24 +59,23 @@ public class FastBitmapDrawable extends Drawable implements Drawable.Callback { float mDisabledAlpha = 1f; // Animator and properties for the fast bitmap drawable's scale - private static final FloatProperty<FastBitmapDrawable> SCALE - = new FloatProperty<FastBitmapDrawable>("scale") { + private static final Property<FastBitmapDrawable, Float> SCALE + = new Property<FastBitmapDrawable, Float>(Float.TYPE, "scale") { @Override public Float get(FastBitmapDrawable fastBitmapDrawable) { return fastBitmapDrawable.mScale; } @Override - public void setValue(FastBitmapDrawable fastBitmapDrawable, float value) { + public void set(FastBitmapDrawable fastBitmapDrawable, Float value) { fastBitmapDrawable.mScale = value; fastBitmapDrawable.invalidateSelf(); } }; private ObjectAnimator mScaleAnimation; private float mScale = 1; - private int mAlpha = 255; - private Drawable mBadge; + private int mAlpha = 255; public FastBitmapDrawable(Bitmap b) { this(b, Color.TRANSPARENT); @@ -91,21 +86,14 @@ public class FastBitmapDrawable extends Drawable implements Drawable.Callback { } protected FastBitmapDrawable(Bitmap b, int iconColor) { + this(b, iconColor, false); + } + + protected FastBitmapDrawable(Bitmap b, int iconColor, boolean isDisabled) { mBitmap = b; mIconColor = iconColor; setFilterBitmap(true); - } - - @Override - protected void onBoundsChange(Rect bounds) { - super.onBoundsChange(bounds); - updateBadgeBounds(bounds); - } - - private void updateBadgeBounds(Rect bounds) { - if (mBadge != null) { - setBadgeBounds(mBadge, bounds); - } + setIsDisabled(isDisabled); } @Override @@ -115,15 +103,9 @@ public class FastBitmapDrawable extends Drawable implements Drawable.Callback { Rect bounds = getBounds(); canvas.scale(mScale, mScale, bounds.exactCenterX(), bounds.exactCenterY()); drawInternal(canvas, bounds); - if (mBadge != null) { - mBadge.draw(canvas); - } canvas.restoreToCount(count); } else { drawInternal(canvas, getBounds()); - if (mBadge != null) { - mBadge.draw(canvas); - } } } @@ -132,11 +114,10 @@ public class FastBitmapDrawable extends Drawable implements Drawable.Callback { } /** - * Returns the primary icon color, slightly tinted white + * Returns the primary icon color */ public int getIconColor() { - int whiteScrim = setColorAlphaBound(Color.WHITE, WHITE_SCRIM_ALPHA); - return ColorUtils.compositeColors(whiteScrim, mIconColor); + return mIconColor; } /** @@ -270,48 +251,27 @@ public class FastBitmapDrawable extends Drawable implements Drawable.Callback { return mIsDisabled; } - public void setBadge(Drawable badge) { - if (mBadge != null) { - mBadge.setCallback(null); - } - mBadge = badge; - if (mBadge != null) { - mBadge.setCallback(this); + private ColorFilter getDisabledColorFilter() { + if (sDisabledFColorFilter == null) { + sDisabledFColorFilter = getDisabledFColorFilter(mDisabledAlpha); } - updateBadgeBounds(getBounds()); - updateFilter(); + return sDisabledFColorFilter; } /** * Updates the paint to reflect the current brightness and saturation. */ protected void updateFilter() { - mPaint.setColorFilter(mIsDisabled ? getDisabledColorFilter(mDisabledAlpha) : mColorFilter); - if (mBadge != null) { - mBadge.setColorFilter(getColorFilter()); - } + mPaint.setColorFilter(mIsDisabled ? getDisabledColorFilter() : mColorFilter); invalidateSelf(); } - protected FastBitmapConstantState newConstantState() { - return new FastBitmapConstantState(mBitmap, mIconColor); - } - @Override - public final ConstantState getConstantState() { - FastBitmapConstantState cs = newConstantState(); - cs.mIsDisabled = mIsDisabled; - if (mBadge != null) { - cs.mBadgeConstantState = mBadge.getConstantState(); - } - return cs; + public ConstantState getConstantState() { + return new FastBitmapConstantState(mBitmap, mIconColor, mIsDisabled); } - public static ColorFilter getDisabledColorFilter() { - return getDisabledColorFilter(1); - } - - private static ColorFilter getDisabledColorFilter(float disabledAlpha) { + public static ColorFilter getDisabledFColorFilter(float disabledAlpha) { ColorMatrix tempBrightnessMatrix = new ColorMatrix(); ColorMatrix tempFilterMatrix = new ColorMatrix(); @@ -327,71 +287,23 @@ public class FastBitmapDrawable extends Drawable implements Drawable.Callback { mat[14] = brightnessI; mat[18] = disabledAlpha; tempFilterMatrix.preConcat(tempBrightnessMatrix); - return new ColorMatrixColorFilter(tempFilterMatrix); - } - - protected static final int getDisabledColor(int color) { - int component = (Color.red(color) + Color.green(color) + Color.blue(color)) / 3; - float scale = 1 - DISABLED_BRIGHTNESS; - int brightnessI = (int) (255 * DISABLED_BRIGHTNESS); - component = Math.min(Math.round(scale * component + brightnessI), FULLY_OPAQUE); - return Color.rgb(component, component, component); - } - - /** - * Sets the bounds for the badge drawable based on the main icon bounds - */ - public static void setBadgeBounds(Drawable badge, Rect iconBounds) { - int size = getBadgeSizeForIconSize(iconBounds.width()); - badge.setBounds(iconBounds.right - size, iconBounds.bottom - size, - iconBounds.right, iconBounds.bottom); - } - - @Override - public void invalidateDrawable(Drawable who) { - if (who == mBadge) { - invalidateSelf(); - } - } - - @Override - public void scheduleDrawable(Drawable who, Runnable what, long when) { - if (who == mBadge) { - scheduleSelf(what, when); - } - } - - @Override - public void unscheduleDrawable(Drawable who, Runnable what) { - unscheduleSelf(what); + return new ColorMatrixColorFilter(tempBrightnessMatrix); } protected static class FastBitmapConstantState extends ConstantState { protected final Bitmap mBitmap; protected final int mIconColor; + protected final boolean mIsDisabled; - // These are initialized later so that subclasses don't need to - // pass everything in constructor - protected boolean mIsDisabled; - private ConstantState mBadgeConstantState; - - public FastBitmapConstantState(Bitmap bitmap, int color) { + public FastBitmapConstantState(Bitmap bitmap, int color, boolean isDisabled) { mBitmap = bitmap; mIconColor = color; - } - - protected FastBitmapDrawable createDrawable() { - return new FastBitmapDrawable(mBitmap, mIconColor); + mIsDisabled = isDisabled; } @Override - public final FastBitmapDrawable newDrawable() { - FastBitmapDrawable drawable = createDrawable(); - drawable.setIsDisabled(mIsDisabled); - if (mBadgeConstantState != null) { - drawable.setBadge(mBadgeConstantState.newDrawable()); - } - return drawable; + public FastBitmapDrawable newDrawable() { + return new FastBitmapDrawable(mBitmap, mIconColor, mIsDisabled); } @Override diff --git a/iconloaderlib/src/com/android/launcher3/icons/IconNormalizer.java b/iconloaderlib/src/com/android/launcher3/icons/IconNormalizer.java index d699225..de39e79 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/IconNormalizer.java +++ b/iconloaderlib/src/com/android/launcher3/icons/IconNormalizer.java @@ -238,7 +238,7 @@ public class IconNormalizer { */ public synchronized float getScale(@NonNull Drawable d, @Nullable RectF outBounds, @Nullable Path path, @Nullable boolean[] outMaskShape) { - if (d instanceof AdaptiveIconDrawable) { + if (BaseIconFactory.ATLEAST_OREO && d instanceof AdaptiveIconDrawable) { if (mAdaptiveIconScale == SCALE_NOT_INITIALIZED) { mAdaptiveIconScale = normalizeAdaptiveIcon(d, mMaxSize, mAdaptiveIconBounds); } diff --git a/iconloaderlib/src/com/android/launcher3/icons/IconProvider.java b/iconloaderlib/src/com/android/launcher3/icons/IconProvider.java index 204651c..449c0da 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/IconProvider.java +++ b/iconloaderlib/src/com/android/launcher3/icons/IconProvider.java @@ -20,9 +20,7 @@ import static android.content.Intent.ACTION_DATE_CHANGED; import static android.content.Intent.ACTION_TIMEZONE_CHANGED; import static android.content.Intent.ACTION_TIME_CHANGED; import static android.content.res.Resources.ID_NULL; -import static android.graphics.drawable.AdaptiveIconDrawable.getExtraInsetFraction; -import android.annotation.TargetApi; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -33,11 +31,8 @@ import android.content.pm.LauncherActivityInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; -import android.content.res.TypedArray; -import android.graphics.drawable.AdaptiveIconDrawable; +import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; -import android.graphics.drawable.InsetDrawable; -import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.PatternMatcher; @@ -45,14 +40,17 @@ import android.os.Process; import android.os.UserHandle; import android.os.UserManager; import android.text.TextUtils; +import android.util.ArrayMap; import android.util.Log; -import androidx.annotation.Nullable; -import androidx.core.os.BuildCompat; - +import com.android.launcher3.icons.ThemedIconDrawable.ThemeData; import com.android.launcher3.util.SafeCloseable; +import org.xmlpull.v1.XmlPullParser; + import java.util.Calendar; +import java.util.Collections; +import java.util.Map; import java.util.function.Supplier; /** @@ -64,22 +62,49 @@ public class IconProvider { private static final int CONFIG_ICON_MASK_RES_ID = Resources.getSystem().getIdentifier( "config_icon_mask", "string", "android"); + private static final String TAG_ICON = "icon"; + private static final String ATTR_PACKAGE = "package"; + private static final String ATTR_DRAWABLE = "drawable"; + private static final String TAG = "IconProvider"; private static final boolean DEBUG = false; - public static final boolean ATLEAST_T = BuildCompat.isAtLeastT(); private static final String ICON_METADATA_KEY_PREFIX = ".dynamic_icons"; private static final String SYSTEM_STATE_SEPARATOR = " "; + private static final String THEMED_ICON_MAP_FILE = "grayscale_icon_map"; - protected final Context mContext; + private static final Map<String, ThemeData> DISABLED_MAP = Collections.emptyMap(); + + private Map<String, ThemeData> mThemedIconMap; + + private final Context mContext; private final ComponentName mCalendar; private final ComponentName mClock; + static final int ICON_TYPE_DEFAULT = 0; + static final int ICON_TYPE_CALENDAR = 1; + static final int ICON_TYPE_CLOCK = 2; + public IconProvider(Context context) { + this(context, false); + } + + public IconProvider(Context context, boolean supportsIconTheme) { mContext = context; mCalendar = parseComponentOrNull(context, R.string.calendar_component_name); mClock = parseComponentOrNull(context, R.string.clock_component_name); + if (!supportsIconTheme) { + // Initialize an empty map if theming is not supported + mThemedIconMap = DISABLED_MAP; + } + } + + /** + * Enables or disables icon theme support + */ + public void setIconThemeSupported(boolean isSupported) { + mThemedIconMap = isSupported ? null : DISABLED_MAP; } /** @@ -98,7 +123,7 @@ public class IconProvider { * Loads the icon for the provided LauncherActivityInfo */ public Drawable getIcon(LauncherActivityInfo info, int iconDpi) { - return getIconWithOverrides(info.getApplicationInfo().packageName, iconDpi, + return getIconWithOverrides(info.getApplicationInfo().packageName, info.getUser(), iconDpi, () -> info.getIcon(iconDpi)); } @@ -113,36 +138,32 @@ public class IconProvider { * Loads the icon for the provided activity info */ public Drawable getIcon(ActivityInfo info, int iconDpi) { - return getIconWithOverrides(info.applicationInfo.packageName, iconDpi, - () -> loadActivityInfoIcon(info, iconDpi)); + return getIconWithOverrides(info.applicationInfo.packageName, + UserHandle.getUserHandleForUid(info.applicationInfo.uid), + iconDpi, () -> loadActivityInfoIcon(info, iconDpi)); } - @TargetApi(Build.VERSION_CODES.TIRAMISU) - private Drawable getIconWithOverrides(String packageName, int iconDpi, + private Drawable getIconWithOverrides(String packageName, UserHandle user, int iconDpi, Supplier<Drawable> fallback) { - ThemeData td = getThemeDataForPackage(packageName); - Drawable icon = null; + + int iconType = ICON_TYPE_DEFAULT; if (mCalendar != null && mCalendar.getPackageName().equals(packageName)) { - icon = loadCalendarDrawable(iconDpi, td); - } else if (mClock != null && mClock.getPackageName().equals(packageName)) { - icon = ClockDrawableWrapper.forPackage(mContext, mClock.getPackageName(), iconDpi, td); + icon = loadCalendarDrawable(iconDpi); + iconType = ICON_TYPE_CALENDAR; + } else if (mClock != null + && mClock.getPackageName().equals(packageName) + && Process.myUserHandle().equals(user)) { + icon = loadClockDrawable(iconDpi); + iconType = ICON_TYPE_CLOCK; } if (icon == null) { icon = fallback.get(); - if (ATLEAST_T && icon instanceof AdaptiveIconDrawable && td != null) { - AdaptiveIconDrawable aid = (AdaptiveIconDrawable) icon; - if (aid.getMonochrome() == null) { - icon = new AdaptiveIconDrawable(aid.getBackground(), - aid.getForeground(), td.loadPaddedDrawable()); - } - } + iconType = ICON_TYPE_DEFAULT; } - return icon; - } - protected ThemeData getThemeDataForPackage(String packageName) { - return null; + ThemeData td = getThemedIconMap().get(packageName); + return td != null ? td.wrapDrawable(icon, iconType) : icon; } private Drawable loadActivityInfoIcon(ActivityInfo ai, int density) { @@ -163,8 +184,45 @@ public class IconProvider { return icon; } - @TargetApi(Build.VERSION_CODES.TIRAMISU) - private Drawable loadCalendarDrawable(int iconDpi, @Nullable ThemeData td) { + private Map<String, ThemeData> getThemedIconMap() { + if (mThemedIconMap != null) { + return mThemedIconMap; + } + ArrayMap<String, ThemeData> map = new ArrayMap<>(); + try { + Resources res = mContext.getResources(); + int resID = res.getIdentifier(THEMED_ICON_MAP_FILE, "xml", mContext.getPackageName()); + if (resID != 0) { + XmlResourceParser parser = res.getXml(resID); + final int depth = parser.getDepth(); + + int type; + + while ((type = parser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT); + + while (((type = parser.next()) != XmlPullParser.END_TAG || + parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) { + if (type != XmlPullParser.START_TAG) { + continue; + } + if (TAG_ICON.equals(parser.getName())) { + String pkg = parser.getAttributeValue(null, ATTR_PACKAGE); + int iconId = parser.getAttributeResourceValue(null, ATTR_DRAWABLE, 0); + if (iconId != 0 && !TextUtils.isEmpty(pkg)) { + map.put(pkg, new ThemeData(res, iconId)); + } + } + } + } + } catch (Exception e) { + Log.e(TAG, "Unable to parse icon map", e); + } + mThemedIconMap = map; + return mThemedIconMap; + } + + private Drawable loadCalendarDrawable(int iconDpi) { PackageManager pm = mContext.getPackageManager(); try { final Bundle metadata = pm.getActivityInfo( @@ -175,22 +233,7 @@ public class IconProvider { final int id = getDynamicIconId(metadata, resources); if (id != ID_NULL) { if (DEBUG) Log.d(TAG, "Got icon #" + id); - Drawable drawable = resources.getDrawableForDensity(id, iconDpi, null /* theme */); - if (ATLEAST_T && drawable instanceof AdaptiveIconDrawable && td != null) { - AdaptiveIconDrawable aid = (AdaptiveIconDrawable) drawable; - if (aid.getMonochrome() != null) { - return drawable; - } - if ("array".equals(td.mResources.getResourceTypeName(td.mResID))) { - TypedArray ta = td.mResources.obtainTypedArray(td.mResID); - int monoId = ta.getResourceId(IconProvider.getDay(), ID_NULL); - ta.recycle(); - return monoId == ID_NULL ? drawable - : new AdaptiveIconDrawable(aid.getBackground(), aid.getForeground(), - new ThemeData(td.mResources, monoId).loadPaddedDrawable()); - } - } - return drawable; + return resources.getDrawableForDensity(id, iconDpi, null /* theme */); } } catch (PackageManager.NameNotFoundException e) { if (DEBUG) { @@ -201,6 +244,10 @@ public class IconProvider { return null; } + private Drawable loadClockDrawable(int iconDpi) { + return ClockDrawableWrapper.forPackage(mContext, mClock.getPackageName(), iconDpi); + } + /** * @param metadata metadata of the default activity of Calendar * @param resources from the Calendar package @@ -228,7 +275,7 @@ public class IconProvider { /** * @return Today's day of the month, zero-indexed. */ - private static int getDay() { + static int getDay() { return Calendar.getInstance().get(Calendar.DAY_OF_MONTH) - 1; } @@ -242,7 +289,8 @@ public class IconProvider { */ public String getSystemIconState() { return (CONFIG_ICON_MASK_RES_ID == ID_NULL - ? "" : mContext.getResources().getString(CONFIG_ICON_MASK_RES_ID)); + ? "" : mContext.getResources().getString(CONFIG_ICON_MASK_RES_ID)) + + (mThemedIconMap == DISABLED_MAP ? ",no-theme" : ",with-theme"); } /** @@ -252,28 +300,6 @@ public class IconProvider { return new IconChangeReceiver(listener, handler); } - public static class ThemeData { - - final Resources mResources; - final int mResID; - - public ThemeData(Resources resources, int resID) { - mResources = resources; - mResID = resID; - } - - Drawable loadPaddedDrawable() { - if (!"drawable".equals(mResources.getResourceTypeName(mResID))) { - return null; - } - Drawable d = mResources.getDrawable(mResID).mutate(); - d = new InsetDrawable(d, .2f); - float inset = getExtraInsetFraction() / (1 + 2 * getExtraInsetFraction()); - Drawable fg = new InsetDrawable(d, inset); - return fg; - } - } - private class IconChangeReceiver extends BroadcastReceiver implements SafeCloseable { private final IconChangeListener mCallback; diff --git a/iconloaderlib/src/com/android/launcher3/icons/ShadowGenerator.java b/iconloaderlib/src/com/android/launcher3/icons/ShadowGenerator.java index 96dee3b..e24f353 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/ShadowGenerator.java +++ b/iconloaderlib/src/com/android/launcher3/icons/ShadowGenerator.java @@ -35,14 +35,14 @@ public class ShadowGenerator { public static final boolean ENABLE_SHADOWS = true; - public static final float BLUR_FACTOR = 1.68f/48; + public static final float BLUR_FACTOR = 1.5f/48; // Percent of actual icon size public static final float KEY_SHADOW_DISTANCE = 1f/48; - private static final int KEY_SHADOW_ALPHA = 7; + private static final int KEY_SHADOW_ALPHA = 10; // Percent of actual icon size private static final float HALF_DISTANCE = 0.5f; - private static final int AMBIENT_SHADOW_ALPHA = 25; + private static final int AMBIENT_SHADOW_ALPHA = 7; private final int mIconSize; diff --git a/iconloaderlib/src/com/android/launcher3/icons/ThemedIconDrawable.java b/iconloaderlib/src/com/android/launcher3/icons/ThemedIconDrawable.java index 494d657..b2e554b 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/ThemedIconDrawable.java +++ b/iconloaderlib/src/com/android/launcher3/icons/ThemedIconDrawable.java @@ -17,16 +17,37 @@ package com.android.launcher3.icons; import static android.content.res.Configuration.UI_MODE_NIGHT_MASK; import static android.content.res.Configuration.UI_MODE_NIGHT_YES; +import static android.content.res.Resources.ID_NULL; + +import static com.android.launcher3.icons.GraphicsUtils.getExpectedBitmapSize; +import static com.android.launcher3.icons.IconProvider.ICON_TYPE_CALENDAR; +import static com.android.launcher3.icons.IconProvider.ICON_TYPE_CLOCK; import android.content.Context; import android.content.res.Resources; +import android.content.res.TypedArray; import android.graphics.Bitmap; -import android.graphics.BlendMode; -import android.graphics.BlendModeColorFilter; +import android.graphics.BitmapFactory; import android.graphics.Canvas; -import android.graphics.ColorFilter; -import android.graphics.Paint; import android.graphics.Rect; +import android.graphics.drawable.AdaptiveIconDrawable; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.InsetDrawable; +import android.os.Process; +import android.os.UserHandle; +import android.util.Log; + +import androidx.annotation.Nullable; + +import com.android.launcher3.icons.BitmapInfo.Extender; +import com.android.launcher3.icons.cache.BaseIconCache; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; /** * Class to handle monochrome themed app icons @@ -36,50 +57,46 @@ public class ThemedIconDrawable extends FastBitmapDrawable { public static final String TAG = "ThemedIconDrawable"; - final BitmapInfo bitmapInfo; + final ThemedBitmapInfo bitmapInfo; final int colorFg, colorBg; // The foreground/monochrome icon for the app - private final Bitmap mMonoIcon; - private final Paint mMonoPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); - - private final Bitmap mBgBitmap; - private final Paint mBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); - - private final ColorFilter mBgFilter, mMonoFilter; + private final Drawable mMonochromeIcon; + private final AdaptiveIconDrawable mBgWrapper; + private final Rect mBadgeBounds; protected ThemedIconDrawable(ThemedConstantState constantState) { - super(constantState.mBitmap, constantState.colorFg); + super(constantState.mBitmap, constantState.colorFg, constantState.mIsDisabled); bitmapInfo = constantState.bitmapInfo; colorBg = constantState.colorBg; colorFg = constantState.colorFg; - mMonoIcon = bitmapInfo.mMono; - mMonoFilter = new BlendModeColorFilter(colorFg, BlendMode.SRC_IN); - mMonoPaint.setColorFilter(mMonoFilter); + mMonochromeIcon = bitmapInfo.mThemeData.loadMonochromeDrawable(colorFg); + mBgWrapper = new AdaptiveIconDrawable(new ColorDrawable(colorBg), null); + mBadgeBounds = bitmapInfo.mUserBadge == null ? null : + new Rect(0, 0, bitmapInfo.mUserBadge.getWidth(), bitmapInfo.mUserBadge.getHeight()); - mBgBitmap = bitmapInfo.mWhiteShadowLayer; - mBgFilter = new BlendModeColorFilter(colorBg, BlendMode.SRC_IN); - mBgPaint.setColorFilter(mBgFilter); } @Override - protected void drawInternal(Canvas canvas, Rect bounds) { - canvas.drawBitmap(mBgBitmap, null, bounds, mBgPaint); - canvas.drawBitmap(mMonoIcon, null, bounds, mMonoPaint); + protected void onBoundsChange(Rect bounds) { + super.onBoundsChange(bounds); + mBgWrapper.setBounds(bounds); + mMonochromeIcon.setBounds(bounds); } @Override - protected void updateFilter() { - super.updateFilter(); - int alpha = mIsDisabled ? (int) (mDisabledAlpha * FULLY_OPAQUE) : FULLY_OPAQUE; - mBgPaint.setAlpha(alpha); - mBgPaint.setColorFilter(mIsDisabled ? new BlendModeColorFilter( - getDisabledColor(colorBg), BlendMode.SRC_IN) : mBgFilter); - - mMonoPaint.setAlpha(alpha); - mMonoPaint.setColorFilter(mIsDisabled ? new BlendModeColorFilter( - getDisabledColor(colorFg), BlendMode.SRC_IN) : mMonoFilter); + protected void drawInternal(Canvas canvas, Rect bounds) { + int count = canvas.save(); + canvas.scale(bitmapInfo.mNormalizationScale, bitmapInfo.mNormalizationScale, + bounds.exactCenterX(), bounds.exactCenterY()); + mPaint.setColor(colorBg); + canvas.drawPath(mBgWrapper.getIconMask(), mPaint); + mMonochromeIcon.draw(canvas); + canvas.restoreToCount(count); + if (mBadgeBounds != null) { + canvas.drawBitmap(bitmapInfo.mUserBadge, mBadgeBounds, getBounds(), mPaint); + } } @Override @@ -88,35 +105,177 @@ public class ThemedIconDrawable extends FastBitmapDrawable { } @Override - public FastBitmapConstantState newConstantState() { - return new ThemedConstantState(bitmapInfo, colorBg, colorFg); - } - - public void changeBackgroundColor(int colorBg){ - mBgPaint.setColorFilter(new BlendModeColorFilter(colorBg, BlendMode.SRC_IN)); + public ConstantState getConstantState() { + return new ThemedConstantState(bitmapInfo, colorBg, colorFg, mIsDisabled); } static class ThemedConstantState extends FastBitmapConstantState { - final BitmapInfo bitmapInfo; + final ThemedBitmapInfo bitmapInfo; final int colorFg, colorBg; - public ThemedConstantState(BitmapInfo bitmapInfo, int colorBg, int colorFg) { - super(bitmapInfo.icon, bitmapInfo.color); + public ThemedConstantState(ThemedBitmapInfo bitmapInfo, + int colorBg, int colorFg, boolean isDisabled) { + super(bitmapInfo.icon, bitmapInfo.color, isDisabled); this.bitmapInfo = bitmapInfo; this.colorBg = colorBg; this.colorFg = colorFg; } @Override - public FastBitmapDrawable createDrawable() { + public FastBitmapDrawable newDrawable() { return new ThemedIconDrawable(this); } } - public static FastBitmapDrawable newDrawable(BitmapInfo info, Context context) { - int[] colors = getColors(context); - return new ThemedConstantState(info, colors[0], colors[1]).newDrawable(); + public static class ThemedBitmapInfo extends BitmapInfo { + + final ThemeData mThemeData; + final float mNormalizationScale; + final Bitmap mUserBadge; + + public ThemedBitmapInfo(Bitmap icon, int color, ThemeData themeData, + float normalizationScale, Bitmap userBadge) { + super(icon, color); + mThemeData = themeData; + mNormalizationScale = normalizationScale; + mUserBadge = userBadge; + } + + @Override + public FastBitmapDrawable newThemedIcon(Context context) { + int[] colors = getColors(context); + FastBitmapDrawable drawable = new ThemedConstantState(this, colors[0], colors[1], false) + .newDrawable(); + drawable.mDisabledAlpha = GraphicsUtils.getFloat(context, R.attr.disabledIconAlpha, 1f); + return drawable; + } + + @Nullable + public byte[] toByteArray() { + if (isNullOrLowRes()) { + return null; + } + String resName = mThemeData.mResources.getResourceName(mThemeData.mResID); + ByteArrayOutputStream out = new ByteArrayOutputStream( + getExpectedBitmapSize(icon) + 3 + resName.length()); + try { + DataOutputStream dos = new DataOutputStream(out); + dos.writeByte(TYPE_THEMED); + dos.writeFloat(mNormalizationScale); + dos.writeUTF(resName); + icon.compress(Bitmap.CompressFormat.PNG, 100, dos); + + dos.flush(); + dos.close(); + return out.toByteArray(); + } catch (IOException e) { + Log.w(TAG, "Could not write bitmap"); + return null; + } + } + + static ThemedBitmapInfo decode(byte[] data, int color, + BitmapFactory.Options decodeOptions, UserHandle user, BaseIconCache iconCache, + Context context) { + try (DataInputStream dis = new DataInputStream(new ByteArrayInputStream(data))) { + dis.readByte(); // type + float normalizationScale = dis.readFloat(); + + String resName = dis.readUTF(); + int resId = context.getResources() + .getIdentifier(resName, "drawable", context.getPackageName()); + if (resId == ID_NULL) { + return null; + } + + Bitmap userBadgeBitmap = null; + if (!Process.myUserHandle().equals(user)) { + try (BaseIconFactory iconFactory = iconCache.getIconFactory()) { + userBadgeBitmap = iconFactory.getUserBadgeBitmap(user); + } + } + + ThemeData themeData = new ThemeData(context.getResources(), resId); + Bitmap icon = BitmapFactory.decodeStream(dis, null, decodeOptions); + return new ThemedBitmapInfo(icon, color, themeData, normalizationScale, + userBadgeBitmap); + } catch (IOException e) { + return null; + } + } + } + + public static class ThemeData { + + final Resources mResources; + final int mResID; + + public ThemeData(Resources resources, int resID) { + mResources = resources; + mResID = resID; + } + + Drawable loadMonochromeDrawable(int accentColor) { + Drawable d = mResources.getDrawable(mResID).mutate(); + d.setTint(accentColor); + d = new InsetDrawable(d, .2f); + return d; + } + + public Drawable wrapDrawable(Drawable original, int iconType) { + if (!(original instanceof AdaptiveIconDrawable)) { + return original; + } + AdaptiveIconDrawable aid = (AdaptiveIconDrawable) original; + String resourceType = mResources.getResourceTypeName(mResID); + if (iconType == ICON_TYPE_CALENDAR && "array".equals(resourceType)) { + TypedArray ta = mResources.obtainTypedArray(mResID); + int id = ta.getResourceId(IconProvider.getDay(), ID_NULL); + ta.recycle(); + return id == ID_NULL ? original + : new ThemedAdaptiveIcon(aid, new ThemeData(mResources, id)); + } else if (iconType == ICON_TYPE_CLOCK && "array".equals(resourceType)) { + ((ClockDrawableWrapper) original).mThemeData = this; + return original; + } else if ("drawable".equals(resourceType)) { + return new ThemedAdaptiveIcon(aid, this); + } else { + return original; + } + } + } + + static class ThemedAdaptiveIcon extends AdaptiveIconDrawable implements Extender { + + protected final ThemeData mThemeData; + + public ThemedAdaptiveIcon(AdaptiveIconDrawable parent, ThemeData themeData) { + super(parent.getBackground(), parent.getForeground()); + mThemeData = themeData; + } + + @Override + public BitmapInfo getExtendedInfo(Bitmap bitmap, int color, BaseIconFactory iconFactory, + float normalizationScale, UserHandle user) { + Bitmap userBadge = Process.myUserHandle().equals(user) + ? null : iconFactory.getUserBadgeBitmap(user); + return new ThemedBitmapInfo(bitmap, color, mThemeData, normalizationScale, userBadge); + } + + @Override + public void drawForPersistence(Canvas canvas) { + draw(canvas); + } + + @Override + public Drawable getThemedDrawable(Context context) { + int[] colors = getColors(context); + Drawable bg = new ColorDrawable(colors[0]); + float inset = getExtraInsetFraction() / (1 + 2 * getExtraInsetFraction()); + Drawable fg = new InsetDrawable(mThemeData.loadMonochromeDrawable(colors[1]), inset); + return new AdaptiveIconDrawable(bg, fg); + } } /** @@ -134,9 +293,4 @@ public class ThemedIconDrawable extends FastBitmapDrawable { } return colors; } - - @Override - public int getIconColor() { - return colorFg; - } } diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java b/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java index 057bdc2..d685737 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java +++ b/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java @@ -15,15 +15,10 @@ */ package com.android.launcher3.icons.cache; -import static android.graphics.BitmapFactory.decodeByteArray; - import static com.android.launcher3.icons.BaseIconFactory.getFullResDefaultActivityIcon; import static com.android.launcher3.icons.BitmapInfo.LOW_RES_ICON; -import static com.android.launcher3.icons.GraphicsUtils.flattenBitmap; import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound; -import static java.util.Objects.requireNonNull; - import android.content.ComponentName; import android.content.ContentValues; import android.content.Context; @@ -37,34 +32,26 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; import android.graphics.Bitmap; -import android.graphics.Bitmap.Config; -import android.graphics.BitmapFactory; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Handler; import android.os.LocaleList; import android.os.Looper; import android.os.Process; -import android.os.Trace; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; -import android.util.SparseArray; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.launcher3.icons.BaseIconFactory; -import com.android.launcher3.icons.BaseIconFactory.IconOptions; import com.android.launcher3.icons.BitmapInfo; import com.android.launcher3.util.ComponentKey; -import com.android.launcher3.util.FlagOp; import com.android.launcher3.util.SQLiteCacheHelper; -import java.nio.ByteBuffer; import java.util.AbstractMap; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -90,6 +77,8 @@ public abstract class BaseIconCache { public CharSequence contentDescription = ""; } + private final HashMap<UserHandle, BitmapInfo> mDefaultIcons = new HashMap<>(); + protected final Context mContext; protected final PackageManager mPackageManager; @@ -101,9 +90,6 @@ public abstract class BaseIconCache { protected LocaleList mLocaleList = LocaleList.getEmptyLocaleList(); protected String mSystemState = ""; - private BitmapInfo mDefaultIcon; - private final SparseArray<FlagOp> mUserFlagOpMap = new SparseArray<>(); - private final String mDbFileName; private final Looper mBgLooper; @@ -159,8 +145,7 @@ public abstract class BaseIconCache { private synchronized void updateIconParamsBg(int iconDpi, int iconPixelSize) { mIconDpi = iconDpi; - mDefaultIcon = null; - mUserFlagOpMap.clear(); + mDefaultIcons.clear(); mIconDb.clear(); mIconDb.close(); mIconDb = new IconDB(mContext, mDbFileName, iconPixelSize); @@ -191,6 +176,12 @@ public abstract class BaseIconCache { return getFullResDefaultActivityIcon(mIconDpi); } + private BitmapInfo makeDefaultIcon(UserHandle user) { + try (BaseIconFactory li = getIconFactory()) { + return li.makeDefaultIcon(user); + } + } + /** * Remove any records for the supplied ComponentName. */ @@ -273,13 +264,7 @@ public abstract class BaseIconCache { // (e.g. fallback icon, default icon). So we drop here since there's no point in caching // an empty entry. if (entry.bitmap.isNullOrLowRes()) return; - - CharSequence entryTitle = cachingLogic.getLabel(object); - if (entryTitle == null) { - Log.d(TAG, "No label returned from caching logic instance: " + cachingLogic); - } - entry.title = entryTitle; - + entry.title = cachingLogic.getLabel(object); entry.contentDescription = mPackageManager.getUserBadgedLabel(entry.title, user); if (cachingLogic.addToMemCache()) mCache.put(key, entry); @@ -303,26 +288,10 @@ public abstract class BaseIconCache { } public synchronized BitmapInfo getDefaultIcon(UserHandle user) { - if (mDefaultIcon == null) { - try (BaseIconFactory li = getIconFactory()) { - mDefaultIcon = li.makeDefaultIcon(); - } - } - return mDefaultIcon.withFlags(getUserFlagOpLocked(user)); - } - - protected FlagOp getUserFlagOpLocked(UserHandle user) { - int key = user.hashCode(); - int index; - if ((index = mUserFlagOpMap.indexOfKey(key)) >= 0) { - return mUserFlagOpMap.valueAt(index); - } else { - try (BaseIconFactory li = getIconFactory()) { - FlagOp op = li.getBitmapFlagOp(new IconOptions().setUser(user)); - mUserFlagOpMap.put(key, op); - return op; - } + if (!mDefaultIcons.containsKey(user)) { + mDefaultIcons.put(user, makeDefaultIcon(user)); } + return mDefaultIcons.get(user); } public boolean isDefaultIcon(BitmapInfo icon, UserHandle user) { @@ -337,20 +306,6 @@ public abstract class BaseIconCache { @NonNull ComponentName componentName, @NonNull UserHandle user, @NonNull Supplier<T> infoProvider, @NonNull CachingLogic<T> cachingLogic, boolean usePackageIcon, boolean useLowResIcon) { - return cacheLocked( - componentName, - user, - infoProvider, - cachingLogic, - null, - usePackageIcon, - useLowResIcon); - } - - protected <T> CacheEntry cacheLocked( - @NonNull ComponentName componentName, @NonNull UserHandle user, - @NonNull Supplier<T> infoProvider, @NonNull CachingLogic<T> cachingLogic, - @Nullable Cursor cursor, boolean usePackageIcon, boolean useLowResIcon) { assertWorkerThread(); ComponentKey cacheKey = new ComponentKey(componentName, user); CacheEntry entry = mCache.get(cacheKey); @@ -363,21 +318,31 @@ public abstract class BaseIconCache { // Check the DB first. T object = null; boolean providerFetchedOnce = false; - boolean cacheEntryUpdated = cursor == null - ? getEntryFromDBLocked(cacheKey, entry, useLowResIcon) - : updateTitleAndIconLocked(cacheKey, entry, cursor, useLowResIcon); - if (!cacheEntryUpdated) { + + if (!getEntryFromDB(cacheKey, entry, useLowResIcon)) { object = infoProvider.get(); providerFetchedOnce = true; - loadFallbackIcon( - object, - entry, - cachingLogic, - usePackageIcon, - /* usePackageTitle= */ true, - componentName, - user); + if (object != null) { + entry.bitmap = cachingLogic.loadIcon(mContext, object); + } else { + if (usePackageIcon) { + CacheEntry packageEntry = getEntryForPackageLocked( + componentName.getPackageName(), user, false); + if (packageEntry != null) { + if (DEBUG) Log.d(TAG, "using package default icon for " + + componentName.toShortString()); + entry.bitmap = packageEntry.bitmap; + entry.title = packageEntry.title; + entry.contentDescription = packageEntry.contentDescription; + } + } + if (entry.bitmap == null) { + if (DEBUG) Log.d(TAG, "using default icon for " + + componentName.toShortString()); + entry.bitmap = getDefaultIcon(user); + } + } } if (TextUtils.isEmpty(entry.title)) { @@ -386,56 +351,15 @@ public abstract class BaseIconCache { providerFetchedOnce = true; } if (object != null) { - loadFallbackTitle(object, entry, cachingLogic, user); + entry.title = cachingLogic.getLabel(object); + entry.contentDescription = mPackageManager.getUserBadgedLabel( + cachingLogic.getDescription(object, entry.title), user); } } } return entry; } - /** - * Fallback method for loading an icon bitmap. - */ - protected <T> void loadFallbackIcon( - T object, CacheEntry entry, @NonNull CachingLogic<T> cachingLogic, - boolean usePackageIcon, boolean usePackageTitle, @NonNull ComponentName componentName, - @NonNull UserHandle user) { - if (object != null) { - entry.bitmap = cachingLogic.loadIcon(mContext, object); - } else { - if (usePackageIcon) { - CacheEntry packageEntry = getEntryForPackageLocked( - componentName.getPackageName(), user, false); - if (packageEntry != null) { - if (DEBUG) Log.d(TAG, "using package default icon for " + - componentName.toShortString()); - entry.bitmap = packageEntry.bitmap; - entry.contentDescription = packageEntry.contentDescription; - - if (usePackageTitle) { - entry.title = packageEntry.title; - } - } - } - if (entry.bitmap == null) { - if (DEBUG) Log.d(TAG, "using default icon for " + - componentName.toShortString()); - entry.bitmap = getDefaultIcon(user); - } - } - } - - /** - * Fallback method for loading an app title. - */ - protected <T> void loadFallbackTitle( - T object, CacheEntry entry, @NonNull CachingLogic<T> cachingLogic, - @NonNull UserHandle user) { - entry.title = cachingLogic.getLabel(object); - entry.contentDescription = mPackageManager.getUserBadgedLabel( - cachingLogic.getDescription(object, entry.title), user); - } - public synchronized void clear() { assertWorkerThread(); mIconDb.clear(); @@ -461,7 +385,7 @@ public abstract class BaseIconCache { } if (icon != null) { BaseIconFactory li = getIconFactory(); - entry.bitmap = li.createShapedIconBitmap(icon, new IconOptions().setUser(user)); + entry.bitmap = li.createShapedIconBitmap(icon, user); li.close(); } if (!TextUtils.isEmpty(title) && entry.bitmap.icon != null) { @@ -489,7 +413,7 @@ public abstract class BaseIconCache { boolean entryUpdated = true; // Check the DB first. - if (!getEntryFromDBLocked(cacheKey, entry, useLowResIcon)) { + if (!getEntryFromDB(cacheKey, entry, useLowResIcon)) { try { int flags = Process.myUserHandle().equals(user) ? 0 : PackageManager.GET_UNINSTALLED_PACKAGES; @@ -503,8 +427,8 @@ public abstract class BaseIconCache { // Load the full res icon for the application, but if useLowResIcon is set, then // only keep the low resolution icon instead of the larger full-sized icon BitmapInfo iconInfo = li.createBadgedIconBitmap( - appInfo.loadIcon(mPackageManager), - new IconOptions().setUser(user).setInstantApp(isInstantApp(appInfo))); + appInfo.loadIcon(mPackageManager), user, appInfo.targetSdkVersion, + isInstantApp(appInfo)); li.close(); entry.title = appInfo.loadLabel(mPackageManager); @@ -533,10 +457,8 @@ public abstract class BaseIconCache { return entry; } - protected boolean getEntryFromDBLocked( - ComponentKey cacheKey, CacheEntry entry, boolean lowRes) { + protected boolean getEntryFromDB(ComponentKey cacheKey, CacheEntry entry, boolean lowRes) { Cursor c = null; - Trace.beginSection("loadIconIndividually"); try { c = mIconDb.query( lowRes ? IconDB.COLUMNS_LOW_RES : IconDB.COLUMNS_HIGH_RES, @@ -545,7 +467,26 @@ public abstract class BaseIconCache { cacheKey.componentName.flattenToString(), Long.toString(getSerialNumberForUser(cacheKey.user))}); if (c.moveToNext()) { - return updateTitleAndIconLocked(cacheKey, entry, c, lowRes); + // Set the alpha to be 255, so that we never have a wrong color + entry.bitmap = BitmapInfo.of(LOW_RES_ICON, setColorAlphaBound(c.getInt(0), 255)); + entry.title = c.getString(1); + if (entry.title == null) { + entry.title = ""; + entry.contentDescription = ""; + } else { + entry.contentDescription = mPackageManager.getUserBadgedLabel( + entry.title, cacheKey.user); + } + + if (!lowRes) { + try { + entry.bitmap = BitmapInfo.fromByteArray( + c.getBlob(2), entry.bitmap.color, cacheKey.user, this, mContext); + } catch (Exception e) { + return false; + } + } + return entry.bitmap != null; } } catch (SQLiteException e) { Log.d(TAG, "Error reading icon cache", e); @@ -553,62 +494,10 @@ public abstract class BaseIconCache { if (c != null) { c.close(); } - Trace.endSection(); } return false; } - private boolean updateTitleAndIconLocked( - ComponentKey cacheKey, CacheEntry entry, Cursor c, boolean lowRes) { - // Set the alpha to be 255, so that we never have a wrong color - entry.bitmap = BitmapInfo.of(LOW_RES_ICON, - setColorAlphaBound(c.getInt(IconDB.INDEX_COLOR), 255)); - entry.title = c.getString(IconDB.INDEX_TITLE); - if (entry.title == null) { - entry.title = ""; - entry.contentDescription = ""; - } else { - entry.contentDescription = mPackageManager.getUserBadgedLabel( - entry.title, cacheKey.user); - } - - if (!lowRes) { - byte[] data = c.getBlob(IconDB.INDEX_ICON); - if (data == null) { - return false; - } - try { - BitmapFactory.Options decodeOptions = new BitmapFactory.Options(); - decodeOptions.inPreferredConfig = Config.HARDWARE; - entry.bitmap = BitmapInfo.of( - requireNonNull(decodeByteArray(data, 0, data.length, decodeOptions)), - entry.bitmap.color); - } catch (Exception e) { - return false; - } - - // Decode mono bitmap - data = c.getBlob(IconDB.INDEX_MONO_ICON); - Bitmap icon = entry.bitmap.icon; - if (data != null && data.length == icon.getHeight() * icon.getWidth()) { - Bitmap monoBitmap = Bitmap.createBitmap( - icon.getWidth(), icon.getHeight(), Config.ALPHA_8); - monoBitmap.copyPixelsFromBuffer(ByteBuffer.wrap(data)); - Bitmap hwMonoBitmap = monoBitmap.copy(Config.HARDWARE, false /*isMutable*/); - if (hwMonoBitmap != null) { - monoBitmap.recycle(); - monoBitmap = hwMonoBitmap; - } - try (BaseIconFactory factory = getIconFactory()) { - entry.bitmap.setMonoIcon(monoBitmap, factory); - } - } - } - entry.bitmap.flags = c.getInt(IconDB.INDEX_FLAGS); - entry.bitmap = entry.bitmap.withFlags(getUserFlagOpLocked(cacheKey.user)); - return entry.bitmap != null; - } - /** * Returns a cursor for an arbitrary query to the cache db */ @@ -621,7 +510,7 @@ public abstract class BaseIconCache { * Cache class to store the actual entries on disk */ public static final class IconDB extends SQLiteCacheHelper { - private static final int RELEASE_VERSION = 34; + private static final int RELEASE_VERSION = 31; public static final String TABLE_NAME = "icons"; public static final String COLUMN_ROWID = "rowid"; @@ -631,29 +520,14 @@ public abstract class BaseIconCache { public static final String COLUMN_VERSION = "version"; public static final String COLUMN_ICON = "icon"; public static final String COLUMN_ICON_COLOR = "icon_color"; - public static final String COLUMN_MONO_ICON = "mono_icon"; - public static final String COLUMN_FLAGS = "flags"; public static final String COLUMN_LABEL = "label"; public static final String COLUMN_SYSTEM_STATE = "system_state"; public static final String COLUMN_KEYWORDS = "keywords"; + public static final String[] COLUMNS_HIGH_RES = new String[] { + IconDB.COLUMN_ICON_COLOR, IconDB.COLUMN_LABEL, IconDB.COLUMN_ICON }; public static final String[] COLUMNS_LOW_RES = new String[] { - COLUMN_COMPONENT, - COLUMN_LABEL, - COLUMN_ICON_COLOR, - COLUMN_FLAGS}; - public static final String[] COLUMNS_HIGH_RES = Arrays.copyOf(COLUMNS_LOW_RES, - COLUMNS_LOW_RES.length + 2, String[].class); - static { - COLUMNS_HIGH_RES[COLUMNS_LOW_RES.length] = COLUMN_ICON; - COLUMNS_HIGH_RES[COLUMNS_LOW_RES.length + 1] = COLUMN_MONO_ICON; - } - private static final int INDEX_TITLE = Arrays.asList(COLUMNS_LOW_RES).indexOf(COLUMN_LABEL); - private static final int INDEX_COLOR = Arrays.asList(COLUMNS_LOW_RES) - .indexOf(COLUMN_ICON_COLOR); - private static final int INDEX_FLAGS = Arrays.asList(COLUMNS_LOW_RES).indexOf(COLUMN_FLAGS); - private static final int INDEX_ICON = COLUMNS_LOW_RES.length; - private static final int INDEX_MONO_ICON = INDEX_ICON + 1; + IconDB.COLUMN_ICON_COLOR, IconDB.COLUMN_LABEL }; public IconDB(Context context, String dbFileName, int iconPixelSize) { super(context, dbFileName, (RELEASE_VERSION << 16) + iconPixelSize, TABLE_NAME); @@ -667,9 +541,7 @@ public abstract class BaseIconCache { + COLUMN_LAST_UPDATED + " INTEGER NOT NULL DEFAULT 0, " + COLUMN_VERSION + " INTEGER NOT NULL DEFAULT 0, " + COLUMN_ICON + " BLOB, " - + COLUMN_MONO_ICON + " BLOB, " + COLUMN_ICON_COLOR + " INTEGER NOT NULL DEFAULT 0, " - + COLUMN_FLAGS + " INTEGER NOT NULL DEFAULT 0, " + COLUMN_LABEL + " TEXT, " + COLUMN_SYSTEM_STATE + " TEXT, " + COLUMN_KEYWORDS + " TEXT, " @@ -681,26 +553,8 @@ public abstract class BaseIconCache { private ContentValues newContentValues(BitmapInfo bitmapInfo, String label, String packageName, @Nullable String keywords) { ContentValues values = new ContentValues(); - if (bitmapInfo.canPersist()) { - values.put(IconDB.COLUMN_ICON, flattenBitmap(bitmapInfo.icon)); - - // Persist mono bitmap as alpha channel - Bitmap mono = bitmapInfo.getMono(); - if (mono != null && mono.getHeight() == bitmapInfo.icon.getHeight() - && mono.getWidth() == bitmapInfo.icon.getWidth() - && mono.getConfig() == Config.ALPHA_8) { - byte[] pixels = new byte[mono.getWidth() * mono.getHeight()]; - mono.copyPixelsToBuffer(ByteBuffer.wrap(pixels)); - values.put(IconDB.COLUMN_MONO_ICON, pixels); - } else { - values.put(IconDB.COLUMN_MONO_ICON, (byte[]) null); - } - } else { - values.put(IconDB.COLUMN_ICON, (byte[]) null); - values.put(IconDB.COLUMN_MONO_ICON, (byte[]) null); - } + values.put(IconDB.COLUMN_ICON, bitmapInfo.toByteArray()); values.put(IconDB.COLUMN_ICON_COLOR, bitmapInfo.color); - values.put(IconDB.COLUMN_FLAGS, bitmapInfo.flags); values.put(IconDB.COLUMN_LABEL, label); values.put(IconDB.COLUMN_SYSTEM_STATE, getIconSystemState(packageName)); diff --git a/iconloaderlib/src/com/android/launcher3/util/FlagOp.java b/iconloaderlib/src/com/android/launcher3/util/FlagOp.java deleted file mode 100644 index 65117ca..0000000 --- a/iconloaderlib/src/com/android/launcher3/util/FlagOp.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.launcher3.util; - -/** - * Utility interface for representing flag operations - */ -public interface FlagOp { - - FlagOp NO_OP = i -> i; - - int apply(int flags); - - /** - * Returns a new OP which adds the provided flag after applying all previous operations - */ - default FlagOp addFlag(int flag) { - return i -> apply(i) | flag; - } - - /** - * Returns a new OP which removes the provided flag after applying all previous operations - */ - default FlagOp removeFlag(int flag) { - return i -> apply(i) & ~flag; - } - - /** - * Returns a new OP which adds or removed the provided flag based on {@code enable} after - * applying all previous operations - */ - default FlagOp setFlag(int flag, boolean enable) { - return enable ? addFlag(flag) : removeFlag(flag); - } -} diff --git a/searchuilib/build.gradle b/searchuilib/build.gradle index 3bf65fe..4f372e3 100644 --- a/searchuilib/build.gradle +++ b/searchuilib/build.gradle @@ -7,6 +7,8 @@ android { defaultConfig { minSdkVersion 25 targetSdkVersion 28 + versionCode 1 + versionName "1.0" } sourceSets { diff --git a/searchuilib/src/com/android/app/search/LayoutType.java b/searchuilib/src/com/android/app/search/LayoutType.java index d7c28ab..cf27e2b 100644 --- a/searchuilib/src/com/android/app/search/LayoutType.java +++ b/searchuilib/src/com/android/app/search/LayoutType.java @@ -43,9 +43,7 @@ public class LayoutType { // will replace ICON_DOUBLE_* ICON_SINGLE_* layouts public static final String ICON_HORIZONTAL_TEXT = "icon_row"; public static final String HORIZONTAL_MEDIUM_TEXT = "icon_row_medium"; - public static final String EXTRA_TALL_ICON_ROW = "extra_tall_icon_row"; public static final String SMALL_ICON_HORIZONTAL_TEXT = "short_icon_row"; - public static final String SMALL_ICON_HORIZONTAL_TEXT_THUMBNAIL = "short_icon_row_thumbnail"; // This layout creates square thumbnail image (currently 3 column) public static final String THUMBNAIL = "thumbnail"; @@ -73,7 +71,4 @@ public class LayoutType { // layout representing quick calculations public static final String CALCULATOR = "calculator"; - - // layout for the section header - public static final String SECTION_HEADER = "section_header"; } diff --git a/searchuilib/src/com/android/app/search/ResultType.java b/searchuilib/src/com/android/app/search/ResultType.java deleted file mode 100644 index 1e5ea12..0000000 --- a/searchuilib/src/com/android/app/search/ResultType.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.app.search; - -/** - * Constants to be used with {@link android.app.search.SearchContext} and - * {@link android.app.search.SearchTarget}. - * - * Note, a result type could be a of two types. - * For example, unpublished settings result type could be in slices: - * <code> resultType = SETTING | SLICE </code> - */ -public class ResultType { - - // published corpus by 3rd party app, supported by SystemService - public static final int APPLICATION = 1 << 0; - public static final int SHORTCUT = 1 << 1; - public static final int SLICE = 1 << 6; - public static final int WIDGETS = 1 << 7; - - // Not extracted from any of the SystemService - public static final int PEOPLE = 1 << 2; - public static final int ACTION = 1 << 3; - public static final int SETTING = 1 << 4; - public static final int SCREENSHOT = 1 << 5; - public static final int PLAY = 1 << 8; - public static final int SUGGEST = 1 << 9; - public static final int ASSISTANT = 1 << 10; - public static final int CHROMETAB = 1 << 11; - public static final int NAVVYSITE = 1 << 12; - public static final int TIPS = 1 << 13; - public static final int PEOPLE_TILE = 1 << 14; - public static final int LEGACY_SHORTCUT = 1 << 15; - public static final int MEMORY = 1 << 16; - public static final int WEB_SUGGEST = 1 << 17; -} |