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

Commit 3acd8379 authored by Tony Wickham's avatar Tony Wickham Committed by Android (Google) Code Review
Browse files

Merge "Always place notification dots directly on adaptive icon path" into ub-launcher3-qt-dev

parents ef8ba7bb e1cb93f9
Loading
Loading
Loading
Loading
+48 −25
Original line number Diff line number Diff line
@@ -23,8 +23,10 @@ import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.Log;
import android.view.ViewDebug;

@@ -36,33 +38,51 @@ public class DotRenderer {
    private static final String TAG = "DotRenderer";

    // The dot size is defined as a percentage of the app icon size.
    private static final float SIZE_PERCENTAGE = 0.38f;
    private static final float SIZE_PERCENTAGE = 0.228f;

    // Extra scale down of the dot
    private static final float DOT_SCALE = 0.6f;

    // Offset the dot slightly away from the icon if there's space.
    private static final float OFFSET_PERCENTAGE = 0.02f;

    private final float mDotCenterOffset;
    private final int mOffset;
    private final float mCircleRadius;
    private final Paint mCirclePaint = new Paint(ANTI_ALIAS_FLAG | FILTER_BITMAP_FLAG);

    private final Bitmap mBackgroundWithShadow;
    private final float mBitmapOffset;

    public DotRenderer(int iconSizePx) {
        mDotCenterOffset = SIZE_PERCENTAGE * iconSizePx;
        mOffset = (int) (OFFSET_PERCENTAGE * iconSizePx);
    // 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;

        int size = (int) (DOT_SCALE * mDotCenterOffset);
    public DotRenderer(int iconSizePx, Path iconShapePath, int pathSize) {
        int size = Math.round(SIZE_PERCENTAGE * iconSizePx);
        ShadowGenerator.Builder builder = new ShadowGenerator.Builder(Color.TRANSPARENT);
        builder.ambientShadowAlpha = 88;
        mBackgroundWithShadow = builder.setupBlurForSize(size).createPill(size, size);
        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) {
        float halfSize = size / 2;
        // Small delta so that we don't get a zero size triangle
        float delta = 1;

        float x = halfSize + direction * halfSize;
        Path trianglePath = new Path();
        trianglePath.moveTo(halfSize, halfSize);
        trianglePath.lineTo(x + delta * direction, 0);
        trianglePath.lineTo(x, -delta);
        trianglePath.close();

        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;
    }

    /**
@@ -74,15 +94,21 @@ public class DotRenderer {
            return;
        }
        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];

        // Ensure dot fits entirely in canvas clip bounds.
        Rect canvasBounds = canvas.getClipBounds();
        float offsetX = params.leftAlign
                ? Math.max(0, canvasBounds.left - (dotCenterX + mBitmapOffset))
                : Math.min(0, canvasBounds.right - (dotCenterX - mBitmapOffset));
        float offsetY = Math.max(0, canvasBounds.top - (dotCenterY + mBitmapOffset));

        // We draw the dot relative to its center.
        float dotCenterX = params.leftAlign
                ? params.iconBounds.left + mDotCenterOffset / 2
                : params.iconBounds.right - mDotCenterOffset / 2;
        float dotCenterY = params.iconBounds.top + mDotCenterOffset / 2;

        int offsetX = Math.min(mOffset, params.spaceForOffset.x);
        int offsetY = Math.min(mOffset, params.spaceForOffset.y);
        canvas.translate(dotCenterX + offsetX, dotCenterY - offsetY);
        canvas.translate(dotCenterX + offsetX, dotCenterY + offsetY);
        canvas.scale(params.scale, params.scale);

        mCirclePaint.setColor(Color.BLACK);
@@ -102,9 +128,6 @@ public class DotRenderer {
        /** The progress of the animation, from 0 to 1. */
        @ViewDebug.ExportedProperty(category = "notification dot")
        public float scale;
        /** Overrides internally calculated offset if specified value is smaller. */
        @ViewDebug.ExportedProperty(category = "notification dot")
        public Point spaceForOffset = new Point();
        /** 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;
+3 −1
Original line number Diff line number Diff line
@@ -46,10 +46,12 @@ import com.android.launcher3.dot.DotInfo;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.graphics.IconPalette;
import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.graphics.PreloadIconDrawable;
import com.android.launcher3.icons.DotRenderer;
import com.android.launcher3.icons.IconCache.IconLoadRequest;
import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.views.ActivityContext;

@@ -388,7 +390,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
    protected void drawDotIfNecessary(Canvas canvas) {
        if (!mForceHideDot && (hasDot() || mDotParams.scale > 0)) {
            getIconBounds(mDotParams.iconBounds);
            mDotParams.spaceForOffset.set((getWidth() - mIconSize) / 2, getPaddingTop());
            Utilities.scaleRectAboutCenter(mDotParams.iconBounds, IconShape.getNormalizationScale());
            final int scrollX = getScrollX();
            final int scrollY = getScrollY();
            canvas.translate(scrollX, scrollY);
+3 −4
Original line number Diff line number Diff line
@@ -24,15 +24,13 @@ import android.graphics.PointF;
import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.view.Surface;
import android.view.View;
import android.view.WindowManager;

import com.android.launcher3.CellLayout.ContainerType;
import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.icons.DotRenderer;
import com.android.launcher3.icons.IconNormalizer;

import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;

public class DeviceProfile {

    public final InvariantDeviceProfile inv;
@@ -240,7 +238,8 @@ public class DeviceProfile {
        updateWorkspacePadding();

        // This is done last, after iconSizePx is calculated above.
        mDotRenderer = new DotRenderer(iconSizePx);
        mDotRenderer = new DotRenderer(iconSizePx, IconShape.getShapePath(),
                IconShape.DEFAULT_PATH_SIZE);
    }

    public DeviceProfile copy(Context context) {
+5 −4
Original line number Diff line number Diff line
@@ -36,6 +36,8 @@ import android.view.ViewDebug;
import android.view.ViewGroup;
import android.widget.FrameLayout;

import androidx.annotation.NonNull;

import com.android.launcher3.Alarm;
import com.android.launcher3.AppInfo;
import com.android.launcher3.BubbleTextView;
@@ -50,11 +52,11 @@ import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.OnAlarmListener;
import com.android.launcher3.R;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.SimpleOnStylusPressListener;
import com.android.launcher3.StylusEventHelper;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.dot.FolderDotInfo;
import com.android.launcher3.dragndrop.BaseItemDragListener;
@@ -68,8 +70,6 @@ import com.android.launcher3.widget.PendingAddShortcutInfo;
import java.util.ArrayList;
import java.util.List;

import androidx.annotation.NonNull;

/**
 * An icon that can appear on in the workspace representing an {@link Folder}.
 */
@@ -510,10 +510,11 @@ public class FolderIcon extends FrameLayout implements FolderListener {
            Rect iconBounds = mDotParams.iconBounds;
            BubbleTextView.getIconBounds(this, iconBounds,
                    mLauncher.getWallpaperDeviceProfile().iconSizePx);
            float iconScale = (float) mBackground.previewSize / iconBounds.width();
            Utilities.scaleRectAboutCenter(iconBounds, iconScale);

            // If we are animating to the accepting state, animate the dot out.
            mDotParams.scale = Math.max(0, mDotScale - mBackground.getScaleProgress());
            mDotParams.spaceForOffset.set(getWidth() - iconBounds.right, iconBounds.top);
            mDotParams.color = mBackground.getDotColor();
            mDotRenderer.draw(canvas, mDotParams);
        }