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.view.View; 26 import android.view.View.MeasureSpec; 27 import android.widget.RemoteViews; 28 29 import androidx.annotation.Nullable; 30 31 import com.android.launcher3.DeviceProfile; 32 import com.android.launcher3.DragSource; 33 import com.android.launcher3.Launcher; 34 import com.android.launcher3.LauncherAppState; 35 import com.android.launcher3.PendingAddItemInfo; 36 import com.android.launcher3.R; 37 import com.android.launcher3.dragndrop.DragOptions; 38 import com.android.launcher3.dragndrop.DraggableView; 39 import com.android.launcher3.graphics.DragPreviewProvider; 40 import com.android.launcher3.icons.FastBitmapDrawable; 41 import com.android.launcher3.icons.LauncherIcons; 42 import com.android.launcher3.icons.RoundDrawableWrapper; 43 import com.android.launcher3.widget.dragndrop.AppWidgetHostViewDragListener; 44 45 /** 46 * Extension of {@link DragPreviewProvider} with logic specific to pending widgets/shortcuts 47 * dragged from the widget tray. 48 */ 49 public class PendingItemDragHelper extends DragPreviewProvider { 50 51 private static final float MAX_WIDGET_SCALE = 1.25f; 52 53 private final PendingAddItemInfo mAddInfo; 54 private int[] mEstimatedCellSize; 55 56 @Nullable private RemoteViews mRemoteViewsPreview; 57 @Nullable private NavigableAppWidgetHostView mAppWidgetHostViewPreview; 58 private final float mEnforcedRoundedCornersForWidget; 59 PendingItemDragHelper(View view)60 public PendingItemDragHelper(View view) { 61 super(view); 62 mAddInfo = (PendingAddItemInfo) view.getTag(); 63 mEnforcedRoundedCornersForWidget = RoundedCornerEnforcement.computeEnforcedRadius( 64 view.getContext()); 65 } 66 67 /** 68 * Sets a {@link RemoteViews} which shows an app widget preview provided by app developers in 69 * the pin widget flow. 70 */ setRemoteViewsPreview(@ullable RemoteViews remoteViewsPreview)71 public void setRemoteViewsPreview(@Nullable RemoteViews remoteViewsPreview) { 72 mRemoteViewsPreview = remoteViewsPreview; 73 } 74 75 /** Sets a {@link NavigableAppWidgetHostView} which shows a preview layout of an app widget. */ setAppWidgetHostViewPreview( @ullable NavigableAppWidgetHostView appWidgetHostViewPreview)76 public void setAppWidgetHostViewPreview( 77 @Nullable NavigableAppWidgetHostView appWidgetHostViewPreview) { 78 mAppWidgetHostViewPreview = appWidgetHostViewPreview; 79 } 80 81 /** 82 * Starts the drag for the pending item associated with the view. 83 * 84 * @param previewBounds The bounds where the image was displayed, 85 * {@link WidgetImageView#getBitmapBounds()} 86 * @param previewBitmapWidth The actual width of the bitmap displayed in the view. 87 * @param previewViewWidth The width of {@link WidgetImageView} displaying the preview 88 * @param screenPos Position of {@link WidgetImageView} on the screen 89 */ startDrag(Rect previewBounds, int previewBitmapWidth, int previewViewWidth, Point screenPos, DragSource source, DragOptions options)90 public void startDrag(Rect previewBounds, int previewBitmapWidth, int previewViewWidth, 91 Point screenPos, DragSource source, DragOptions options) { 92 final Launcher launcher = Launcher.getLauncher(mView.getContext()); 93 LauncherAppState app = LauncherAppState.getInstance(launcher); 94 95 Drawable preview = null; 96 final int previewWidth; 97 final int previewHeight; 98 final float scale; 99 final Point dragOffset; 100 final Rect dragRegion; 101 102 mEstimatedCellSize = launcher.getWorkspace().estimateItemSize(mAddInfo); 103 104 DraggableView draggableView; 105 106 if (mAddInfo instanceof PendingAddWidgetInfo) { 107 PendingAddWidgetInfo createWidgetInfo = (PendingAddWidgetInfo) mAddInfo; 108 109 int maxWidth = Math.min((int) (previewBitmapWidth * MAX_WIDGET_SCALE), mEstimatedCellSize[0]); 110 111 int[] previewSizeBeforeScale = new int[1]; 112 113 if (mRemoteViewsPreview != null) { 114 mAppWidgetHostViewPreview = new LauncherAppWidgetHostView(launcher); 115 mAppWidgetHostViewPreview.setAppWidget(/* appWidgetId= */ -1, 116 ((PendingAddWidgetInfo) mAddInfo).info); 117 DeviceProfile deviceProfile = launcher.getDeviceProfile(); 118 Rect padding = new Rect(); 119 mAppWidgetHostViewPreview.getWidgetInset(deviceProfile, padding); 120 mAppWidgetHostViewPreview.setPadding(padding.left, padding.top, padding.right, 121 padding.bottom); 122 mAppWidgetHostViewPreview.updateAppWidget(/* remoteViews= */ mRemoteViewsPreview); 123 int width = 124 deviceProfile.cellWidthPx * mAddInfo.spanX + padding.left + padding.right; 125 int height = 126 deviceProfile.cellHeightPx * mAddInfo.spanY + padding.top + padding.bottom; 127 mAppWidgetHostViewPreview.measure( 128 MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), 129 MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); 130 } 131 if (mAppWidgetHostViewPreview != null) { 132 previewSizeBeforeScale[0] = mAppWidgetHostViewPreview.getMeasuredWidth(); 133 launcher.getDragController() 134 .addDragListener(new AppWidgetHostViewDragListener(launcher)); 135 } 136 if (preview == null && mAppWidgetHostViewPreview == null) { 137 Drawable p = new FastBitmapDrawable( 138 app.getWidgetCache().generateWidgetPreview(launcher, 139 createWidgetInfo.info, maxWidth, null, 140 previewSizeBeforeScale).first); 141 if (RoundedCornerEnforcement.isRoundedCornerEnabled()) { 142 p = new RoundDrawableWrapper(p, mEnforcedRoundedCornersForWidget); 143 } 144 preview = p; 145 } 146 147 if (previewSizeBeforeScale[0] < previewBitmapWidth) { 148 // The icon has extra padding around it. 149 int padding = (previewBitmapWidth - previewSizeBeforeScale[0]) / 2; 150 if (previewBitmapWidth > previewViewWidth) { 151 padding = padding * previewViewWidth / previewBitmapWidth; 152 } 153 154 previewBounds.left += padding; 155 previewBounds.right -= padding; 156 } 157 if (mAppWidgetHostViewPreview != null) { 158 previewWidth = mAppWidgetHostViewPreview.getMeasuredWidth(); 159 previewHeight = mAppWidgetHostViewPreview.getMeasuredHeight(); 160 } else { 161 previewWidth = preview.getIntrinsicWidth(); 162 previewHeight = preview.getIntrinsicHeight(); 163 } 164 scale = previewBounds.width() / (float) previewWidth; 165 launcher.getDragController().addDragListener(new WidgetHostViewLoader(launcher, mView)); 166 167 dragOffset = null; 168 dragRegion = null; 169 draggableView = DraggableView.ofType(DraggableView.DRAGGABLE_WIDGET); 170 } else { 171 PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) mAddInfo; 172 Drawable icon = createShortcutInfo.activityInfo.getFullResIcon(app.getIconCache()); 173 LauncherIcons li = LauncherIcons.obtain(launcher); 174 preview = new FastBitmapDrawable( 175 li.createScaledBitmapWithoutShadow(icon, 0)); 176 previewWidth = preview.getIntrinsicWidth(); 177 previewHeight = preview.getIntrinsicHeight(); 178 li.recycle(); 179 scale = ((float) launcher.getDeviceProfile().iconSizePx) / previewWidth; 180 181 dragOffset = new Point(previewPadding / 2, previewPadding / 2); 182 183 // Create a preview same as the workspace cell size and draw the icon at the 184 // appropriate position. 185 DeviceProfile dp = launcher.getDeviceProfile(); 186 int iconSize = dp.iconSizePx; 187 188 int padding = launcher.getResources() 189 .getDimensionPixelSize(R.dimen.widget_preview_shortcut_padding); 190 previewBounds.left += padding; 191 previewBounds.top += padding; 192 193 dragRegion = new Rect(); 194 dragRegion.left = (mEstimatedCellSize[0] - iconSize) / 2; 195 dragRegion.right = dragRegion.left + iconSize; 196 dragRegion.top = (mEstimatedCellSize[1] 197 - iconSize - dp.iconTextSizePx - dp.iconDrawablePaddingPx) / 2; 198 dragRegion.bottom = dragRegion.top + iconSize; 199 draggableView = DraggableView.ofType(DraggableView.DRAGGABLE_ICON); 200 } 201 202 // Since we are not going through the workspace for starting the drag, set drag related 203 // information on the workspace before starting the drag. 204 launcher.getWorkspace().prepareDragWithProvider(this); 205 206 int dragLayerX = screenPos.x + previewBounds.left 207 + (int) ((scale * previewWidth - previewWidth) / 2); 208 int dragLayerY = screenPos.y + previewBounds.top 209 + (int) ((scale * previewHeight - previewHeight) / 2); 210 211 // Start the drag 212 if (mAppWidgetHostViewPreview != null) { 213 launcher.getDragController().startDrag(mAppWidgetHostViewPreview, draggableView, 214 dragLayerX, dragLayerY, source, mAddInfo, dragOffset, dragRegion, scale, scale, 215 options); 216 } else { 217 launcher.getDragController().startDrag(preview, draggableView, dragLayerX, dragLayerY, 218 source, mAddInfo, dragOffset, dragRegion, scale, scale, options); 219 } 220 } 221 222 @Override convertPreviewToAlphaBitmap(Bitmap preview)223 protected Bitmap convertPreviewToAlphaBitmap(Bitmap preview) { 224 if (mAddInfo instanceof PendingAddShortcutInfo || mEstimatedCellSize == null) { 225 return super.convertPreviewToAlphaBitmap(preview); 226 } 227 228 int w = mEstimatedCellSize[0]; 229 int h = mEstimatedCellSize[1]; 230 final Bitmap b = Bitmap.createBitmap(w, h, Bitmap.Config.ALPHA_8); 231 Rect src = new Rect(0, 0, preview.getWidth(), preview.getHeight()); 232 233 float scaleFactor = Math.min((w - blurSizeOutline) / (float) preview.getWidth(), 234 (h - blurSizeOutline) / (float) preview.getHeight()); 235 int scaledWidth = (int) (scaleFactor * preview.getWidth()); 236 int scaledHeight = (int) (scaleFactor * preview.getHeight()); 237 Rect dst = new Rect(0, 0, scaledWidth, scaledHeight); 238 239 // center the image 240 dst.offset((w - scaledWidth) / 2, (h - scaledHeight) / 2); 241 new Canvas(b).drawBitmap(preview, src, dst, new Paint(Paint.FILTER_BITMAP_FLAG)); 242 return b; 243 } 244 } 245