diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index a089517b65..d1daac88de 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -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; @@ -398,15 +399,6 @@ public class Workspace extends PagedView 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. @@ -1506,10 +1498,10 @@ public class Workspace extends PagedView 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]; @@ -1535,9 +1527,18 @@ public class Workspace extends PagedView } } - 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; } @@ -2585,7 +2586,11 @@ public class Workspace extends PagedView } - 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); @@ -2597,7 +2602,7 @@ public class Workspace extends PagedView 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, @@ -2667,8 +2672,8 @@ public class Workspace extends PagedView 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]); diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java index c972cbbd7e..7bc9865162 100644 --- a/src/com/android/launcher3/dragndrop/AddItemActivity.java +++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java @@ -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 diff --git a/src/com/android/launcher3/dragndrop/AppWidgetHostViewDrawable.java b/src/com/android/launcher3/dragndrop/AppWidgetHostViewDrawable.java new file mode 100644 index 0000000000..477bc6e316 --- /dev/null +++ b/src/com/android/launcher3/dragndrop/AppWidgetHostViewDrawable.java @@ -0,0 +1,79 @@ +/* + * 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(); + } +} diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java index 93df599a81..b7a70cb654 100644 --- a/src/com/android/launcher3/dragndrop/DragController.java +++ b/src/com/android/launcher3/dragndrop/DragController.java @@ -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; diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java index 86b93d0aa3..df4d81197a 100644 --- a/src/com/android/launcher3/dragndrop/DragView.java +++ b/src/com/android/launcher3/dragndrop/DragView.java @@ -67,9 +67,9 @@ public class DragView extends View implements StateListener { 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 { * 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 { } }); - 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 { @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 { 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 { @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 { 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 { } } - 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 { // 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); diff --git a/src/com/android/launcher3/dragndrop/PinItemDragListener.java b/src/com/android/launcher3/dragndrop/PinItemDragListener.java index 2290473a81..9f12e6ec45 100644 --- a/src/com/android/launcher3/dragndrop/PinItemDragListener.java +++ b/src/com/android/launcher3/dragndrop/PinItemDragListener.java @@ -96,7 +96,7 @@ public class PinItemDragListener extends BaseItemDragListener { PendingItemDragHelper dragHelper = new PendingItemDragHelper(view); if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_APPWIDGET) { - dragHelper.setPreview(getPreview(mRequest)); + dragHelper.setRemoteViewsPreview(getPreview(mRequest)); } return dragHelper; } diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java index 21822a3542..9bc54442a7 100644 --- a/src/com/android/launcher3/graphics/DragPreviewProvider.java +++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java @@ -30,9 +30,11 @@ import android.graphics.drawable.Drawable; import android.view.View; import com.android.launcher3.BubbleTextView; +import com.android.launcher3.FastBitmapDrawable; import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.dragndrop.AppWidgetHostViewDrawable; import com.android.launcher3.dragndrop.DraggableView; import com.android.launcher3.icons.BitmapRenderer; import com.android.launcher3.util.SafeCloseable; @@ -88,10 +90,14 @@ public class DragPreviewProvider { } /** - * Returns a new bitmap to show when the {@link #mView} is being dragged around. - * Responsibility for the bitmap is transferred to the caller. + * Returns a new drawable to show when the {@link #mView} is being dragged around. + * Responsibility for the drawable is transferred to the caller. */ - public Bitmap createDragBitmap() { + public Drawable createDrawable() { + if (mView instanceof LauncherAppWidgetHostView) { + return new AppWidgetHostViewDrawable((LauncherAppWidgetHostView) mView); + } + int width = 0; int height = 0; // Assume scaleX == scaleY, which is always the case for workspace items. @@ -105,8 +111,9 @@ public class DragPreviewProvider { height = mView.getHeight(); } - return BitmapRenderer.createHardwareBitmap(width + blurSizeOutline, - height + blurSizeOutline, (c) -> drawDragView(c, scale)); + return new FastBitmapDrawable( + BitmapRenderer.createHardwareBitmap(width + blurSizeOutline, + height + blurSizeOutline, (c) -> drawDragView(c, scale))); } public final void generateDragOutline(Bitmap preview) { @@ -129,7 +136,7 @@ public class DragPreviewProvider { return bounds; } - public float getScaleAndPosition(Bitmap preview, int[] outPos) { + public float getScaleAndPosition(Drawable preview, int[] outPos) { float scale = Launcher.getLauncher(mView.getContext()) .getDragLayer().getLocationInDragLayer(mView, outPos); if (mView instanceof LauncherAppWidgetHostView) { @@ -139,8 +146,8 @@ public class DragPreviewProvider { } outPos[0] = Math.round(outPos[0] - - (preview.getWidth() - scale * mView.getWidth() * mView.getScaleX()) / 2); - outPos[1] = Math.round(outPos[1] - (1 - scale) * preview.getHeight() / 2 + (preview.getIntrinsicWidth() - scale * mView.getWidth() * mView.getScaleX()) / 2); + outPos[1] = Math.round(outPos[1] - (1 - scale) * preview.getIntrinsicHeight() / 2 - previewPadding / 2); return scale; } diff --git a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java index 3e59b612cf..530aaedefc 100644 --- a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java +++ b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java @@ -23,6 +23,7 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.view.View; +import com.android.launcher3.FastBitmapDrawable; import com.android.launcher3.Launcher; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; @@ -42,15 +43,16 @@ public class ShortcutDragPreviewProvider extends DragPreviewProvider { } @Override - public Bitmap createDragBitmap() { + public Drawable createDrawable() { if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) { int size = Launcher.getLauncher(mView.getContext()).getDeviceProfile().iconSizePx; - return BitmapRenderer.createHardwareBitmap( - size + blurSizeOutline, - size + blurSizeOutline, - (c) -> drawDragViewOnBackground(c, size)); + return new FastBitmapDrawable( + BitmapRenderer.createHardwareBitmap( + size + blurSizeOutline, + size + blurSizeOutline, + (c) -> drawDragViewOnBackground(c, size))); } else { - return createDragBitmapLegacy(); + return new FastBitmapDrawable(createDragBitmapLegacy()); } } @@ -81,7 +83,7 @@ public class ShortcutDragPreviewProvider extends DragPreviewProvider { } @Override - public float getScaleAndPosition(Bitmap preview, int[] outPos) { + public float getScaleAndPosition(Drawable preview, int[] outPos) { Launcher launcher = Launcher.getLauncher(mView.getContext()); int iconSize = getDrawableBounds(mView.getBackground()).width(); float scale = launcher.getDragLayer().getLocationInDragLayer(mView, outPos); @@ -91,9 +93,10 @@ public class ShortcutDragPreviewProvider extends DragPreviewProvider { iconLeft = mView.getWidth() - iconSize - iconLeft; } - outPos[0] += Math.round(scale * iconLeft + (scale * iconSize - preview.getWidth()) / 2 + - mPositionShift.x); - outPos[1] += Math.round((scale * mView.getHeight() - preview.getHeight()) / 2 + outPos[0] += Math.round( + scale * iconLeft + (scale * iconSize - preview.getIntrinsicWidth()) / 2 + + mPositionShift.x); + outPos[1] += Math.round((scale * mView.getHeight() - preview.getIntrinsicHeight()) / 2 + mPositionShift.y); float size = launcher.getDeviceProfile().iconSizePx; return scale * iconSize / size; diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java index 4fe631aa61..fc63af0c45 100644 --- a/src/com/android/launcher3/widget/BaseWidgetSheet.java +++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java @@ -108,17 +108,18 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView // If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and // we abort the drag. - if (image.getBitmap() == null) { + if (image.getDrawable() == null) { return false; } PendingItemDragHelper dragHelper = new PendingItemDragHelper(v); - dragHelper.setPreview(v.getPreview()); + dragHelper.setRemoteViewsPreview(v.getPreview()); + dragHelper.setAppWidgetHostViewPreview(v.getAppWidgetHostViewPreview()); int[] loc = new int[2]; getPopupContainer().getLocationInDragLayer(image, loc); - dragHelper.startDrag(image.getBitmapBounds(), image.getBitmap().getWidth(), + dragHelper.startDrag(image.getBitmapBounds(), image.getDrawable().getIntrinsicWidth(), image.getWidth(), new Point(loc[0], loc[1]), this, new DragOptions()); close(true); return true; diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java index 6e8383698d..8961f3642a 100644 --- a/src/com/android/launcher3/widget/PendingItemDragHelper.java +++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java @@ -29,10 +29,12 @@ import androidx.annotation.Nullable; import com.android.launcher3.DeviceProfile; import com.android.launcher3.DragSource; +import com.android.launcher3.FastBitmapDrawable; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.PendingAddItemInfo; import com.android.launcher3.R; +import com.android.launcher3.dragndrop.AppWidgetHostViewDrawable; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.dragndrop.DraggableView; import com.android.launcher3.graphics.DragPreviewProvider; @@ -49,15 +51,26 @@ public class PendingItemDragHelper extends DragPreviewProvider { private final PendingAddItemInfo mAddInfo; private int[] mEstimatedCellSize; - @Nullable private RemoteViews mPreview; + @Nullable private RemoteViews mRemoteViewsPreview; + @Nullable private LauncherAppWidgetHostView mAppWidgetHostViewPreview; public PendingItemDragHelper(View view) { super(view); mAddInfo = (PendingAddItemInfo) view.getTag(); } - public void setPreview(@Nullable RemoteViews preview) { - mPreview = preview; + /** + * Sets a {@link RemoteViews} which shows an app widget preview provided by app developers in + * the pin widget flow. + */ + public void setRemoteViewsPreview(@Nullable RemoteViews remoteViewsPreview) { + mRemoteViewsPreview = remoteViewsPreview; + } + + /** Sets a {@link LauncherAppWidgetHostView} which shows a preview layout of an app widget. */ + public void setAppWidgetHostViewPreview( + @Nullable LauncherAppWidgetHostView appWidgetHostViewPreview) { + mAppWidgetHostViewPreview = appWidgetHostViewPreview; } /** @@ -74,7 +87,7 @@ public class PendingItemDragHelper extends DragPreviewProvider { final Launcher launcher = Launcher.getLauncher(mView.getContext()); LauncherAppState app = LauncherAppState.getInstance(launcher); - Bitmap preview = null; + Drawable preview = null; final float scale; final Point dragOffset; final Rect dragRegion; @@ -90,13 +103,19 @@ public class PendingItemDragHelper extends DragPreviewProvider { int[] previewSizeBeforeScale = new int[1]; - if (mPreview != null) { - preview = WidgetCell.generateFromRemoteViews(launcher, mPreview, - createWidgetInfo.info, maxWidth, previewSizeBeforeScale); + if (mRemoteViewsPreview != null) { + preview = new FastBitmapDrawable( + WidgetCell.generateFromRemoteViews(launcher, mRemoteViewsPreview, + createWidgetInfo.info, maxWidth, previewSizeBeforeScale)); + } + if (mAppWidgetHostViewPreview != null) { + preview = new AppWidgetHostViewDrawable(mAppWidgetHostViewPreview); } if (preview == null) { - preview = app.getWidgetCache().generateWidgetPreview(launcher, - createWidgetInfo.info, maxWidth, null, previewSizeBeforeScale).first; + preview = new FastBitmapDrawable( + app.getWidgetCache().generateWidgetPreview(launcher, + createWidgetInfo.info, maxWidth, null, + previewSizeBeforeScale).first); } if (previewSizeBeforeScale[0] < previewBitmapWidth) { @@ -109,7 +128,7 @@ public class PendingItemDragHelper extends DragPreviewProvider { previewBounds.left += padding; previewBounds.right -= padding; } - scale = previewBounds.width() / (float) preview.getWidth(); + scale = previewBounds.width() / (float) preview.getIntrinsicWidth(); launcher.getDragController().addDragListener(new WidgetHostViewLoader(launcher, mView)); dragOffset = null; @@ -119,9 +138,10 @@ public class PendingItemDragHelper extends DragPreviewProvider { PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) mAddInfo; Drawable icon = createShortcutInfo.activityInfo.getFullResIcon(app.getIconCache()); LauncherIcons li = LauncherIcons.obtain(launcher); - preview = li.createScaledBitmapWithoutShadow(icon, 0); + preview = new FastBitmapDrawable( + li.createScaledBitmapWithoutShadow(icon, 0)); li.recycle(); - scale = ((float) launcher.getDeviceProfile().iconSizePx) / preview.getWidth(); + scale = ((float) launcher.getDeviceProfile().iconSizePx) / preview.getIntrinsicWidth(); dragOffset = new Point(previewPadding / 2, previewPadding / 2); @@ -149,9 +169,9 @@ public class PendingItemDragHelper extends DragPreviewProvider { launcher.getWorkspace().prepareDragWithProvider(this); int dragLayerX = screenPos.x + previewBounds.left - + (int) ((scale * preview.getWidth() - preview.getWidth()) / 2); + + (int) ((scale * preview.getIntrinsicWidth() - preview.getIntrinsicWidth()) / 2); int dragLayerY = screenPos.y + previewBounds.top - + (int) ((scale * preview.getHeight() - preview.getHeight()) / 2); + + (int) ((scale * preview.getIntrinsicHeight() - preview.getIntrinsicHeight()) / 2); // Start the drag launcher.getDragController().startDrag(preview, draggableView, dragLayerX, dragLayerY, diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java index 229df50288..5328041d16 100644 --- a/src/com/android/launcher3/widget/WidgetCell.java +++ b/src/com/android/launcher3/widget/WidgetCell.java @@ -18,10 +18,10 @@ package com.android.launcher3.widget; import static com.android.launcher3.Utilities.ATLEAST_S; -import android.appwidget.AppWidgetHostView; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; import android.os.CancellationSignal; import android.util.AttributeSet; import android.util.Log; @@ -35,11 +35,15 @@ import android.widget.LinearLayout; import android.widget.RemoteViews; import android.widget.TextView; +import androidx.annotation.Nullable; + import com.android.launcher3.BaseActivity; import com.android.launcher3.CheckLongPressHelper; import com.android.launcher3.DeviceProfile; +import com.android.launcher3.FastBitmapDrawable; import com.android.launcher3.R; import com.android.launcher3.WidgetPreviewLoader; +import com.android.launcher3.dragndrop.AppWidgetHostViewDrawable; import com.android.launcher3.icons.BaseIconFactory; import com.android.launcher3.icons.BitmapRenderer; import com.android.launcher3.model.WidgetItem; @@ -84,14 +88,14 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { private boolean mAnimatePreview = true; private boolean mApplyBitmapDeferred = false; - private Bitmap mDeferredBitmap; + private Drawable mDeferredDrawable; protected final BaseActivity mActivity; protected final DeviceProfile mDeviceProfile; private final CheckLongPressHelper mLongPressHelper; private RemoteViews mPreview; - private AppWidgetHostView mPreviewAppWidgetHostView; + private LauncherAppWidgetHostView mAppWidgetHostViewPreview; public WidgetCell(Context context) { this(context, null); @@ -147,7 +151,7 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { Log.d(TAG, "reset called on:" + mWidgetName.getText()); } mWidgetImage.animate().cancel(); - mWidgetImage.setBitmap(null, null); + mWidgetImage.setDrawable(null, null); mWidgetName.setText(null); mWidgetDims.setText(null); mWidgetDescription.setText(null); @@ -159,7 +163,7 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { mActiveRequest = null; } mPreview = null; - mPreviewAppWidgetHostView = null; + mAppWidgetHostViewPreview = null; } public void applyFromCellItem(WidgetItem item, WidgetPreviewLoader loader) { @@ -194,7 +198,7 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { && mPreview == null && item.widgetInfo != null && item.widgetInfo.previewLayout != Resources.ID_NULL) { - mPreviewAppWidgetHostView = new AppWidgetHostView(getContext()); + mAppWidgetHostViewPreview = new LauncherAppWidgetHostView(getContext()); LauncherAppWidgetProviderInfo launcherAppWidgetProviderInfo = LauncherAppWidgetProviderInfo.fromProviderInfo(getContext(), item.widgetInfo.clone()); @@ -202,11 +206,11 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { // rendering a preview layout for work profile apps yet. For non-work profile layout, a // proper solution is to use RemoteViews(PackageName, LayoutId). launcherAppWidgetProviderInfo.initialLayout = item.widgetInfo.previewLayout; - mPreviewAppWidgetHostView.setAppWidget(/* appWidgetId= */ -1, + mAppWidgetHostViewPreview.setAppWidget(/* appWidgetId= */ -1, launcherAppWidgetProviderInfo); - mPreviewAppWidgetHostView.setPadding(/* left= */ 0, /* top= */0, /* right= */ + mAppWidgetHostViewPreview.setPadding(/* left= */ 0, /* top= */0, /* right= */ 0, /* bottom= */ 0); - mPreviewAppWidgetHostView.updateAppWidget(/* remoteViews= */ null); + mAppWidgetHostViewPreview.updateAppWidget(/* remoteViews= */ null); } } @@ -214,6 +218,11 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { return mWidgetImage; } + @Nullable + public LauncherAppWidgetHostView getAppWidgetHostViewPreview() { + return mAppWidgetHostViewPreview; + } + /** * Sets if applying bitmap preview should be deferred. The UI will still load the bitmap, but * will not cause invalidate, so that when deferring is disabled later, all the bitmaps are @@ -223,9 +232,9 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { public void setApplyBitmapDeferred(boolean isDeferred) { if (mApplyBitmapDeferred != isDeferred) { mApplyBitmapDeferred = isDeferred; - if (!mApplyBitmapDeferred && mDeferredBitmap != null) { - applyPreview(mDeferredBitmap); - mDeferredBitmap = null; + if (!mApplyBitmapDeferred && mDeferredDrawable != null) { + applyPreview(mDeferredDrawable); + mDeferredDrawable = null; } } } @@ -235,17 +244,21 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { } public void applyPreview(Bitmap bitmap) { + applyPreview(new FastBitmapDrawable(bitmap)); + } + + private void applyPreview(Drawable drawable) { if (mApplyBitmapDeferred) { - mDeferredBitmap = bitmap; + mDeferredDrawable = drawable; return; } - if (bitmap != null) { + if (drawable != null) { LayoutParams layoutParams = (LayoutParams) mWidgetImage.getLayoutParams(); - layoutParams.width = bitmap.getWidth(); - layoutParams.height = bitmap.getHeight(); + layoutParams.width = drawable.getIntrinsicWidth(); + layoutParams.height = drawable.getIntrinsicHeight(); mWidgetImage.setLayoutParams(layoutParams); - mWidgetImage.setBitmap(bitmap, mWidgetPreviewLoader.getBadgeForUser(mItem.user, + mWidgetImage.setDrawable(drawable, mWidgetPreviewLoader.getBadgeForUser(mItem.user, BaseIconFactory.getBadgeSizeForIconSize(mDeviceProfile.allAppsIconSizePx))); if (mAnimatePreview) { mWidgetImage.setAlpha(0f); @@ -262,18 +275,26 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { Bitmap preview = generateFromRemoteViews( mActivity, mPreview, mItem.widgetInfo, mPresetPreviewSize, new int[1]); if (preview != null) { - applyPreview(preview); + applyPreview(new FastBitmapDrawable(preview)); return; } } - if (mPreviewAppWidgetHostView != null) { - Bitmap preview = generateFromView(mActivity, mPreviewAppWidgetHostView, - mItem.widgetInfo, mPreviewWidth, new int[1]); - if (preview != null) { - applyPreview(preview); - return; - } + if (mAppWidgetHostViewPreview != null) { + DeviceProfile dp = mActivity.getDeviceProfile(); + int viewWidth = dp.cellWidthPx * mItem.spanX; + int viewHeight = dp.cellHeightPx * mItem.spanY; + + mAppWidgetHostViewPreview.measure( + MeasureSpec.makeMeasureSpec(viewWidth, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(viewHeight, MeasureSpec.EXACTLY)); + + viewWidth = mAppWidgetHostViewPreview.getMeasuredWidth(); + viewHeight = mAppWidgetHostViewPreview.getMeasuredHeight(); + mAppWidgetHostViewPreview.layout(0, 0, viewWidth, viewHeight); + Drawable drawable = new AppWidgetHostViewDrawable(mAppWidgetHostViewPreview); + applyPreview(drawable); + return; } if (mActiveRequest != null) { return; diff --git a/src/com/android/launcher3/widget/WidgetImageView.java b/src/com/android/launcher3/widget/WidgetImageView.java index df2bcffc16..39d701ce5b 100644 --- a/src/com/android/launcher3/widget/WidgetImageView.java +++ b/src/com/android/launcher3/widget/WidgetImageView.java @@ -17,9 +17,7 @@ package com.android.launcher3.widget; import android.content.Context; -import android.graphics.Bitmap; import android.graphics.Canvas; -import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; @@ -35,11 +33,10 @@ import com.android.launcher3.Utilities; */ public class WidgetImageView extends View { - private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); private final RectF mDstRectF = new RectF(); private final int mBadgeMargin; - private Bitmap mBitmap; + private Drawable mDrawable; private Drawable mBadge; public WidgetImageView(Context context) { @@ -57,21 +54,22 @@ public class WidgetImageView extends View { .getDimensionPixelSize(R.dimen.profile_badge_margin); } - public void setBitmap(Bitmap bitmap, Drawable badge) { - mBitmap = bitmap; + public void setDrawable(Drawable drawable, Drawable badge) { + mDrawable = drawable; mBadge = badge; invalidate(); } - public Bitmap getBitmap() { - return mBitmap; + public Drawable getDrawable() { + return mDrawable; } @Override protected void onDraw(Canvas canvas) { - if (mBitmap != null) { + if (mDrawable != null) { updateDstRectF(); - canvas.drawBitmap(mBitmap, null, mDstRectF, mPaint); + mDrawable.setBounds(getBitmapBounds()); + mDrawable.draw(canvas); // Only draw the badge if a preview was drawn. if (mBadge != null) { @@ -91,11 +89,11 @@ public class WidgetImageView extends View { private void updateDstRectF() { float myWidth = getWidth(); float myHeight = getHeight(); - float bitmapWidth = mBitmap.getWidth(); + float bitmapWidth = mDrawable.getIntrinsicWidth(); final float scale = bitmapWidth > myWidth ? myWidth / bitmapWidth : 1; float scaledWidth = bitmapWidth * scale; - float scaledHeight = mBitmap.getHeight() * scale; + float scaledHeight = mDrawable.getIntrinsicHeight() * scale; mDstRectF.left = (myWidth - scaledWidth) / 2; mDstRectF.right = (myWidth + scaledWidth) / 2;