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

Commit 3ba48fa0 authored by Sunny Goyal's avatar Sunny Goyal Committed by Android (Google) Code Review
Browse files

Merge "Updating FolderIcon drawing to avoid dependency on software layer." into ub-launcher3-dorval

parents d896d1c2 19b93b79
Loading
Loading
Loading
Loading
+4 −5
Original line number Diff line number Diff line
@@ -105,7 +105,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {

    private ArrayList<FolderIcon.PreviewBackground> mFolderBackgrounds = new ArrayList<FolderIcon.PreviewBackground>();
    FolderIcon.PreviewBackground mFolderLeaveBehind = new FolderIcon.PreviewBackground();
    Paint mFolderBgPaint = new Paint();

    private float mBackgroundAlpha;

@@ -501,9 +500,9 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
            cellToPoint(bg.delegateCellX, bg.delegateCellY, mTempLocation);
            canvas.save();
            canvas.translate(mTempLocation[0], mTempLocation[1]);
            bg.drawBackground(canvas, mFolderBgPaint);
            bg.drawBackground(canvas);
            if (!bg.isClipping) {
                bg.drawBackgroundStroke(canvas, mFolderBgPaint);
                bg.drawBackgroundStroke(canvas);
            }
            canvas.restore();
        }
@@ -513,7 +512,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
                    mFolderLeaveBehind.delegateCellY, mTempLocation);
            canvas.save();
            canvas.translate(mTempLocation[0], mTempLocation[1]);
            mFolderLeaveBehind.drawLeaveBehind(canvas, mFolderBgPaint);
            mFolderLeaveBehind.drawLeaveBehind(canvas);
            canvas.restore();
        }
    }
@@ -528,7 +527,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
                cellToPoint(bg.delegateCellX, bg.delegateCellY, mTempLocation);
                canvas.save();
                canvas.translate(mTempLocation[0], mTempLocation[1]);
                bg.drawBackgroundStroke(canvas, mFolderBgPaint);
                bg.drawBackgroundStroke(canvas);
                canvas.restore();
            }
        }
+128 −72
Original line number Diff line number Diff line
@@ -24,11 +24,15 @@ import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.os.Parcelable;
import android.util.AttributeSet;
@@ -125,8 +129,6 @@ public class FolderIcon extends FrameLayout implements FolderListener {
    private ArrayList<PreviewItemDrawingParams> mDrawingParams = new ArrayList<PreviewItemDrawingParams>();
    private Drawable mReferenceDrawable = null;

    Paint mBgPaint = new Paint();

    private Alarm mOpenAlarm = new Alarm();

    private FolderBadgeInfo mBadgeInfo = new FolderBadgeInfo();
@@ -179,11 +181,6 @@ public class FolderIcon extends FrameLayout implements FolderListener {
        DeviceProfile grid = launcher.getDeviceProfile();
        FolderIcon icon = (FolderIcon) LayoutInflater.from(launcher).inflate(resId, group, false);

        // For performance and compatibility reasons we render the preview using a software layer.
        // In particular, hardware path clipping has spotty ecosystem support and bad performance.
        // Software rendering also allows us to use shadow layers.
        icon.setLayerType(LAYER_TYPE_SOFTWARE, new Paint(Paint.FILTER_BITMAP_FLAG));

        icon.setClipToPadding(false);
        icon.mFolderName = (BubbleTextView) icon.findViewById(R.id.folder_icon_name);
        icon.mFolderName.setText(folderInfo.title);
@@ -493,7 +490,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
    }

    private void drawPreviewItem(Canvas canvas, PreviewItemDrawingParams params) {
        canvas.save();
        canvas.save(Canvas.MATRIX_SAVE_FLAG);
        canvas.translate(params.transX, params.transY);
        canvas.scale(params.scale, params.scale);
        Drawable d = params.drawable;
@@ -520,10 +517,29 @@ public class FolderIcon extends FrameLayout implements FolderListener {
     * information, handles drawing, and animation (accept state <--> rest state).
     */
    public static class PreviewBackground {

        private final PorterDuffXfermode mClipPorterDuffXfermode
                = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
        // Create a RadialGradient such that it draws a black circle and then extends with
        // transparent. To achieve this, we keep the gradient to black for the range [0, 1) and
        // just at the edge quickly change it to transparent.
        private final RadialGradient mClipShader = new RadialGradient(0, 0, 1,
                new int[] {Color.BLACK, Color.BLACK, Color.TRANSPARENT },
                new float[] {0, 0.999f, 1},
                Shader.TileMode.CLAMP);

        private final PorterDuffXfermode mShadowPorterDuffXfermode
                = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
        private RadialGradient mShadowShader = null;

        private final Matrix mShaderMatrix = new Matrix();
        private final Path mPath = new Path();

        private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

        private float mScale = 1f;
        private float mColorMultiplier = 1f;
        private Path mClipPath = new Path();
        private int mStrokeWidth;
        private float mStrokeWidth;
        private View mInvalidateDelegate;

        public int previewSize;
@@ -546,7 +562,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
        private static final int BG_OPACITY = 160;
        private static final int MAX_BG_OPACITY = 225;
        private static final int BG_INTENSITY = 245;
        private static final int SHADOW_OPACITY = 80;
        private static final int SHADOW_OPACITY = 40;

        ValueAnimator mScaleAnimator;

@@ -562,7 +578,16 @@ public class FolderIcon extends FrameLayout implements FolderListener {
            basePreviewOffsetX = (availableSpace - this.previewSize) / 2;
            basePreviewOffsetY = previewPadding + grid.folderBackgroundOffset + topPadding;

            mStrokeWidth = Utilities.pxFromDp(1, dm);
            // Stroke width is 1dp
            mStrokeWidth = dm.density;

            float radius = getScaledRadius();
            float shadowRadius = radius + mStrokeWidth;
            int shadowColor = Color.argb(SHADOW_OPACITY, 0, 0, 0);
            mShadowShader = new RadialGradient(0, 0, 1,
                    new int[] {shadowColor, Color.TRANSPARENT},
                    new float[] {radius / shadowRadius, 1},
                    Shader.TileMode.CLAMP);

            invalidate();
        }
@@ -592,10 +617,6 @@ public class FolderIcon extends FrameLayout implements FolderListener {
        }

        void invalidate() {
            int radius = getScaledRadius();
            mClipPath.reset();
            mClipPath.addCircle(radius, radius, radius, Path.Direction.CW);

            if (mInvalidateDelegate != null) {
                mInvalidateDelegate.invalidate();
            }
@@ -610,70 +631,94 @@ public class FolderIcon extends FrameLayout implements FolderListener {
            invalidate();
        }

        public void drawBackground(Canvas canvas, Paint paint) {
            canvas.save();
            canvas.translate(getOffsetX(), getOffsetY());

            paint.reset();
            paint.setStyle(Paint.Style.FILL);
            paint.setXfermode(null);
            paint.setAntiAlias(true);

        public void drawBackground(Canvas canvas) {
            mPaint.setStyle(Paint.Style.FILL);
            int alpha = (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
            paint.setColor(Color.argb(alpha, BG_INTENSITY, BG_INTENSITY, BG_INTENSITY));

            float radius = getScaledRadius();
            mPaint.setColor(Color.argb(alpha, BG_INTENSITY, BG_INTENSITY, BG_INTENSITY));

            canvas.drawCircle(radius, radius, radius, paint);
            canvas.clipPath(mClipPath, Region.Op.DIFFERENCE);
            drawCircle(canvas, 0 /* deltaRadius */);

            paint.setStyle(Paint.Style.STROKE);
            paint.setColor(Color.TRANSPARENT);
            paint.setShadowLayer(mStrokeWidth, 0, mStrokeWidth, Color.argb(SHADOW_OPACITY, 0, 0, 0));
            canvas.drawCircle(radius, radius, radius, paint);
            // Draw shadow.
            if (mShadowShader == null) {
                return;
            }
            float radius = getScaledRadius();
            float shadowRadius = radius + mStrokeWidth;
            mPaint.setColor(Color.BLACK);
            int offsetX = getOffsetX();
            int offsetY = getOffsetY();
            final int saveCount;
            if (canvas.isHardwareAccelerated()) {
                saveCount = canvas.saveLayer(offsetX - mStrokeWidth, offsetY,
                        offsetX + radius + shadowRadius, offsetY + shadowRadius + shadowRadius,
                        null, Canvas.CLIP_TO_LAYER_SAVE_FLAG | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);

            canvas.restore();
            } else {
                saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
                clipCanvasSoftware(canvas, Region.Op.DIFFERENCE);
            }

        public void drawBackgroundStroke(Canvas canvas, Paint paint) {
            canvas.save();
            canvas.translate(getOffsetX(), getOffsetY());
            mShaderMatrix.setScale(shadowRadius, shadowRadius);
            mShaderMatrix.postTranslate(radius + offsetX, shadowRadius + offsetY);
            mShadowShader.setLocalMatrix(mShaderMatrix);
            mPaint.setShader(mShadowShader);
            canvas.drawPaint(mPaint);
            mPaint.setShader(null);

            paint.reset();
            paint.setAntiAlias(true);
            paint.setColor(Color.argb(255, BG_INTENSITY, BG_INTENSITY, BG_INTENSITY));
            paint.setStyle(Paint.Style.STROKE);
            paint.setStrokeWidth(mStrokeWidth);
            if (canvas.isHardwareAccelerated()) {
                mPaint.setXfermode(mShadowPorterDuffXfermode);
                canvas.drawCircle(radius + offsetX, radius + offsetY, radius, mPaint);
                mPaint.setXfermode(null);
            }

            float radius = getScaledRadius();
            canvas.drawCircle(radius, radius, radius - 1, paint);
            canvas.restoreToCount(saveCount);
        }

            canvas.restore();
        public void drawBackgroundStroke(Canvas canvas) {
            mPaint.setColor(Color.argb(255, BG_INTENSITY, BG_INTENSITY, BG_INTENSITY));
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setStrokeWidth(mStrokeWidth);
            drawCircle(canvas, 1 /* deltaRadius */);
        }

        public void drawLeaveBehind(Canvas canvas, Paint paint) {
        public void drawLeaveBehind(Canvas canvas) {
            float originalScale = mScale;
            mScale = 0.5f;

            canvas.save();
            canvas.translate(getOffsetX(), getOffsetY());
            mPaint.setStyle(Paint.Style.FILL);
            mPaint.setColor(Color.argb(160, 245, 245, 245));
            drawCircle(canvas, 0 /* deltaRadius */);

            paint.reset();
            paint.setAntiAlias(true);
            paint.setColor(Color.argb(160, 245, 245, 245));
            mScale = originalScale;
        }

        private void drawCircle(Canvas canvas,float deltaRadius) {
            float radius = getScaledRadius();
            canvas.drawCircle(radius, radius, radius, paint);
            canvas.drawCircle(radius + getOffsetX(), radius + getOffsetY(),
                    radius - deltaRadius, mPaint);
        }

            canvas.restore();
            mScale = originalScale;
        // It is the callers responsibility to save and restore the canvas layers.
        private void clipCanvasSoftware(Canvas canvas, Region.Op op) {
            mPath.reset();
            float r = getScaledRadius();
            mPath.addCircle(r + getOffsetX(), r + getOffsetY(), r, Path.Direction.CW);
            canvas.clipPath(mPath, op);
        }

        // It is the callers responsibility to save and restore the canvas.
        private void clipCanvas(Canvas canvas) {
            canvas.translate(getOffsetX(), getOffsetY());
            canvas.clipPath(mClipPath);
            canvas.translate(-getOffsetX(), -getOffsetY());
        // It is the callers responsibility to save and restore the canvas layers.
        private void clipCanvasHardware(Canvas canvas) {
            mPaint.setColor(Color.BLACK);
            mPaint.setXfermode(mClipPorterDuffXfermode);

            float radius = getScaledRadius();
            mShaderMatrix.setScale(radius, radius);
            mShaderMatrix.postTranslate(radius + getOffsetX(), radius + getOffsetY());
            mClipShader.setLocalMatrix(mShaderMatrix);
            mPaint.setShader(mClipShader);
            canvas.drawPaint(mPaint);
            mPaint.setXfermode(null);
            mPaint.setShader(null);
        }

        private void delegateDrawing(CellLayout delegate, int cellX, int cellY) {
@@ -793,17 +838,22 @@ public class FolderIcon extends FrameLayout implements FolderListener {
        }

        if (!mBackground.drawingDelegated()) {
            mBackground.drawBackground(canvas, mBgPaint);
            mBackground.drawBackground(canvas);
        }

        if (mFolder == null) return;
        if (mFolder.getItemCount() == 0 && !mAnimating) return;

        canvas.save();

        final int saveCount;

        if (canvas.isHardwareAccelerated()) {
            saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null,
                    Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
        } else {
            saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
            if (mPreviewLayoutRule.clipToBackground()) {
            mBackground.clipCanvas(canvas);
                mBackground.clipCanvasSoftware(canvas, Region.Op.INTERSECT);
            }
        }

        // The items are drawn in coordinates relative to the preview offset
@@ -816,18 +866,24 @@ public class FolderIcon extends FrameLayout implements FolderListener {
                drawPreviewItem(canvas, p);
            }
        }
        canvas.restore();
        canvas.translate(-mBackground.basePreviewOffsetX, -mBackground.basePreviewOffsetY);

        if (mPreviewLayoutRule.clipToBackground() && canvas.isHardwareAccelerated()) {
            mBackground.clipCanvasHardware(canvas);
        }
        canvas.restoreToCount(saveCount);

        if (mPreviewLayoutRule.clipToBackground() && !mBackground.drawingDelegated()) {
            mBackground.drawBackgroundStroke(canvas, mBgPaint);
            mBackground.drawBackgroundStroke(canvas);
        }

        if ((mBadgeInfo != null && mBadgeInfo.getNotificationCount() > 0) || mBadgeScale > 0) {
            // If we are animating to the accepting state, animate the badge out.
            int offsetX = mBackground.getOffsetX();
            int offsetY = mBackground.getOffsetY();
            int previewSize = (int) (mBackground.previewSize * mBackground.mScale);
            Rect bounds = new Rect(offsetX, offsetY, offsetX + previewSize, offsetY + previewSize);
        if ((mBadgeInfo != null && mBadgeInfo.getNotificationCount() > 0) || mBadgeScale > 0) {
            // If we are animating to the accepting state, animate the badge out.

            float badgeScale = Math.max(0, mBadgeScale - mBackground.getScaleProgress());
            mBadgeRenderer.draw(canvas, IconPalette.FOLDER_ICON_PALETTE, mBadgeInfo, bounds, badgeScale);
        }