Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e32dcb7a authored by Sunny Goyal's avatar Sunny Goyal
Browse files

Updating theme and clock icons to work with full bleed shapes

Bug: 421883017
Bug: 399666950
Bug: 415198088

Flag: com.android.launcher3.enable_launcher_icon_shapes
Test: Launcher screenshot tests
Change-Id: If804b446e81527e5c8bc70ee1940e8d63764bfcd
parent fa4ef437
Loading
Loading
Loading
Loading
+21 −35
Original line number Diff line number Diff line
@@ -8,9 +8,9 @@ import static android.graphics.drawable.AdaptiveIconDrawable.getExtraInsetFracti

import static com.android.launcher3.icons.BitmapInfo.FLAG_FULL_BLEED;
import static com.android.launcher3.icons.BitmapInfo.FLAG_INSTANT;
import static com.android.launcher3.icons.GraphicsUtils.generateIconShape;
import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
import static com.android.launcher3.icons.ShadowGenerator.BLUR_FACTOR;
import static com.android.launcher3.icons.ShadowGenerator.ICON_SCALE_FOR_SHADOWS;

import static java.lang.annotation.RetentionPolicy.SOURCE;

@@ -96,11 +96,8 @@ public class BaseIconFactory implements AutoCloseable {
    @Nullable
    private ShadowGenerator mShadowGenerator;

    /** Shadow bitmap used as background for theme icons */
    private Bitmap mWhiteShadowLayer;
    /** Default IconShape for when custom shape is not needed */
    private IconShape mDefaultIconShape;

    /** Bitmap used for {@link BitmapShader} to mask Adaptive Icons when drawing */
    private Bitmap mShaderBitmap;

@@ -235,11 +232,19 @@ public class BaseIconFactory implements AutoCloseable {
                ? options.mExtractedColor : ColorExtractor.findDominantColorByHue(bitmap);

        BitmapInfo info = BitmapInfo.of(bitmap, color, getDefaultIconShape());
        if (mDrawFullBleedIcons) info.flags |= FLAG_FULL_BLEED;

        FlagOp flagOp = getBitmapFlagOp(options);
        if (adaptiveIcon instanceof WrappedAdaptiveIcon) {
            flagOp = flagOp.addFlag(BitmapInfo.FLAG_WRAPPED_NON_ADAPTIVE);
        }
        if (mDrawFullBleedIcons) flagOp = flagOp.addFlag(FLAG_FULL_BLEED);
        info = info.withFlags(flagOp);

        if (adaptiveIcon instanceof Extender extender) {
            info = extender.getExtendedInfo(bitmap, color, this, scale[0]);
        } else if (IconProvider.ATLEAST_T && mThemeController != null && adaptiveIcon != null) {
            info = extender.getUpdatedBitmapInfo(info, this);
        }

        if (IconProvider.ATLEAST_T && mThemeController != null && adaptiveIcon != null) {
            info.setThemedBitmap(
                    mThemeController.createThemedBitmap(
                        adaptiveIcon,
@@ -249,11 +254,7 @@ public class BaseIconFactory implements AutoCloseable {
                    )
            );
        }
        FlagOp flagOp = getBitmapFlagOp(options);
        if (adaptiveIcon instanceof WrappedAdaptiveIcon) {
            flagOp = flagOp.addFlag(BitmapInfo.FLAG_WRAPPED_NON_ADAPTIVE);
        }
        info = info.withFlags(flagOp);

        return info;
    }

@@ -266,8 +267,7 @@ public class BaseIconFactory implements AutoCloseable {
        AdaptiveIconDrawable tempAdaptiveIcon =
                new AdaptiveIconDrawable(new ColorDrawable(BLACK), null);
        tempAdaptiveIcon.setBounds(0, 0, mIconBitmapSize, mIconBitmapSize);
        mDefaultIconShape = new IconShape(mIconBitmapSize, tempAdaptiveIcon.getIconMask(),
                getWhiteShadowLayer());
        mDefaultIconShape = generateIconShape(mIconBitmapSize, tempAdaptiveIcon.getIconMask());
        return mDefaultIconShape;
    }

@@ -313,19 +313,9 @@ public class BaseIconFactory implements AutoCloseable {
        return drawable.getIconMask();
    }

    @NonNull
    public Bitmap getWhiteShadowLayer() {
        if (mWhiteShadowLayer == null) {
            mWhiteShadowLayer = createScaledBitmap(
                    new AdaptiveIconDrawable(new ColorDrawable(Color.WHITE), null),
                    MODE_HARDWARE_WITH_SHADOW);
        }
        return mWhiteShadowLayer;
    }

    /**
     * Takes an {@link AdaptiveIconDrawable} and uses it to create a new Shader Bitmap.
     * {@link mShaderBitmap} will be used to create a {@link BitmapShader} for masking,
     * {@link #mShaderBitmap} will be used to create a {@link BitmapShader} for masking,
     * such as for icon shapes. Will reuse underlying Bitmap where possible.
     *
     * @param adaptiveIcon AdaptiveIconDrawable to draw with shader
@@ -361,9 +351,8 @@ public class BaseIconFactory implements AutoCloseable {

    @NonNull
    public Bitmap createScaledBitmap(@NonNull Drawable icon, @BitmapGenerationMode int mode) {
        float[] scale = new float[1];
        icon = normalizeAndWrapToAdaptiveIcon(icon, scale);
        return createIconBitmap(icon, Math.min(scale[0], ICON_SCALE_FOR_SHADOWS), mode, false);
        icon = normalizeAndWrapToAdaptiveIcon(icon, new float[1]);
        return createIconBitmap(icon, ICON_VISIBLE_AREA_FACTOR, mode, false);
    }

    /**
@@ -477,14 +466,11 @@ public class BaseIconFactory implements AutoCloseable {
                    && !isFullBleedEnabled) {
                getShadowGenerator().addPathShadow(aid.getIconMask(), canvas);
            }

            if (icon instanceof Extender) {
                ((Extender) icon).drawForPersistence(canvas);
            } else {
                drawAdaptiveIcon(canvas, aid, isFullBleedEnabled,
                        getShapePath(aid, icon.getBounds()));
            if (icon instanceof Extender extender) {
                extender.drawForPersistence();
            }

            drawAdaptiveIcon(canvas, aid, isFullBleedEnabled, getShapePath(aid, icon.getBounds()));
            canvas.restoreToCount(count);
        } else {
            if (icon instanceof BitmapDrawable) {
@@ -533,7 +519,7 @@ public class BaseIconFactory implements AutoCloseable {

    /**
     * Draws AdaptiveIconDrawable onto canvas using provided Path
     * and {@link mShaderBitmap} as a shader.
     * and {@link #mShaderBitmap} as a shader.
     *
     * @param canvas    canvas to draw on
     * @param drawable  AdaptiveIconDrawable to draw
+22 −31
Original line number Diff line number Diff line
@@ -17,21 +17,23 @@ package com.android.launcher3.icons

import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.drawable.Drawable
import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
import androidx.annotation.IntDef
import com.android.launcher3.icons.FastBitmapDrawableDelegate.DelegateFactory
import com.android.launcher3.icons.FastBitmapDrawableDelegate.SimpleDelegateFactory
import com.android.launcher3.icons.PlaceHolderDrawableDelegate.PlaceHolderDelegateFactory
import com.android.launcher3.icons.cache.CacheLookupFlag
import com.android.launcher3.util.FlagOp

open class BitmapInfo(
class BitmapInfo(
    @JvmField val icon: Bitmap,
    @JvmField val color: Int,
    @JvmField val defaultIconShape: IconShape = IconShape.EMPTY,
    @BitmapInfoFlags @JvmField var flags: Int = 0,
    var themedBitmap: ThemedBitmap? = null,
    var delegateFactory: DelegateFactory = SimpleDelegateFactory,
) {
    @IntDef(
        flag = true,
@@ -66,14 +68,15 @@ open class BitmapInfo(
    }

    @Override
    open fun clone(): BitmapInfo {
    fun clone(): BitmapInfo {
        return copyInternalsTo(BitmapInfo(icon, color, defaultIconShape))
    }

    protected fun copyInternalsTo(target: BitmapInfo): BitmapInfo {
    private fun copyInternalsTo(target: BitmapInfo): BitmapInfo {
        target.themedBitmap = themedBitmap
        target.flags = flags
        target.badgeInfo = badgeInfo
        target.delegateFactory = delegateFactory
        return target
    }

@@ -84,24 +87,15 @@ open class BitmapInfo(
    val isLowRes: Boolean
        get() = matchingLookupFlag.useLowRes()

    open val matchingLookupFlag: CacheLookupFlag
    val matchingLookupFlag: CacheLookupFlag
        /** Returns the lookup flag to match this current state of this info */
        get() =
            CacheLookupFlag.DEFAULT_LOOKUP_FLAG.withUseLowRes(LOW_RES_ICON == icon)
                .withThemeIcon(themedBitmap != null)

    /** BitmapInfo can be stored on disk or other persistent storage */
    open fun canPersist(): Boolean {
        return !isNullOrLowRes
    }

    /** Creates a drawable for the provided BitmapInfo */
    @JvmOverloads
    fun newIcon(
        context: Context,
        @DrawableCreationFlags creationFlags: Int = 0,
    ): FastBitmapDrawable {
        return newIcon(context, creationFlags, null)
    fun canPersist(): Boolean {
        return !isNullOrLowRes && delegateFactory == SimpleDelegateFactory
    }

    /**
@@ -112,10 +106,11 @@ open class BitmapInfo(
     * @param iconShape information for custom Icon Shapes, to use with Full-bleed icons.
     * @return FastBitmapDrawable
     */
    open fun newIcon(
    @JvmOverloads
    fun newIcon(
        context: Context,
        @DrawableCreationFlags creationFlags: Int,
        iconShape: IconShape?,
        @DrawableCreationFlags creationFlags: Int = 0,
        iconShape: IconShape? = null,
    ): FastBitmapDrawable {
        val drawable: FastBitmapDrawable =
            if (isLowRes) {
@@ -129,15 +124,15 @@ open class BitmapInfo(
                    themedBitmap != null &&
                    themedBitmap !== ThemedBitmap.NOT_SUPPORTED
            ) {
                themedBitmap!!.newDrawable(this, context)
                themedBitmap!!.newDrawable(this, context, iconShape ?: defaultIconShape)
            } else {
                FastBitmapDrawable(this, iconShape ?: defaultIconShape)
                FastBitmapDrawable(this, iconShape ?: defaultIconShape, delegateFactory)
            }
        applyFlags(context, drawable, creationFlags)
        return drawable
    }

    protected fun applyFlags(
    private fun applyFlags(
        context: Context,
        drawable: FastBitmapDrawable,
        @DrawableCreationFlags creationFlags: Int,
@@ -219,18 +214,14 @@ open class BitmapInfo(
        }
    }

    /** Interface to be implemented by drawables to provide a custom BitmapInfo */
    /** Interface to be implemented by drawables to customize a BitmapInfo */
    interface Extender {
        /** Called for creating a custom BitmapInfo */
        fun getExtendedInfo(
            bitmap: Bitmap,
            color: Int,
            iconFactory: BaseIconFactory,
            normalizationScale: Float,
        ): BitmapInfo

        /** Returns an update [BitmapInfo] replacing the existing [info] */
        fun getUpdatedBitmapInfo(info: BitmapInfo, factory: BaseIconFactory): BitmapInfo

        /** Called to draw the UI independent of any runtime configurations like time or theme */
        fun drawForPersistence(canvas: Canvas)
        fun drawForPersistence()
    }

    /**
+58 −180
Original line number Diff line number Diff line
@@ -15,34 +15,28 @@
 */
package com.android.launcher3.icons

import android.annotation.TargetApi
import android.content.Context
import android.content.pm.PackageManager.GET_META_DATA
import android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES
import android.graphics.Bitmap
import android.graphics.BlendMode.SRC_IN
import android.graphics.BlendModeColorFilter
import android.graphics.BitmapShader
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.ColorFilter
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.Shader
import android.graphics.Shader.TileMode.CLAMP
import android.graphics.drawable.AdaptiveIconDrawable
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.LayerDrawable
import android.os.Build.VERSION_CODES
import android.os.SystemClock
import android.util.Log
import com.android.launcher3.icons.BitmapInfo.Extender
import com.android.launcher3.icons.FastBitmapDrawable.Companion.getDisabledColorFilter
import com.android.launcher3.icons.FastBitmapDrawableDelegate.DelegateFactory
import com.android.launcher3.icons.GraphicsUtils.transformed
import com.android.launcher3.icons.cache.CacheLookupFlag
import com.android.launcher3.icons.mono.ThemedIconDelegate.Companion.getColors
import com.android.launcher3.icons.GraphicsUtils.getColorMultipliedFilter
import com.android.launcher3.icons.GraphicsUtils.resizeToContentSize
import java.util.Calendar
import java.util.concurrent.TimeUnit.MINUTES
import kotlin.math.max

/**
 * Wrapper over [AdaptiveIconDrawable] to intercept icon flattening logic for dynamic clock icons
@@ -50,59 +44,42 @@ import kotlin.math.max
class ClockDrawableWrapper
private constructor(base: AdaptiveIconDrawable, private val animationInfo: ClockAnimationInfo) :
    AdaptiveIconDrawable(base.background, base.foreground), Extender {
    private var mThemeInfo: ClockAnimationInfo? = null

    override fun getMonochrome(): Drawable? {
        val info = mThemeInfo ?: return null
        val d = info.baseDrawableState.newDrawable().mutate()
        if (d is AdaptiveIconDrawable) {
            val mono = d.foreground
            info.applyTime(Calendar.getInstance(), mono as LayerDrawable)
            return mono
        }
        return null
        val monoLayer =
            (animationInfo.baseDrawableState.newDrawable().mutate() as? AdaptiveIconDrawable)
                ?.monochrome
        if (monoLayer is LayerDrawable) animationInfo.applyTime(Calendar.getInstance(), monoLayer)
        return monoLayer
    }

    override fun getExtendedInfo(
        bitmap: Bitmap,
        color: Int,
        iconFactory: BaseIconFactory,
        normalizationScale: Float,
    ): ClockBitmapInfo {
    override fun getUpdatedBitmapInfo(info: BitmapInfo, factory: BaseIconFactory): BitmapInfo {
        val bitmapSize = factory.iconBitmapSize
        val flattenBG =
            iconFactory.createScaledBitmap(
                AdaptiveIconDrawable(background.constantState!!.newDrawable(), null),
                BaseIconFactory.MODE_HARDWARE_WITH_SHADOW,
            )

        // Only pass theme info if mono-icon is enabled
        val themeInfo = if (iconFactory.themeController != null) mThemeInfo else null
        val themeBG = if (themeInfo == null) null else iconFactory.whiteShadowLayer

        return ClockBitmapInfo(
            bitmap,
            color,
            normalizationScale,
            animationInfo,
            flattenBG,
            themeInfo,
            themeBG,
            BitmapRenderer.createHardwareBitmap(bitmapSize, bitmapSize) {
                val drawable = AdaptiveIconDrawable(background.constantState!!.newDrawable(), null)
                drawable.setBounds(0, 0, bitmapSize, bitmapSize)
                it.drawColor(Color.BLACK)
                drawable.background?.draw(it)
            }
        val result = info.clone()
        result.delegateFactory =
            animationInfo.copy(
                themeFgColor = NO_COLOR,
                shaderProvider = { BitmapShader(flattenBG, CLAMP, CLAMP) },
            )
        return result
    }

    override fun drawForPersistence(canvas: Canvas) {
    override fun drawForPersistence() {
        val foreground = foreground as LayerDrawable
        resetLevel(foreground, animationInfo.hourLayerIndex)
        resetLevel(foreground, animationInfo.minuteLayerIndex)
        resetLevel(foreground, animationInfo.secondLayerIndex)
        draw(canvas)
        animationInfo.applyTime(Calendar.getInstance(), getForeground() as LayerDrawable)
    }

    private fun resetLevel(drawable: LayerDrawable, index: Int) {
        if (index != INVALID_VALUE) {
            drawable.getDrawable(index).setLevel(0)
        }
        if (index != INVALID_VALUE) drawable.getDrawable(index).setLevel(0)
    }

    data class ClockAnimationInfo(
@@ -114,9 +91,7 @@ private constructor(base: AdaptiveIconDrawable, private val animationInfo: Clock
        val defaultSecond: Int,
        val baseDrawableState: ConstantState,
        val themeFgColor: Int = NO_COLOR,
        val boundsOffset: Float = 0f,
        val bg: Bitmap = BitmapInfo.LOW_RES_ICON,
        val bgFilter: ColorFilter? = null,
        val shaderProvider: (IconShape) -> Shader? = { null },
    ) : DelegateFactory {

        fun applyTime(time: Calendar, foregroundDrawable: LayerDrawable): Boolean {
@@ -138,7 +113,6 @@ private constructor(base: AdaptiveIconDrawable, private val animationInfo: Clock
                    val convertedSecond = (time[Calendar.SECOND] + (60 - defaultSecond)) % 60
                    convertedSecond * LEVELS_PER_SECOND
                }

            return invalidateHour || invalidateMinute || invalidateSecond
        }

@@ -147,112 +121,42 @@ private constructor(base: AdaptiveIconDrawable, private val animationInfo: Clock
            iconShape: IconShape,
            paint: Paint,
            host: FastBitmapDrawable,
        ): FastBitmapDrawableDelegate = ClockDrawableDelegate(this, host)
    }

    class ClockBitmapInfo(
        icon: Bitmap,
        color: Int,
        scale: Float,
        val animInfo: ClockAnimationInfo,
        val mFlattenedBackground: Bitmap,
        val themeData: ClockAnimationInfo?,
        val themeBackground: Bitmap?,
    ) : BitmapInfo(icon, color, flags = 0, themedBitmap = null) {
        val boundsOffset: Float =
            max(ShadowGenerator.BLUR_FACTOR.toDouble(), ((1 - scale) / 2).toDouble()).toFloat()

        @TargetApi(VERSION_CODES.TIRAMISU)
        override fun newIcon(
            context: Context,
            @DrawableCreationFlags creationFlags: Int,
            iconShape: IconShape?,
        ): FastBitmapDrawable {
            val bg: Bitmap
            val themedFgColor: Int
            val bgFilter: ColorFilter?
            val baseState: ConstantState
            if (
                (creationFlags and FLAG_THEMED) != 0 && themeData != null && themeBackground != null
            ) {
                val colors = getColors(context)
                val tintedDrawable = themeData.baseDrawableState.newDrawable().mutate()
                themedFgColor = colors[1]
                tintedDrawable.setTint(colors[1])
                bg = themeBackground
                bgFilter = BlendModeColorFilter(colors[0], SRC_IN)
                baseState = tintedDrawable.constantState!!
            } else {
                baseState = animInfo.baseDrawableState
                themedFgColor = NO_COLOR
                bg = mFlattenedBackground
                bgFilter = null
            }

            val animInfoCopy =
                animInfo.copy(
                    baseDrawableState = baseState,
                    themeFgColor = themedFgColor,
                    boundsOffset = boundsOffset,
                    bg = bg,
                    bgFilter = bgFilter,
                )
            val d = FastBitmapDrawable(this, iconShape ?: defaultIconShape, animInfoCopy)
            applyFlags(context, d, creationFlags)
            return d
        }

        override fun canPersist() = false

        override fun clone(): BitmapInfo {
            return copyInternalsTo(
                ClockBitmapInfo(
                    icon,
                    color,
                    1 - 2 * boundsOffset,
                    animInfo,
                    mFlattenedBackground,
                    themeData,
                    themeBackground,
                )
            )
        ): FastBitmapDrawableDelegate {
            return ClockDrawableDelegate(this, host, paint, iconShape)
        }

        override val matchingLookupFlag: CacheLookupFlag
            get() = CacheLookupFlag.DEFAULT_LOOKUP_FLAG.withThemeIcon(themeData != null)
    }

    private class ClockDrawableDelegate(
        private val animInfo: ClockAnimationInfo,
        private val host: FastBitmapDrawable,
        private val paint: Paint,
        private val iconShape: IconShape,
    ) : FastBitmapDrawableDelegate, Runnable {
        private val time: Calendar = Calendar.getInstance()

        private val boundsOffset = animInfo.boundsOffset
        private val bG: Bitmap = animInfo.bg
        private val bgFilter: ColorFilter? = animInfo.bgFilter
        private val bgPaint =
            Paint(Paint.FILTER_BITMAP_FLAG or Paint.ANTI_ALIAS_FLAG).apply {
                colorFilter = bgFilter
        private val time = Calendar.getInstance()
        private val themedFgColor = animInfo.themeFgColor

        private val foreground =
            ((animInfo.baseDrawableState.newDrawable().mutate() as AdaptiveIconDrawable).foreground
                    as LayerDrawable)
                .apply {
                    val extraMargin = (getExtraInsetFraction() * iconShape.pathSize).toInt()
                    setBounds(
                        -extraMargin,
                        -extraMargin,
                        iconShape.pathSize + extraMargin,
                        iconShape.pathSize + extraMargin,
                    )
                    colorFilter = getColorMultipliedFilter(themedFgColor, paint.colorFilter)
                }
        private val themedFgColor: Int = animInfo.themeFgColor

        private val fullDrawable =
            animInfo.baseDrawableState.newDrawable().mutate() as AdaptiveIconDrawable
        private val foreground = fullDrawable.foreground as LayerDrawable
        private val canvasScale: Float = 1 - 2 * boundsOffset
        override fun createPaintShader(bitmapInfo: BitmapInfo, shape: IconShape): Shader? =
            animInfo.shaderProvider.invoke(shape)

        override fun setAlpha(alpha: Int) {
            bgPaint.alpha = alpha
            foreground.alpha = alpha
        }

        override fun onBoundsChange(bounds: Rect) {
            // b/211896569 AdaptiveIcon does not work properly when bounds
            // are not aligned to top/left corner
            fullDrawable.setBounds(0, 0, bounds.width(), bounds.height())
        }

        override fun drawContent(
            info: BitmapInfo,
            host: FastBitmapDrawable,
@@ -260,35 +164,23 @@ private constructor(base: AdaptiveIconDrawable, private val animationInfo: Clock
            bounds: Rect,
            paint: Paint,
        ) {
            canvas.drawBitmap(bG, null, bounds, bgPaint)
            host.drawShaderInBounds(canvas, bounds)

            // prepare and draw the foreground
            animInfo.applyTime(time, foreground)
            canvas.transformed {
                translate(bounds.left.toFloat(), bounds.top.toFloat())
                scale(
                    canvasScale,
                    canvasScale,
                    (bounds.width() / 2).toFloat(),
                    (bounds.height() / 2).toFloat(),
                )
                clipPath(fullDrawable.iconMask)
            canvas.resizeToContentSize(bounds, iconShape.pathSize.toFloat()) {
                clipPath(iconShape.path)
                foreground.draw(this)
            }
            reschedule()
        }

        override fun isThemed(): Boolean {
            return bgPaint.colorFilter != null
            return themedFgColor != NO_COLOR
        }

        override fun updateFilter(isDisabled: Boolean, disabledAlpha: Float) {
            val alpha =
                if (isDisabled) (disabledAlpha * FastBitmapDrawable.FULLY_OPAQUE).toInt()
                else FastBitmapDrawable.FULLY_OPAQUE
            setAlpha(alpha)
            bgPaint.setColorFilter(if (isDisabled) getDisabledColorFilter() else bgFilter)
            foreground.colorFilter = if (isDisabled) getDisabledColorFilter() else null
        override fun updateFilter(filter: ColorFilter?) {
            foreground.colorFilter = getColorMultipliedFilter(themedFgColor, filter)
        }

        override fun getIconColor(info: BitmapInfo): Int {
@@ -328,7 +220,7 @@ private constructor(base: AdaptiveIconDrawable, private val animationInfo: Clock
        private const val TAG = "ClockDrawableWrapper"

        private const val DISABLE_SECONDS = true
        private const val NO_COLOR = -1
        private const val NO_COLOR = Color.TRANSPARENT

        // Time after which the clock icon should check for an update. The actual invalidate
        // will only happen in case of any change.
@@ -405,22 +297,8 @@ private constructor(base: AdaptiveIconDrawable, private val animationInfo: Clock
                foreground.setDrawable(animInfo.secondLayerIndex, null)
                animInfo = animInfo.copy(secondLayerIndex = INVALID_VALUE)
            }

            val wrapper = ClockDrawableWrapper(drawable, animInfo)
            if (IconProvider.ATLEAST_T && drawable.monochrome is LayerDrawable) {
                wrapper.mThemeInfo =
                    animInfo.copy(
                        baseDrawableState =
                            AdaptiveIconDrawable(
                                    ColorDrawable(Color.WHITE),
                                    drawable.monochrome!!.mutate(),
                                )
                                .constantState!!
                    )
            }

            animInfo.applyTime(Calendar.getInstance(), foreground)
            return wrapper
            return ClockDrawableWrapper(drawable, animInfo)
        }
    }
}
+17 −33

File changed.

Preview size limit exceeded, changes collapsed.

+20 −2
Original line number Diff line number Diff line
@@ -16,11 +16,16 @@

package com.android.launcher3.icons

import android.graphics.BitmapShader
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.ColorFilter
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.Shader
import android.graphics.Shader.TileMode.CLAMP
import androidx.core.graphics.ColorUtils
import com.android.launcher3.icons.BitmapInfo.Companion.FLAG_FULL_BLEED

/** A delegate for changing the rendering of [FastBitmapDrawable], to support multi-inheritance */
interface FastBitmapDrawableDelegate {
@@ -36,7 +41,11 @@ interface FastBitmapDrawableDelegate {
        bounds: Rect,
        paint: Paint,
    ) {
        host.drawContent(canvas, bounds)
        if ((info.flags and FLAG_FULL_BLEED) != 0) {
            host.drawShaderInBounds(canvas, bounds)
        } else {
            canvas.drawBitmap(info.icon, null, bounds, paint)
        }
    }

    /** [FastBitmapDrawable.getIconColor] */
@@ -53,7 +62,7 @@ interface FastBitmapDrawableDelegate {
    fun setAlpha(alpha: Int) {}

    /** [android.graphics.drawable.Drawable.setColorFilter] */
    fun updateFilter(isDisabled: Boolean, disabledAlpha: Float) {}
    fun updateFilter(filter: ColorFilter?) {}

    /** [android.graphics.drawable.Drawable.setVisible] */
    fun onVisibilityChanged(isVisible: Boolean) {}
@@ -61,6 +70,15 @@ interface FastBitmapDrawableDelegate {
    /** [android.graphics.drawable.Drawable.onLevelChange] */
    fun onLevelChange(level: Int): Boolean = false

    /** Creates a default shader to be used for drawing the drawable */
    fun createPaintShader(bitmapInfo: BitmapInfo, shape: IconShape): Shader? {
        return if ((bitmapInfo.flags and FLAG_FULL_BLEED) != 0) {
            BitmapShader(bitmapInfo.icon, CLAMP, CLAMP)
        } else {
            null
        }
    }

    /**
     * Interface for creating new delegates. This should not store any state information and can
     * safely be stored in a [android.graphics.drawable.Drawable.ConstantState]
Loading