1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.launcher3.widget; 18 19 import android.graphics.Bitmap; 20 import android.graphics.Canvas; 21 import android.graphics.Paint; 22 import android.graphics.Point; 23 import android.graphics.Rect; 24 import android.graphics.drawable.Drawable; 25 import android.util.Log; 26 import android.util.Size; 27 import android.view.View; 28 import android.view.View.MeasureSpec; 29 import android.widget.RemoteViews; 30 31 import androidx.annotation.Nullable; 32 33 import com.android.launcher3.DeviceProfile; 34 import com.android.launcher3.DragSource; 35 import com.android.launcher3.Launcher; 36 import com.android.launcher3.LauncherAppState; 37 import com.android.launcher3.PendingAddItemInfo; 38 import com.android.launcher3.R; 39 import com.android.launcher3.dragndrop.DragOptions; 40 import com.android.launcher3.dragndrop.DraggableView; 41 import com.android.launcher3.graphics.DragPreviewProvider; 42 import com.android.launcher3.icons.FastBitmapDrawable; 43 import com.android.launcher3.icons.LauncherIcons; 44 import com.android.launcher3.icons.RoundDrawableWrapper; 45 import com.android.launcher3.testing.TestProtocol; 46 import com.android.launcher3.widget.dragndrop.AppWidgetHostViewDragListener; 47 import com.android.launcher3.widget.util.WidgetSizes; 48 49 /** 50 * Extension of {@link DragPreviewProvider} with logic specific to pending widgets/shortcuts 51 * dragged from the widget tray. 52 */ 53 public class PendingItemDragHelper extends DragPreviewProvider { 54 55 private static final float MAX_WIDGET_SCALE = 1.25f; 56 57 private final PendingAddItemInfo mAddInfo; 58 private int[] mEstimatedCellSize; 59 60 @Nullable private RemoteViews mRemoteViewsPreview; 61 private float mRemoteViewsPreviewScale = 1f; 62 @Nullable private NavigableAppWidgetHostView mAppWidgetHostViewPreview; 63 private final float mEnforcedRoundedCornersForWidget; 64 PendingItemDragHelper(View view)65 public PendingItemDragHelper(View view) { 66 super(view); 67 mAddInfo = (PendingAddItemInfo) view.getTag(); 68 mEnforcedRoundedCornersForWidget = RoundedCornerEnforcement.computeEnforcedRadius( 69 view.getContext()); 70 } 71 72 /** 73 * Sets a {@link RemoteViews} which shows an app widget preview provided by app developers in 74 * the pin widget flow. 75 */ setRemoteViewsPreview(@ullable RemoteViews remoteViewsPreview, float previewScale)76 public void setRemoteViewsPreview(@Nullable RemoteViews remoteViewsPreview, 77 float previewScale) { 78 mRemoteViewsPreview = remoteViewsPreview; 79 mRemoteViewsPreviewScale = previewScale; 80 } 81 82 /** Sets a {@link NavigableAppWidgetHostView} which shows a preview layout of an app widget. */ setAppWidgetHostViewPreview( @ullable NavigableAppWidgetHostView appWidgetHostViewPreview)83 public void setAppWidgetHostViewPreview( 84 @Nullable NavigableAppWidgetHostView appWidgetHostViewPreview) { 85 mAppWidgetHostViewPreview = appWidgetHostViewPreview; 86 } 87 88 /** 89 * Starts the drag for the pending item associated with the view. 90 * 91 * @param previewBounds The bounds where the image was displayed, 92 * {@link WidgetImageView#getBitmapBounds()} 93 * @param previewBitmapWidth The actual width of the bitmap displayed in the view. 94 * @param previewViewWidth The width of {@link WidgetImageView} displaying the preview 95 * @param screenPos Position of {@link WidgetImageView} on the screen 96 */ startDrag(Rect previewBounds, int previewBitmapWidth, int previewViewWidth, Point screenPos, DragSource source, DragOptions options)97 public void startDrag(Rect previewBounds, int previewBitmapWidth, int previewViewWidth, 98 Point screenPos, DragSource source, DragOptions options) { 99 if (TestProtocol.sDebugTracing) { 100 Log.d(TestProtocol.NO_DROP_TARGET, "3"); 101 } 102 final Launcher launcher = Launcher.getLauncher(mView.getContext()); 103 LauncherAppState app = LauncherAppState.getInstance(launcher); 104 105 Drawable preview = null; 106 final int previewWidth; 107 final int previewHeight; 108 final float scale; 109 final Point dragOffset; 110 final Rect dragRegion; 111 112 mEstimatedCellSize = launcher.getWorkspace().estimateItemSize(mAddInfo); 113 114 DraggableView draggableView; 115 116 if (mAddInfo instanceof PendingAddWidgetInfo) { 117 PendingAddWidgetInfo createWidgetInfo = (PendingAddWidgetInfo) mAddInfo; 118 119 int maxWidth = Math.min((int) (previewBitmapWidth * MAX_WIDGET_SCALE), mEstimatedCellSize[0]); 120 121 int[] previewSizeBeforeScale = new int[1]; 122 123 if (mRemoteViewsPreview != null) { 124 mAppWidgetHostViewPreview = new LauncherAppWidgetHostView(launcher); 125 mAppWidgetHostViewPreview.setAppWidget(/* appWidgetId= */ -1, 126 ((PendingAddWidgetInfo) mAddInfo).info); 127 DeviceProfile deviceProfile = launcher.getDeviceProfile(); 128 Rect padding = new Rect(); 129 mAppWidgetHostViewPreview.getWidgetInset(deviceProfile, padding); 130 mAppWidgetHostViewPreview.setPadding(padding.left, padding.top, padding.right, 131 padding.bottom); 132 mAppWidgetHostViewPreview.updateAppWidget(/* remoteViews= */ mRemoteViewsPreview); 133 Size widgetSizes = WidgetSizes.getWidgetPaddedSizePx(launcher, 134 mAddInfo.componentName, deviceProfile, mAddInfo.spanX, mAddInfo.spanY); 135 mAppWidgetHostViewPreview.measure( 136 MeasureSpec.makeMeasureSpec(widgetSizes.getWidth(), MeasureSpec.EXACTLY), 137 MeasureSpec.makeMeasureSpec(widgetSizes.getHeight(), MeasureSpec.EXACTLY)); 138 mAppWidgetHostViewPreview.setClipChildren(false); 139 mAppWidgetHostViewPreview.setClipToPadding(false); 140 mAppWidgetHostViewPreview.setScaleToFit(mRemoteViewsPreviewScale); 141 } 142 if (mAppWidgetHostViewPreview != null) { 143 previewSizeBeforeScale[0] = mAppWidgetHostViewPreview.getMeasuredWidth(); 144 launcher.getDragController() 145 .addDragListener(new AppWidgetHostViewDragListener(launcher)); 146 } 147 if (preview == null && mAppWidgetHostViewPreview == null) { 148 Drawable p = new FastBitmapDrawable(new DatabaseWidgetPreviewLoader(launcher) 149 .generateWidgetPreview( 150 createWidgetInfo.info, maxWidth, previewSizeBeforeScale)); 151 if (RoundedCornerEnforcement.isRoundedCornerEnabled()) { 152 p = new RoundDrawableWrapper(p, mEnforcedRoundedCornersForWidget); 153 } 154 preview = p; 155 } 156 157 if (previewSizeBeforeScale[0] < previewBitmapWidth) { 158 // The icon has extra padding around it. 159 int padding = (previewBitmapWidth - previewSizeBeforeScale[0]) / 2; 160 if (previewBitmapWidth > previewViewWidth) { 161 padding = padding * previewViewWidth / previewBitmapWidth; 162 } 163 164 previewBounds.left += padding; 165 previewBounds.right -= padding; 166 } 167 if (mAppWidgetHostViewPreview != null) { 168 previewWidth = mAppWidgetHostViewPreview.getMeasuredWidth(); 169 previewHeight = mAppWidgetHostViewPreview.getMeasuredHeight(); 170 } else { 171 previewWidth = preview.getIntrinsicWidth(); 172 previewHeight = preview.getIntrinsicHeight(); 173 } 174 scale = previewBounds.width() / (float) previewWidth; 175 launcher.getDragController().addDragListener(new WidgetHostViewLoader(launcher, mView)); 176 177 dragOffset = null; 178 dragRegion = null; 179 draggableView = DraggableView.ofType(DraggableView.DRAGGABLE_WIDGET); 180 } else { 181 PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) mAddInfo; 182 Drawable icon = createShortcutInfo.activityInfo.getFullResIcon(app.getIconCache()); 183 LauncherIcons li = LauncherIcons.obtain(launcher); 184 preview = new FastBitmapDrawable( 185 li.createScaledBitmapWithoutShadow(icon, 0)); 186 previewWidth = preview.getIntrinsicWidth(); 187 previewHeight = preview.getIntrinsicHeight(); 188 li.recycle(); 189 scale = ((float) launcher.getDeviceProfile().iconSizePx) / previewWidth; 190 191 dragOffset = new Point(previewPadding / 2, previewPadding / 2); 192 193 // Create a preview same as the workspace cell size and draw the icon at the 194 // appropriate position. 195 DeviceProfile dp = launcher.getDeviceProfile(); 196 int iconSize = dp.iconSizePx; 197 198 int padding = launcher.getResources() 199 .getDimensionPixelSize(R.dimen.widget_preview_shortcut_padding); 200 previewBounds.left += padding; 201 previewBounds.top += padding; 202 203 dragRegion = new Rect(); 204 dragRegion.left = (mEstimatedCellSize[0] - iconSize) / 2; 205 dragRegion.right = dragRegion.left + iconSize; 206 dragRegion.top = (mEstimatedCellSize[1] 207 - iconSize - dp.iconTextSizePx - dp.iconDrawablePaddingPx) / 2; 208 dragRegion.bottom = dragRegion.top + iconSize; 209 draggableView = DraggableView.ofType(DraggableView.DRAGGABLE_ICON); 210 } 211 212 int dragLayerX = screenPos.x + previewBounds.left 213 + (int) ((scale * previewWidth - previewWidth) / 2); 214 int dragLayerY = screenPos.y + previewBounds.top 215 + (int) ((scale * previewHeight - previewHeight) / 2); 216 217 // Start the drag 218 if (mAppWidgetHostViewPreview != null) { 219 launcher.getDragController().startDrag(mAppWidgetHostViewPreview, draggableView, 220 dragLayerX, dragLayerY, source, mAddInfo, dragOffset, dragRegion, scale, scale, 221 options); 222 } else { 223 launcher.getDragController().startDrag(preview, draggableView, dragLayerX, dragLayerY, 224 source, mAddInfo, dragOffset, dragRegion, scale, scale, options); 225 } 226 } 227 228 @Override convertPreviewToAlphaBitmap(Bitmap preview)229 protected Bitmap convertPreviewToAlphaBitmap(Bitmap preview) { 230 if (mAddInfo instanceof PendingAddShortcutInfo || mEstimatedCellSize == null) { 231 return super.convertPreviewToAlphaBitmap(preview); 232 } 233 234 int w = mEstimatedCellSize[0]; 235 int h = mEstimatedCellSize[1]; 236 final Bitmap b = Bitmap.createBitmap(w, h, Bitmap.Config.ALPHA_8); 237 Rect src = new Rect(0, 0, preview.getWidth(), preview.getHeight()); 238 239 float scaleFactor = Math.min((w - blurSizeOutline) / (float) preview.getWidth(), 240 (h - blurSizeOutline) / (float) preview.getHeight()); 241 int scaledWidth = (int) (scaleFactor * preview.getWidth()); 242 int scaledHeight = (int) (scaleFactor * preview.getHeight()); 243 Rect dst = new Rect(0, 0, scaledWidth, scaledHeight); 244 245 // center the image 246 dst.offset((w - scaledWidth) / 2, (h - scaledHeight) / 2); 247 new Canvas(b).drawBitmap(preview, src, dst, new Paint(Paint.FILTER_BITMAP_FLAG)); 248 return b; 249 } 250 } 251