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