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

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

Merge "Improving folder icon drawable" into tm-dev

parents 8c656753 c317d925
Loading
Loading
Loading
Loading
+132 −54
Original line number Diff line number Diff line
@@ -20,9 +20,13 @@ import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;

import android.annotation.TargetApi;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Path.Direction;
import android.graphics.Picture;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.AdaptiveIconDrawable;
@@ -31,10 +35,11 @@ import android.os.Build;
import android.util.Log;

import androidx.annotation.Nullable;
import androidx.annotation.UiThread;

import com.android.launcher3.Utilities;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.folder.PreviewBackground;
import com.android.launcher3.graphics.ShiftedBitmapDrawable;
import com.android.launcher3.icons.BitmapRenderer;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.views.ActivityContext;
@@ -69,79 +74,104 @@ public class FolderAdaptiveIcon extends AdaptiveIconDrawable {
        return mBadge;
    }

    @TargetApi(Build.VERSION_CODES.P)
    public static @Nullable FolderAdaptiveIcon createFolderAdaptiveIcon(
            ActivityContext activity, int folderId, Point dragViewSize) {
            ActivityContext activity, int folderId, Point size) {
        Preconditions.assertNonUiThread();
        if (!Utilities.ATLEAST_P) {
            return null;
        }

        // assume square
        if (size.x != size.y) {
            return null;
        }
        int requestedSize = size.x;

        // Only use the size actually needed for drawing the folder icon
        int drawingSize = activity.getDeviceProfile().folderIconSizePx;
        int foregroundSize = Math.max(requestedSize, drawingSize);
        float shift = foregroundSize - requestedSize;

        Picture background = new Picture();
        Picture foreground = new Picture();
        Picture badge = new Picture();

        // Create the actual drawable on the UI thread to avoid race conditions with
        Canvas bgCanvas = background.beginRecording(requestedSize, requestedSize);
        Canvas badgeCanvas = badge.beginRecording(requestedSize, requestedSize);

        Canvas fgCanvas = foreground.beginRecording(foregroundSize, foregroundSize);
        fgCanvas.translate(shift, shift);

        // Do not clip the folder drawing since the icon previews extend outside the background.
        Path mask = new Path();
        mask.addRect(-shift, -shift, requestedSize + shift, requestedSize + shift,
                Direction.CCW);

        // Initialize the actual draw commands on the UI thread to avoid race conditions with
        // FolderIcon draw pass
        try {
            return MAIN_EXECUTOR.submit(() -> {
            MAIN_EXECUTOR.submit(() -> {
                FolderIcon icon = activity.findFolderIcon(folderId);
                return icon == null ? null : createDrawableOnUiThread(icon, dragViewSize);

                if (icon == null) {
                    throw new IllegalArgumentException("Folder not found with id: " + folderId);
                }
                initLayersOnUiThread(icon, requestedSize, bgCanvas, fgCanvas, badgeCanvas);
            }).get();
        } catch (Exception e) {
            Log.e(TAG, "Unable to create folder icon", e);
            return null;
        }
        } finally {
            background.endRecording();
            foreground.endRecording();
            badge.endRecording();
        }

    private static FolderAdaptiveIcon createDrawableOnUiThread(FolderIcon icon,
                                                               Point dragViewSize) {
        Preconditions.assertUIThread();
        // Only convert foreground to a bitmap as it can contain multiple draw commands. Other
        // layers either draw a nothing or a single draw call.
        Bitmap fgBitmap = Bitmap.createBitmap(foreground);
        Paint foregroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

        // Do not use PictureDrawable as it moves the picture to the canvas bounds, whereas we want
        // to draw it at (0,0)
        return new FolderAdaptiveIcon(
                new BitmapRendererDrawable(c -> c.drawPicture(background)),
                new BitmapRendererDrawable(
                        c -> c.drawBitmap(fgBitmap, -shift, -shift, foregroundPaint)),
                new BitmapRendererDrawable(c -> c.drawPicture(badge)),
                mask);
    }

    @UiThread
    private static void initLayersOnUiThread(FolderIcon icon, int size,
            Canvas backgroundCanvas, Canvas foregroundCanvas, Canvas badgeCanvas) {
        icon.getPreviewBounds(sTmpRect);

        PreviewBackground bg = icon.getFolderBackground();

        // assume square
        assert (dragViewSize.x == dragViewSize.y);
        final int previewSize = sTmpRect.width();

        final int margin = (dragViewSize.x - previewSize) / 2;
        PreviewBackground bg = icon.getFolderBackground();
        final int margin = (size - previewSize) / 2;
        final float previewShiftX = -sTmpRect.left + margin;
        final float previewShiftY = -sTmpRect.top + margin;

        // Initialize badge, which consists of the outline stroke, shadow and dot; these
        // must be rendered above the foreground
        Bitmap badgeBmp = BitmapRenderer.createHardwareBitmap(dragViewSize.x, dragViewSize.y,
                (canvas) -> {
                    canvas.save();
                    canvas.translate(previewShiftX, previewShiftY);
                    bg.drawShadow(canvas);
                    bg.drawBackgroundStroke(canvas);
                    icon.drawDot(canvas);
                    canvas.restore();
                });

        // Initialize mask
        Path mask = new Path();
        Matrix m = new Matrix();
        m.setTranslate(previewShiftX, previewShiftY);
        bg.getClipPath().transform(m, mask);

        Bitmap previewBitmap = BitmapRenderer.createHardwareBitmap(dragViewSize.x, dragViewSize.y,
                (canvas) -> {
                    canvas.save();
                    canvas.translate(previewShiftX, previewShiftY);
                    icon.getPreviewItemManager().draw(canvas);
                    canvas.restore();
                });

        Bitmap bgBitmap = BitmapRenderer.createHardwareBitmap(dragViewSize.x, dragViewSize.y,
                (canvas) -> {
                    Paint p = new Paint();
                    p.setColor(bg.getBgColor());

                    canvas.drawCircle(dragViewSize.x / 2f, dragViewSize.y / 2f, bg.getRadius(), p);
                });

        ShiftedBitmapDrawable badge = new ShiftedBitmapDrawable(badgeBmp, 0, 0);
        ShiftedBitmapDrawable foreground = new ShiftedBitmapDrawable(previewBitmap, 0, 0);
        ShiftedBitmapDrawable background = new ShiftedBitmapDrawable(bgBitmap, 0, 0);

        return new FolderAdaptiveIcon(background, foreground, badge, mask);
        badgeCanvas.save();
        badgeCanvas.translate(previewShiftX, previewShiftY);
        icon.drawDot(badgeCanvas);
        badgeCanvas.restore();

        // Draw foreground
        foregroundCanvas.save();
        foregroundCanvas.translate(previewShiftX, previewShiftY);
        icon.getPreviewItemManager().draw(foregroundCanvas);
        foregroundCanvas.restore();

        // Draw background
        Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        backgroundPaint.setColor(bg.getBgColor());
        bg.drawShadow(backgroundCanvas);
        backgroundCanvas.drawCircle(size / 2f, size / 2f, bg.getRadius(), backgroundPaint);
        bg.drawBackgroundStroke(backgroundCanvas);
    }

    @Override
@@ -174,4 +204,52 @@ public class FolderAdaptiveIcon extends AdaptiveIconDrawable {
                    & mBadge.getChangingConfigurations();
        }
    }

    private static class BitmapRendererDrawable extends Drawable {

        private final BitmapRenderer mRenderer;

        BitmapRendererDrawable(BitmapRenderer renderer) {
            mRenderer = renderer;
        }

        @Override
        public void draw(Canvas canvas) {
            mRenderer.draw(canvas);
        }

        @Override
        public void setAlpha(int i) { }

        @Override
        public void setColorFilter(ColorFilter colorFilter) {  }

        @Override
        public int getOpacity() {
            return PixelFormat.TRANSLUCENT;
        }

        @Override
        public ConstantState getConstantState() {
            return new MyConstantState(mRenderer);
        }

        private static class MyConstantState extends ConstantState {
            private final BitmapRenderer mRenderer;

            MyConstantState(BitmapRenderer renderer) {
                mRenderer = renderer;
            }

            @Override
            public Drawable newDrawable() {
                return new BitmapRendererDrawable(mRenderer);
            }

            @Override
            public int getChangingConfigurations() {
                return 0;
            }
        }
    }
}
+0 −105
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.graphics;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;

/**
 * A simple drawable which draws a bitmap at a fixed position irrespective of the bounds
 */
public class ShiftedBitmapDrawable extends Drawable {

    private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
    private final Bitmap mBitmap;
    private float mShiftX;
    private float mShiftY;

    private final ConstantState mConstantState;

    public ShiftedBitmapDrawable(Bitmap bitmap, float shiftX, float shiftY) {
        mBitmap = bitmap;
        mShiftX = shiftX;
        mShiftY = shiftY;

        mConstantState = new MyConstantState(mBitmap, mShiftX, mShiftY);
    }

    public float getShiftX() {
        return mShiftX;
    }

    public float getShiftY() {
        return mShiftY;
    }

    public void setShiftX(float shiftX) {
        mShiftX = shiftX;
    }

    public void setShiftY(float shiftY) {
        mShiftY = shiftY;
    }

    @Override
    public void draw(Canvas canvas) {
        canvas.drawBitmap(mBitmap, mShiftX, mShiftY, mPaint);
    }

    @Override
    public void setAlpha(int i) { }

    @Override
    public void setColorFilter(ColorFilter colorFilter) {
        mPaint.setColorFilter(colorFilter);
    }

    @Override
    public int getOpacity() {
        return PixelFormat.TRANSLUCENT;
    }

    @Override
    public ConstantState getConstantState() {
        return mConstantState;
    }

    private static class MyConstantState extends ConstantState {
        private final Bitmap mBitmap;
        private float mShiftX;
        private float mShiftY;

        MyConstantState(Bitmap bitmap, float shiftX, float shiftY) {
            mBitmap = bitmap;
            mShiftX = shiftX;
            mShiftY = shiftY;
        }

        @Override
        public Drawable newDrawable() {
            return new ShiftedBitmapDrawable(mBitmap, mShiftX, mShiftY);
        }

        @Override
        public int getChangingConfigurations() {
            return 0;
        }
    }
}
 No newline at end of file
+1 −3
Original line number Diff line number Diff line
@@ -49,7 +49,6 @@ import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.graphics.PreloadIconDrawable;
import com.android.launcher3.icons.FastBitmapDrawable;
@@ -413,8 +412,7 @@ public class FloatingIconView extends FrameLayout implements
    @WorkerThread
    @SuppressWarnings("WrongThread")
    private static int getOffsetForIconBounds(Launcher l, Drawable drawable, RectF position) {
        if (!(drawable instanceof AdaptiveIconDrawable)
                || (drawable instanceof FolderAdaptiveIcon)) {
        if (!(drawable instanceof AdaptiveIconDrawable)) {
            return 0;
        }
        int blurSizeOutline =