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

Commit a7adca8e authored by Android Build Coastguard Worker's avatar Android Build Coastguard Worker
Browse files

Snap for 13805031 from 0e46bf2d to 25Q4-release

Change-Id: If1de7ac0465ea6f9b2ced0994a0587b787d481b5
parents c4102abd 0e46bf2d
Loading
Loading
Loading
Loading
+19 −17
Original line number Diff line number Diff line
@@ -6,12 +6,14 @@ import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Build;
import android.os.UserHandle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -73,32 +75,32 @@ public class BubbleIconFactory extends BaseIconFactory {
     * Creates the bitmap for the provided drawable and returns the scale used for
     * drawing the actual drawable. This is used for the larger icon shown for the bubble.
     */
    public Bitmap getBubbleBitmap(@NonNull Drawable icon, float[] outScale) {
        if (outScale != null) {
            outScale[0] = IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
        }
        return createIconBitmap(
                wrapToAdaptiveIcon(icon),
                IconNormalizer.ICON_VISIBLE_AREA_FACTOR,
                MODE_WITH_SHADOW);
    public Bitmap getBubbleBitmap(@NonNull Drawable icon) {
        return createBadgedIconBitmap(
                icon, new IconOptions()
                        .setBitmapGenerationMode(MODE_WITH_SHADOW)
                        // We do not care about extracted color
                        .setExtractedColor(Color.TRANSPARENT)).icon;
    }

    /**
     * Returns a {@link BitmapInfo} for the app-badge that is shown on top of each bubble. This
     * will include the workprofile indicator on the badge if appropriate.
     */
    public BitmapInfo getBadgeBitmap(Drawable userBadgedAppIcon, boolean isImportantConversation) {
        if (userBadgedAppIcon instanceof AdaptiveIconDrawable) {
            AdaptiveIconDrawable ad = (AdaptiveIconDrawable) userBadgedAppIcon;
            userBadgedAppIcon = new CircularAdaptiveIcon(ad.getBackground(),
                    ad.getForeground());
    public BitmapInfo getBadgeBitmap(Drawable appIcon, UserHandle user,
            boolean isImportantConversation) {
        if (appIcon instanceof AdaptiveIconDrawable ad) {
            appIcon = new CircularAdaptiveIcon(ad.getBackground(), ad.getForeground());
        }
        if (isImportantConversation) {
            userBadgedAppIcon = new CircularRingDrawable(userBadgedAppIcon);
        }
        Bitmap userBadgedBitmap = mBadgeFactory.createIconBitmap(
                userBadgedAppIcon, 1, MODE_WITH_SHADOW);
        return mBadgeFactory.createIconBitmap(userBadgedBitmap);
            appIcon = new CircularRingDrawable(appIcon);
        }
        return mBadgeFactory.createBadgedIconBitmap(
                appIcon,
                new IconOptions()
                        .setBitmapGenerationMode(MODE_WITH_SHADOW)
                        .setWrapNonAdaptiveIcon(false)
                        .setUser(user));
    }

    private class CircularRingDrawable extends CircularAdaptiveIcon {
+64 −38
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static android.graphics.Color.luminance;
import static android.graphics.Paint.ANTI_ALIAS_FLAG;
import static android.graphics.Paint.FILTER_BITMAP_FLAG;

import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
import static com.android.systemui.shared.Flags.notificationDotContrastBorder;

import android.graphics.Bitmap;
@@ -28,10 +29,12 @@ import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.PointF;
import android.graphics.Rect;
import android.util.Log;
import android.view.ViewDebug;

import androidx.annotation.NonNull;
import androidx.core.graphics.ColorUtils;

/**
@@ -52,12 +55,9 @@ public class DotRenderer {
    private final Bitmap mBackgroundWithShadow;
    private final float mBitmapOffset;

    // Stores the center x and y position as a percentage (0 to 1) of the icon size
    private final float[] mRightDotPosition;
    private final float[] mLeftDotPosition;

    private static final int MIN_DOT_SIZE = 1;
    public DotRenderer(int iconSizePx, Path iconShapePath, int pathSize) {

    public DotRenderer(int iconSizePx) {
        int size = Math.round(SIZE_PERCENTAGE * iconSizePx);
        if (size <= 0) {
            size = MIN_DOT_SIZE;
@@ -68,13 +68,9 @@ public class DotRenderer {
        mCircleRadius = builder.radius;

        mBitmapOffset = -mBackgroundWithShadow.getHeight() * 0.5f; // Same as width.

        // Find the points on the path that are closest to the top left and right corners.
        mLeftDotPosition = getPathPoint(iconShapePath, pathSize, -1);
        mRightDotPosition = getPathPoint(iconShapePath, pathSize, 1);
    }

    private static float[] getPathPoint(Path path, float size, float direction) {
    private static PointF getPathPoint(Path path, float size, float direction) {
        float halfSize = size / 2;
        // Small delta so that we don't get a zero size triangle
        float delta = 1;
@@ -89,18 +85,7 @@ public class DotRenderer {
        trianglePath.op(path, Path.Op.INTERSECT);
        float[] pos = new float[2];
        new PathMeasure(trianglePath, false).getPosTan(0, pos, null);

        pos[0] = pos[0] / size;
        pos[1] = pos[1] / size;
        return pos;
    }

    public float[] getLeftDotPosition() {
        return mLeftDotPosition;
    }

    public float[] getRightDotPosition() {
        return mRightDotPosition;
        return new PointF(pos[0] / size, pos[1] / size);
    }

    /**
@@ -114,9 +99,9 @@ public class DotRenderer {
        canvas.save();

        Rect iconBounds = params.iconBounds;
        float[] dotPosition = params.leftAlign ? mLeftDotPosition : mRightDotPosition;
        float dotCenterX = iconBounds.left + iconBounds.width() * dotPosition[0];
        float dotCenterY = iconBounds.top + iconBounds.height() * dotPosition[1];
        PointF dotPosition = params.getDotPosition();
        float dotCenterX = iconBounds.left + iconBounds.width() * dotPosition.x;
        float dotCenterY = iconBounds.top + iconBounds.height() * dotPosition.y;

        // Ensure dot fits entirely in canvas clip bounds.
        Rect canvasBounds = canvas.getClipBounds();
@@ -133,25 +118,14 @@ public class DotRenderer {
        mCirclePaint.setColor(Color.BLACK);
        canvas.drawBitmap(mBackgroundWithShadow, mBitmapOffset, mBitmapOffset, mCirclePaint);

        // Draw Colored Dot - Update dot color to have sufficient accessibility contrast
        if (notificationDotContrastBorder() && luminance(params.dotColor) < LUMINENSCE_LIMIT) {
            double[] lab = new double[3];
            ColorUtils.colorToLAB(params.dotColor, lab);
            params.dotColor = ColorUtils.LABToColor(100 * LUMINENSCE_LIMIT, lab[1], lab[2]);
        }

        mCirclePaint.setColor(params.dotColor);
        mCirclePaint.setColor(params.mDotColor);
        canvas.drawCircle(0, 0, mCircleRadius, mCirclePaint);
        canvas.restore();
    }

    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;
        private int mDotColor;
        /** The bounds of the icon that the dot is drawn on top of. */
        @ViewDebug.ExportedProperty(category = "notification dot")
        public Rect iconBounds = new Rect();
@@ -161,5 +135,57 @@ public class DotRenderer {
        /** Whether the dot should align to the top left of the icon rather than the top right. */
        @ViewDebug.ExportedProperty(category = "notification dot")
        public boolean leftAlign;

        @NonNull
        public IconShapeInfo shapeInfo = IconShapeInfo.DEFAULT;

        public PointF getDotPosition() {
            return leftAlign ? shapeInfo.leftCornerPosition : shapeInfo.rightCornerPosition;
        }

        /** The color (possibly based on the icon) to use for the dot. */
        public void setDotColor(int color) {
            mDotColor = color;

            if (notificationDotContrastBorder() && luminance(color) < LUMINENSCE_LIMIT) {
                double[] lab = new double[3];
                ColorUtils.colorToLAB(color, lab);
                mDotColor = ColorUtils.LABToColor(100 * LUMINENSCE_LIMIT, lab[1], lab[2]);
            }
        }
    }

    /**
     * Class stores information about the icon icon shape on which the dot is being rendered.
     * It stores the center x and y position as a percentage (0 to 1) of the icon size
     */
    public record IconShapeInfo(PointF leftCornerPosition, PointF rightCornerPosition) {

        /** Shape when the icon rendered completely fills {@link DrawParams#iconBounds} */
        public static IconShapeInfo DEFAULT =
                fromPath(IconShape.EMPTY.path, IconShape.EMPTY.pathSize);

        /** Shape when a normalized icon is rendered within {@link DrawParams#iconBounds} */
        public static IconShapeInfo DEFAULT_NORMALIZED = new IconShapeInfo(
                normalizedPosition(DEFAULT.leftCornerPosition),
                normalizedPosition(DEFAULT.rightCornerPosition)
        );

        /**
         * Creates an IconShapeInfo from the provided path in bounds [0, 0, pathSize, pathSize]
         */
        public static IconShapeInfo fromPath(Path path, int pathSize) {
            return new IconShapeInfo(
                    getPathPoint(path, pathSize, -1),
                    getPathPoint(path, pathSize, 1));
        }

        private static PointF normalizedPosition(PointF pos) {
            float center = 0.5f;
            return new PointF(
                    center + ICON_VISIBLE_AREA_FACTOR * (pos.x - center),
                    center + ICON_VISIBLE_AREA_FACTOR * (pos.y - center)
            );
        }
    }
}