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

Commit 0226d1ad authored by Steven Ng's avatar Steven Ng
Browse files

Replace DragView bitmap with drawable

This is a preliminary work for local color extraction. In order to
apply local colors extracted from wallpaper to AppWidgetHostView
during drag, we need to hold a reference of the dragging
AppWidgetHostView.

In this CL, the following changes are made:
1. Instead of using bitmap image directly for icons, folders, shortcuts,
   legacy widget drawable preview, a BitmapDrawable wrapper is
    introduced.
2. Introduce a WidgetHostViewDraggableDrawable which draws
   LauncherAppWidgetHostView directly to canvas. No more bitmap
   generation overhead.
3. Remove drag outline from the drag logic because this will be replaced
   by a new grid color hint UI: https://screenshot.googleplex.com/7jBEVeuxFecFKKT.png

Test: Add: add widgets, shortcuts from widgets tray.
           add icons from all apps.
           create folder.
      Drag: drag existing widgets, shortcuts, folders and icons around

Bug: 182282587
Change-Id: Ia45ff756ea5bb80cf0761f0727a9453d50c204c0
parent 976bd24f
Loading
Loading
Loading
Loading
+24 −19
Original line number Diff line number Diff line
@@ -69,6 +69,7 @@ import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dot.FolderDotInfo;
import com.android.launcher3.dragndrop.AppWidgetHostViewDrawable;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragOptions;
@@ -396,15 +397,6 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
            layout.markCellsAsUnoccupiedForView(mDragInfo.cell);
        }

        if (mOutlineProvider != null) {
            if (dragObject.dragView != null) {
                Bitmap preview = dragObject.dragView.getPreviewBitmap();

                // The outline is used to visualize where the item will land if dropped
                mOutlineProvider.generateDragOutline(preview);
            }
        }

        updateChildrenLayersEnabled();

        // Do not add a new page if it is a accessible drag which was not started by the workspace.
@@ -1504,10 +1496,10 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
            draggableView = (DraggableView) child;
        }

        // The drag bitmap follows the touch point around on the screen
        final Bitmap b = previewProvider.createDragBitmap();
        // The draggable drawable follows the touch point around on the screen
        final Drawable drawable = previewProvider.createDrawable();
        int halfPadding = previewProvider.previewPadding / 2;
        float scale = previewProvider.getScaleAndPosition(b, mTempXY);
        float scale = previewProvider.getScaleAndPosition(drawable, mTempXY);
        int dragLayerX = mTempXY[0];
        int dragLayerY = mTempXY[1];

@@ -1533,9 +1525,18 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
            }
        }

        DragView dv = mDragController.startDrag(b, draggableView, dragLayerX, dragLayerY, source,
                dragObject, dragVisualizeOffset, dragRect, scale * iconScale,
                scale, dragOptions);
        DragView dv = mDragController.startDrag(
                drawable,
                draggableView,
                dragLayerX,
                dragLayerY,
                source,
                dragObject,
                dragVisualizeOffset,
                dragRect,
                scale * iconScale,
                scale,
                dragOptions);
        dv.setIntrinsicIconScaleFactor(dragOptions.intrinsicIconScaleFactor);
        return dv;
    }
@@ -2583,7 +2584,11 @@ public class Workspace extends PagedView<WorkspacePageIndicator>

    }

    public Bitmap createWidgetBitmap(ItemInfo widgetInfo, View layout) {
    private Drawable createWidgetDrawable(ItemInfo widgetInfo, View layout) {
        if (layout instanceof LauncherAppWidgetHostView) {
            return new AppWidgetHostViewDrawable((LauncherAppWidgetHostView) layout);
        }

        int[] unScaledSize = estimateItemSize(widgetInfo);
        int visibility = layout.getVisibility();
        layout.setVisibility(VISIBLE);
@@ -2595,7 +2600,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
        Bitmap b = BitmapRenderer.createHardwareBitmap(
                unScaledSize[0], unScaledSize[1], layout::draw);
        layout.setVisibility(visibility);
        return b;
        return new FastBitmapDrawable(b);
    }

    private void getFinalPositionForDropAnimation(int[] loc, float[] scaleXY,
@@ -2665,8 +2670,8 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
        boolean isWidget = info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET ||
                info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
        if ((animationType == ANIMATE_INTO_POSITION_AND_RESIZE || external) && finalView != null) {
            Bitmap crossFadeBitmap = createWidgetBitmap(info, finalView);
            dragView.setCrossFadeBitmap(crossFadeBitmap);
            Drawable crossFadeDrawable = createWidgetDrawable(info, finalView);
            dragView.setCrossFadeDrawable(crossFadeDrawable);
            dragView.crossFade((int) (duration * 0.8f));
        } else if (isWidget && external) {
            scaleXY[0] = scaleXY[1] = Math.min(scaleXY[0],  scaleXY[1]);
+2 −2
Original line number Diff line number Diff line
@@ -142,7 +142,7 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener

        // If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and
        // we abort the drag.
        if (img.getBitmap() == null) {
        if (img.getDrawable() == null) {
            return false;
        }

@@ -151,7 +151,7 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener

        // Start home and pass the draw request params
        PinItemDragListener listener = new PinItemDragListener(mRequest, bounds,
                img.getBitmap().getWidth(), img.getWidth());
                img.getDrawable().getIntrinsicWidth(), img.getWidth());


        // Start a system drag and drop. We use a transparent bitmap as preview for system drag
+79 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;

import com.android.launcher3.widget.LauncherAppWidgetHostView;

/** A drawable which renders {@link LauncherAppWidgetHostView} to a canvas. */
public final class AppWidgetHostViewDrawable extends Drawable {

    private final LauncherAppWidgetHostView mAppWidgetHostView;
    private Paint mPaint = new Paint();

    public AppWidgetHostViewDrawable(LauncherAppWidgetHostView appWidgetHostView) {
        mAppWidgetHostView = appWidgetHostView;
    }

    @Override
    public void draw(Canvas canvas) {
        int saveCount = canvas.saveLayer(0, 0, getIntrinsicWidth(), getIntrinsicHeight(), mPaint);
        mAppWidgetHostView.draw(canvas);
        canvas.restoreToCount(saveCount);
    }

    @Override
    public int getIntrinsicWidth() {
        return mAppWidgetHostView.getMeasuredWidth();
    }

    @Override
    public int getIntrinsicHeight() {
        return mAppWidgetHostView.getMeasuredHeight();
    }

    @Override
    public int getOpacity() {
        // This is up to app widget provider. We don't know if the host view will cover anything
        // behind the drawable.
        return PixelFormat.UNKNOWN;
    }

    @Override
    public void setAlpha(int alpha) {
        mPaint.setAlpha(alpha);
    }

    @Override
    public int getAlpha() {
        return mPaint.getAlpha();
    }

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

    @Override
    public ColorFilter getColorFilter() {
        return mPaint.getColorFilter();
    }
}
+22 −7
Original line number Diff line number Diff line
@@ -25,9 +25,9 @@ import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.animation.ValueAnimator;
import android.content.ComponentName;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.DragEvent;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
@@ -129,7 +129,7 @@ public class DragController implements DragDriver.EventListener, TouchController
     * drop, it is the responsibility of the {@link DropTarget} to exit out of the spring loaded
     * mode. If the drop was cancelled for some reason, the UI will automatically exit out of this mode.
     *
     * @param b The bitmap to display as the drag image.  It will be re-scaled to the
     * @param drawable The drawable to be displayed in the drag view.  It will be re-scaled to the
     *          enlarged size.
     * @param originalView The source view (ie. icon, widget etc.) that is being dragged
     *          and which the DragView represents
@@ -140,9 +140,18 @@ public class DragController implements DragDriver.EventListener, TouchController
     * @param dragRegion Coordinates within the bitmap b for the position of item being dragged.
     *          Makes dragging feel more precise, e.g. you can clip out a transparent border
     */
    public DragView startDrag(Bitmap b, DraggableView originalView, int dragLayerX, int dragLayerY,
            DragSource source, ItemInfo dragInfo, Point dragOffset, Rect dragRegion,
            float initialDragViewScale, float dragViewScaleOnDrop, DragOptions options) {
    public DragView startDrag(
            Drawable drawable,
            DraggableView originalView,
            int dragLayerX,
            int dragLayerY,
            DragSource source,
            ItemInfo dragInfo,
            Point dragOffset,
            Rect dragRegion,
            float initialDragViewScale,
            float dragViewScaleOnDrop,
            DragOptions options) {
        if (PROFILE_DRAWING_DURING_DRAG) {
            android.os.Debug.startMethodTracing("Launcher");
        }
@@ -173,8 +182,14 @@ public class DragController implements DragDriver.EventListener, TouchController
        final Resources res = mLauncher.getResources();
        final float scaleDps = mIsInPreDrag
                ? res.getDimensionPixelSize(R.dimen.pre_drag_view_scale) : 0f;
        final DragView dragView = mDragObject.dragView = new DragView(mLauncher, b, registrationX,
                registrationY, initialDragViewScale, dragViewScaleOnDrop, scaleDps);
        final DragView dragView = mDragObject.dragView = new DragView(
                mLauncher,
                drawable,
                registrationX,
                registrationY,
                initialDragViewScale,
                dragViewScaleOnDrop,
                scaleDps);
        dragView.setItemInfo(dragInfo);
        mDragObject.dragComplete = false;

+39 −28
Original line number Diff line number Diff line
@@ -67,9 +67,9 @@ public class DragView extends View implements StateListener<LauncherState> {
    public static final int COLOR_CHANGE_DURATION = 120;
    public static final int VIEW_ZOOM_DURATION = 150;

    private boolean mDrawBitmap = true;
    private Bitmap mBitmap;
    private Bitmap mCrossFadeBitmap;
    private boolean mShouldDraw = true;
    private Drawable mDrawable;
    private Drawable mCrossFadeDrawable;
    @Thunk Paint mPaint;
    private final int mBlurSizeOutline;
    private final int mRegistrationX;
@@ -114,19 +114,21 @@ public class DragView extends View implements StateListener<LauncherState> {
     * The registration point is the point inside our view that the touch events should
     * be centered upon.
     * @param launcher The Launcher instance
     * @param bitmap The view that we're dragging around.  We scale it up when we draw it.
     * @param drawable The view that we're dragging around.  We scale it up when we draw it.
     * @param registrationX The x coordinate of the registration point.
     * @param registrationY The y coordinate of the registration point.
     */
    public DragView(Launcher launcher, Bitmap bitmap, int registrationX, int registrationY,
                    final float initialScale, final float scaleOnDrop, final float finalScaleDps) {
    public DragView(Launcher launcher, Drawable drawable, int registrationX,
            int registrationY, final float initialScale, final float scaleOnDrop,
            final float finalScaleDps) {
        super(launcher);
        mLauncher = launcher;
        mDragLayer = launcher.getDragLayer();
        mDragController = launcher.getDragController();
        mFirstFrameAnimatorHelper = new FirstFrameAnimatorHelper(this);

        final float scale = (bitmap.getWidth() + finalScaleDps) / bitmap.getWidth();
        final float scale = (drawable.getIntrinsicWidth() + finalScaleDps)
                / drawable.getIntrinsicWidth();

        // Set the initial scale to avoid any jumps
        setScaleX(initialScale);
@@ -144,8 +146,9 @@ public class DragView extends View implements StateListener<LauncherState> {
            }
        });

        mBitmap = bitmap;
        setDragRegion(new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()));
        mDrawable = drawable;
        setDragRegion(new Rect(0, 0, drawable.getIntrinsicWidth(),
                drawable.getIntrinsicHeight()));

        // The point in our scaled bitmap that the touch events are located
        mRegistrationX = registrationX;
@@ -197,8 +200,8 @@ public class DragView extends View implements StateListener<LauncherState> {
            @Override
            public void run() {
                Object[] outObj = new Object[1];
                int w = mBitmap.getWidth();
                int h = mBitmap.getHeight();
                int w = mDrawable.getIntrinsicWidth();
                int h = mDrawable.getIntrinsicHeight();
                Drawable dr = Utilities.getFullDrawable(mLauncher, info, w, h, outObj);

                if (dr instanceof AdaptiveIconDrawable) {
@@ -214,11 +217,11 @@ public class DragView extends View implements StateListener<LauncherState> {
                    mBadge.setBounds(badgeBounds);

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

                    try (LauncherIcons li = LauncherIcons.obtain(mLauncher)) {
                        Drawable nDr; // drawable to be normalized
                        if (mDrawBitmap) {
                        if (mShouldDraw) {
                            nDr = dr;
                        } else {
                            // Since we just want the scale, avoid heavy drawing operations
@@ -308,7 +311,7 @@ public class DragView extends View implements StateListener<LauncherState> {

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(mBitmap.getWidth(), mBitmap.getHeight());
        setMeasuredDimension(mDrawable.getIntrinsicWidth(), mDrawable.getIntrinsicHeight());
    }

    /** Sets the scale of the view over the normal workspace icon size. */
@@ -352,29 +355,37 @@ public class DragView extends View implements StateListener<LauncherState> {
        return mDragRegion;
    }

    public Bitmap getPreviewBitmap() {
        return mBitmap;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        mHasDrawn = true;

        if (mDrawBitmap) {
        if (mShouldDraw) {
            // Always draw the bitmap to mask anti aliasing due to clipPath
            boolean crossFade = mCrossFadeProgress > 0 && mCrossFadeBitmap != null;
            boolean crossFade = mCrossFadeProgress > 0 && mCrossFadeDrawable != null;
            if (crossFade) {
                int alpha = crossFade ? (int) (255 * (1 - mCrossFadeProgress)) : 255;
                mPaint.setAlpha(alpha);
            }
            canvas.drawBitmap(mBitmap, 0.0f, 0.0f, mPaint);
            mDrawable.setColorFilter(mPaint.getColorFilter());
            mDrawable.setAlpha(mPaint.getAlpha());
            mDrawable.setBounds(
                    new Rect(0, 0, mDrawable.getIntrinsicWidth(),
                            mDrawable.getIntrinsicHeight()));
            mDrawable.draw(canvas);
            if (crossFade) {
                mPaint.setAlpha((int) (255 * mCrossFadeProgress));
                final int saveCount = canvas.save();
                float sX = (mBitmap.getWidth() * 1.0f) / mCrossFadeBitmap.getWidth();
                float sY = (mBitmap.getHeight() * 1.0f) / mCrossFadeBitmap.getHeight();
                float sX = ((float) mDrawable.getIntrinsicWidth())
                        / mCrossFadeDrawable.getIntrinsicWidth();
                float sY = ((float) mDrawable.getIntrinsicHeight())
                        / mCrossFadeDrawable.getIntrinsicHeight();
                canvas.scale(sX, sY);
                canvas.drawBitmap(mCrossFadeBitmap, 0.0f, 0.0f, mPaint);
                mCrossFadeDrawable.setColorFilter(mPaint.getColorFilter());
                mCrossFadeDrawable.setAlpha(mPaint.getAlpha());
                mDrawable.setBounds(
                        new Rect(0, 0, mDrawable.getIntrinsicWidth(),
                                mDrawable.getIntrinsicHeight()));
                mCrossFadeDrawable.draw(canvas);
                canvas.restoreToCount(saveCount);
            }
        }
@@ -390,8 +401,8 @@ public class DragView extends View implements StateListener<LauncherState> {
        }
    }

    public void setCrossFadeBitmap(Bitmap crossFadeBitmap) {
        mCrossFadeBitmap = crossFadeBitmap;
    public void setCrossFadeDrawable(Drawable crossFadeDrawable) {
        mCrossFadeDrawable = crossFadeDrawable;
    }

    public void crossFade(int duration) {
@@ -469,8 +480,8 @@ public class DragView extends View implements StateListener<LauncherState> {

        // Start the pick-up animation
        DragLayer.LayoutParams lp = new DragLayer.LayoutParams(0, 0);
        lp.width = mBitmap.getWidth();
        lp.height = mBitmap.getHeight();
        lp.width = mDrawable.getIntrinsicWidth();
        lp.height = mDrawable.getIntrinsicHeight();
        lp.customPosition = true;
        setLayoutParams(lp);
        move(touchX, touchY);
Loading