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

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

Adding spring effect for folder icons when dragging similar to adaptive icons

Bug: 63889144
Change-Id: Idd1fd13c6343bf3e0a3a0e5ff0be730d41996575
parent 4560d2f7
Loading
Loading
Loading
Loading
+11 −9
Original line number Diff line number Diff line
@@ -1465,21 +1465,23 @@ public class Launcher extends BaseActivity
            mWorkspace.addInScreen(view, info);
        } else {
            // Adding a shortcut to a Folder.
            final long folderIconId = container;
            FolderIcon folderIcon = (FolderIcon) mWorkspace.getFirstMatch(new ItemOperator() {
                @Override
                public boolean evaluate(ItemInfo info, View view) {
                    return info != null && info.id == folderIconId;
                }
            });

            FolderIcon folderIcon = findFolderIcon(container);
            if (folderIcon != null) {
                FolderInfo folderInfo = (FolderInfo) folderIcon.getTag();
                folderInfo.add(info, args.rank, false);
            } else {
                Log.e(TAG, "Could not find folder with id " + folderIconId + " to add shortcut.");
                Log.e(TAG, "Could not find folder with id " + container + " to add shortcut.");
            }
        }
    }

    public FolderIcon findFolderIcon(final long folderIconId) {
        return (FolderIcon) mWorkspace.getFirstMatch(new ItemOperator() {
            @Override
            public boolean evaluate(ItemInfo info, View view) {
                return info != null && info.id == folderIconId;
            }
        });
    }

    /**
+37 −18
Original line number Diff line number Diff line
@@ -78,6 +78,7 @@ public class DragView extends View {

    @Thunk static float sDragAlpha = 1f;

    private boolean mDrawBitmap = true;
    private Bitmap mBitmap;
    private Bitmap mCrossFadeBitmap;
    @Thunk Paint mPaint;
@@ -187,7 +188,8 @@ public class DragView extends View {
    }

    /**
     * Initialize {@code #mIconDrawable} only if the icon type is app icon (not shortcut or folder).
     * Initialize {@code #mIconDrawable} if the item can be represented using
     * an {@link AdaptiveIconDrawable} or {@link FolderAdaptiveIcon}.
     */
    @TargetApi(Build.VERSION_CODES.O)
    public void setItemInfo(final ItemInfo info) {
@@ -195,7 +197,8 @@ public class DragView extends View {
            return;
        }
        if (info.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
                info.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
                info.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT &&
                info.itemType != LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
            return;
        }
        // Load the adaptive icon on a background thread and add the view in ui thread.
@@ -205,7 +208,7 @@ public class DragView extends View {
            public void run() {
                LauncherAppState appState = LauncherAppState.getInstance(mLauncher);
                Object[] outObj = new Object[1];
                Drawable dr = getFullDrawable(info, appState, outObj);
                final Drawable dr = getFullDrawable(info, appState, outObj);

                if (dr instanceof AdaptiveIconDrawable) {
                    int w = mBitmap.getWidth();
@@ -249,6 +252,9 @@ public class DragView extends View {
                            // Assign the variable on the UI thread to avoid race conditions.
                            mScaledMaskPath = mask;

                            // Do not draw the background in case of folder as its translucent
                            mDrawBitmap = !(dr instanceof FolderAdaptiveIcon);

                            if (info.isDisabled()) {
                                FastBitmapDrawable d = new FastBitmapDrawable(null);
                                d.setIsDisabled(true);
@@ -323,6 +329,14 @@ public class DragView extends View {
                return sm.getShortcutIconDrawable(si.get(0),
                        appState.getInvariantDeviceProfile().fillResIconDpi);
            }
        } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
            FolderAdaptiveIcon icon =  FolderAdaptiveIcon.createFolderAdaptiveIcon(
                    mLauncher, info.id, new Point(mBitmap.getWidth(), mBitmap.getHeight()));
            if (icon == null) {
                return null;
            }
            outObj[0] = icon;
            return icon;
        } else {
            return null;
        }
@@ -350,6 +364,8 @@ public class DragView extends View {
            float insetFraction = (iconSize - badgeSize) / iconSize;
            return new InsetDrawable(new FastBitmapDrawable(badge),
                    insetFraction, insetFraction, 0, 0);
        } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
            return ((FolderAdaptiveIcon) obj).getBadge();
        } else {
            return mLauncher.getPackageManager()
                    .getUserBadgedIcon(new FixedSizeEmptyDrawable(iconSize), info.user);
@@ -405,6 +421,8 @@ public class DragView extends View {
    @Override
    protected void onDraw(Canvas canvas) {
        mHasDrawn = true;

        if (mDrawBitmap) {
            // Always draw the bitmap to mask anti aliasing due to clipPath
            boolean crossFade = mCrossFadeProgress > 0 && mCrossFadeBitmap != null;
            if (crossFade) {
@@ -421,6 +439,7 @@ public class DragView extends View {
                canvas.drawBitmap(mCrossFadeBitmap, 0.0f, 0.0f, mPaint);
                canvas.restoreToCount(saveCount);
            }
        }

        if (mScaledMaskPath != null) {
            int cnt = canvas.save();
+175 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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.dragndrop;

import android.annotation.TargetApi;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.Log;

import com.android.launcher3.Launcher;
import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.R;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.folder.PreviewBackground;
import com.android.launcher3.util.Preconditions;

import java.util.concurrent.Callable;

/**
 * {@link AdaptiveIconDrawable} representation of a {@link FolderIcon}
 */
@TargetApi(Build.VERSION_CODES.O)
public class FolderAdaptiveIcon extends AdaptiveIconDrawable {
    private static final String TAG = "FolderAdaptiveIcon";

    private final Drawable mBadge;
    private final Path mMask;

    private FolderAdaptiveIcon(Drawable bg, Drawable fg, Drawable badge, Path mask) {
        super(bg, fg);
        mBadge = badge;
        mMask = mask;
    }

    @Override
    public Path getIconMask() {
        return mMask;
    }

    public Drawable getBadge() {
        return mBadge;
    }

    public static FolderAdaptiveIcon createFolderAdaptiveIcon(
            final Launcher launcher, final long folderId, Point dragViewSize) {
        Preconditions.assertNonUiThread();
        int margin = launcher.getResources()
                .getDimensionPixelSize(R.dimen.blur_size_medium_outline);

        // Allocate various bitmaps on the background thread, because why not!
        final Bitmap badge = Bitmap.createBitmap(
                dragViewSize.x - margin, dragViewSize.y - margin, Bitmap.Config.ARGB_8888);

        // The bitmap for the preview is generated larger than needed to allow for the spring effect
        float sizeScaleFactor = 1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction();
        final Bitmap preview = Bitmap.createBitmap(
                (int) (dragViewSize.x * sizeScaleFactor), (int) (dragViewSize.y * sizeScaleFactor),
                Bitmap.Config.ARGB_8888);

        // Create the actual drawable on the UI thread to avoid race conditions with
        // FolderIcon draw pass
        try {
            return new MainThreadExecutor().submit(new Callable<FolderAdaptiveIcon>() {
                @Override
                public FolderAdaptiveIcon call() throws Exception {
                    FolderIcon icon = launcher.findFolderIcon(folderId);
                    return icon == null ? null : createDrawableOnUiThread(icon, badge, preview);
                }
            }).get();
        } catch (Exception e) {
            Log.e(TAG, "Unable to create folder icon", e);
            return null;
        }
    }

    /**
     * Initializes various bitmaps on the UI thread and returns the final drawable.
     */
    private static FolderAdaptiveIcon createDrawableOnUiThread(FolderIcon icon,
            Bitmap badgeBitmap, Bitmap previewBitmap) {
        Preconditions.assertUIThread();
        float margin = icon.getResources().getDimension(R.dimen.blur_size_medium_outline) / 2;

        Canvas c = new Canvas();
        PreviewBackground bg = icon.getFolderBackground();

        // Initialize badge
        c.setBitmap(badgeBitmap);
        bg.drawShadow(c);
        bg.drawBackgroundStroke(c);
        icon.drawBadge(c);

        // Initialize preview
        float shiftFactor = AdaptiveIconDrawable.getExtraInsetFraction() /
                (1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction());
        float previewShiftX = shiftFactor * previewBitmap.getWidth();
        float previewShiftY = shiftFactor * previewBitmap.getHeight();

        c.setBitmap(previewBitmap);
        c.translate(previewShiftX, previewShiftY);
        icon.getPreviewItemManager().draw(c);
        c.setBitmap(null);

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

        ShiftedBitmapDrawable badge = new ShiftedBitmapDrawable(badgeBitmap, margin, margin);
        ShiftedBitmapDrawable foreground = new ShiftedBitmapDrawable(previewBitmap,
                margin - previewShiftX, margin - previewShiftY);

        return new FolderAdaptiveIcon(new ColorDrawable(bg.getBgColor()), foreground, badge, mask);
    }

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

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

        ShiftedBitmapDrawable(Bitmap bitmap, float shiftX, float shiftY) {
            mBitmap = bitmap;
            mShiftX = shiftX;
            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;
        }
    }
}
+13 −4
Original line number Diff line number Diff line
@@ -440,6 +440,14 @@ public class FolderIcon extends FrameLayout implements FolderListener {
        invalidate();
    }

    public PreviewBackground getFolderBackground() {
        return mBackground;
    }

    public PreviewItemManager getPreviewItemManager() {
        return mPreviewItemManager;
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
@@ -463,14 +471,11 @@ public class FolderIcon extends FrameLayout implements FolderListener {
        } else {
            saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
            if (mPreviewLayoutRule.clipToBackground()) {
                mBackground.clipCanvasSoftware(canvas, Region.Op.INTERSECT);
                canvas.clipPath(mBackground.getClipPath(), Region.Op.INTERSECT);
            }
        }

        // The items are drawn in coordinates relative to the preview offset
        canvas.translate(mBackground.basePreviewOffsetX, mBackground.basePreviewOffsetY);
        mPreviewItemManager.draw(canvas);
        canvas.translate(-mBackground.basePreviewOffsetX, -mBackground.basePreviewOffsetY);

        if (mPreviewLayoutRule.clipToBackground() && canvas.isHardwareAccelerated()) {
            mBackground.clipCanvasHardware(canvas);
@@ -481,6 +486,10 @@ public class FolderIcon extends FrameLayout implements FolderListener {
            mBackground.drawBackgroundStroke(canvas);
        }

        drawBadge(canvas);
    }

    public void drawBadge(Canvas canvas) {
        if ((mBadgeInfo != null && mBadgeInfo.hasBadge()) || mBadgeScale > 0) {
            int offsetX = mBackground.getOffsetX();
            int offsetY = mBackground.getOffsetY();
+15 −7
Original line number Diff line number Diff line
@@ -195,19 +195,28 @@ public class PreviewBackground {
        invalidate();
    }

    public int getBgColor() {
        int alpha = (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
        return ColorUtils.setAlphaComponent(mBgColor, alpha);
    }

    public void drawBackground(Canvas canvas) {
        mPaint.setStyle(Paint.Style.FILL);
        int alpha = (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
        mPaint.setColor(ColorUtils.setAlphaComponent(mBgColor, alpha));
        mPaint.setColor(getBgColor());

        drawCircle(canvas, 0 /* deltaRadius */);

        // Draw shadow.
        drawShadow(canvas);
    }

    public void drawShadow(Canvas canvas) {
        if (mShadowShader == null) {
            return;
        }

        float radius = getScaledRadius();
        float shadowRadius = radius + mStrokeWidth;
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(Color.BLACK);
        int offsetX = getOffsetX();
        int offsetY = getOffsetY();
@@ -219,7 +228,7 @@ public class PreviewBackground {

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

        mShaderMatrix.setScale(shadowRadius, shadowRadius);
@@ -295,12 +304,11 @@ public class PreviewBackground {
                radius - deltaRadius, mPaint);
    }

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

    // It is the callers responsibility to save and restore the canvas layers.
Loading