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 17package com.android.launcher3.widget; 18 19import android.graphics.Bitmap; 20import android.graphics.Canvas; 21import android.graphics.Paint; 22import android.graphics.Point; 23import android.graphics.Rect; 24import android.graphics.drawable.Drawable; 25import android.os.Build; 26import android.view.View; 27import android.widget.RemoteViews; 28 29import com.android.launcher3.DeviceProfile; 30import com.android.launcher3.DragSource; 31import com.android.launcher3.Launcher; 32import com.android.launcher3.LauncherAppState; 33import com.android.launcher3.PendingAddItemInfo; 34import com.android.launcher3.R; 35import com.android.launcher3.Workspace; 36import com.android.launcher3.dragndrop.DragOptions; 37import com.android.launcher3.dragndrop.LivePreviewWidgetCell; 38import com.android.launcher3.graphics.DragPreviewProvider; 39import com.android.launcher3.graphics.HolographicOutlineHelper; 40import com.android.launcher3.graphics.LauncherIcons; 41 42/** 43 * Extension of {@link DragPreviewProvider} with logic specific to pending widgets/shortcuts 44 * dragged from the widget tray. 45 */ 46public class PendingItemDragHelper extends DragPreviewProvider { 47 48 private static final float MAX_WIDGET_SCALE = 1.25f; 49 50 private final PendingAddItemInfo mAddInfo; 51 52 private Bitmap mPreviewBitmap; 53 private RemoteViews mPreview; 54 55 public PendingItemDragHelper(View view) { 56 super(view); 57 mAddInfo = (PendingAddItemInfo) view.getTag(); 58 } 59 60 public void setPreview(RemoteViews preview) { 61 mPreview = preview; 62 } 63 64 /** 65 * Starts the drag for the pending item associated with the view. 66 * 67 * @param previewBounds The bounds where the image was displayed, 68 * {@link WidgetImageView#getBitmapBounds()} 69 * @param previewBitmapWidth The actual width of the bitmap displayed in the view. 70 * @param previewViewWidth The width of {@link WidgetImageView} displaying the preview 71 * @param screenPos Position of {@link WidgetImageView} on the screen 72 */ 73 public void startDrag(Rect previewBounds, int previewBitmapWidth, int previewViewWidth, 74 Point screenPos, DragSource source, DragOptions options) { 75 final Launcher launcher = Launcher.getLauncher(mView.getContext()); 76 LauncherAppState app = LauncherAppState.getInstance(launcher); 77 78 Bitmap preview = null; 79 final float scale; 80 final Point dragOffset; 81 final Rect dragRegion; 82 83 84 if (mAddInfo instanceof PendingAddWidgetInfo) { 85 PendingAddWidgetInfo createWidgetInfo = (PendingAddWidgetInfo) mAddInfo; 86 int[] size = launcher.getWorkspace().estimateItemSize(createWidgetInfo, true, false); 87 88 int maxWidth = Math.min((int) (previewBitmapWidth * MAX_WIDGET_SCALE), size[0]); 89 90 int[] previewSizeBeforeScale = new int[1]; 91 92 if (mPreview != null) { 93 preview = LivePreviewWidgetCell.generateFromRemoteViews(launcher, mPreview, 94 createWidgetInfo.info, maxWidth, previewSizeBeforeScale); 95 } 96 if (preview == null) { 97 preview = app.getWidgetCache().generateWidgetPreview( 98 launcher, createWidgetInfo.info, maxWidth, null, previewSizeBeforeScale); 99 } 100 101 if (previewSizeBeforeScale[0] < previewBitmapWidth) { 102 // The icon has extra padding around it. 103 int padding = (previewBitmapWidth - previewSizeBeforeScale[0]) / 2; 104 if (previewBitmapWidth > previewViewWidth) { 105 padding = padding * previewViewWidth / previewBitmapWidth; 106 } 107 108 previewBounds.left += padding; 109 previewBounds.right -= padding; 110 } 111 scale = previewBounds.width() / (float) preview.getWidth(); 112 launcher.getDragController().addDragListener(new WidgetHostViewLoader(launcher, mView)); 113 114 dragOffset = null; 115 dragRegion = null; 116 } else { 117 PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) mAddInfo; 118 Drawable icon = createShortcutInfo.activityInfo.getFullResIcon(app.getIconCache()); 119 preview = LauncherIcons.createScaledBitmapWithoutShadow(icon, launcher, 0); 120 mAddInfo.spanX = mAddInfo.spanY = 1; 121 scale = ((float) launcher.getDeviceProfile().iconSizePx) / preview.getWidth(); 122 123 dragOffset = new Point(previewPadding / 2, previewPadding / 2); 124 125 // Create a preview same as the workspace cell size and draw the icon at the 126 // appropriate position. 127 int[] size = launcher.getWorkspace().estimateItemSize(mAddInfo, false, true); 128 DeviceProfile dp = launcher.getDeviceProfile(); 129 int iconSize = dp.iconSizePx; 130 131 int padding = launcher.getResources() 132 .getDimensionPixelSize(R.dimen.widget_preview_shortcut_padding); 133 previewBounds.left += padding; 134 previewBounds.top += padding; 135 136 dragRegion = new Rect(); 137 dragRegion.left = (size[0] - iconSize) / 2; 138 dragRegion.right = dragRegion.left + iconSize; 139 dragRegion.top = (size[1] - iconSize - dp.iconTextSizePx - dp.iconDrawablePaddingPx) / 2; 140 dragRegion.bottom = dragRegion.top + iconSize; 141 } 142 143 // Since we are not going through the workspace for starting the drag, set drag related 144 // information on the workspace before starting the drag. 145 launcher.getWorkspace().prepareDragWithProvider(this); 146 147 int dragLayerX = screenPos.x + previewBounds.left 148 + (int) ((scale * preview.getWidth() - preview.getWidth()) / 2); 149 int dragLayerY = screenPos.y + previewBounds.top 150 + (int) ((scale * preview.getHeight() - preview.getHeight()) / 2); 151 152 mPreviewBitmap = preview; 153 // Start the drag 154 launcher.getDragController().startDrag(preview, dragLayerX, dragLayerY, source, mAddInfo, 155 dragOffset, dragRegion, scale, options); 156 } 157 158 159 @Override 160 public Bitmap createDragOutline(Canvas canvas) { 161 if (mAddInfo instanceof PendingAddShortcutInfo) { 162 int width = mPreviewBitmap.getWidth(); 163 int height = mPreviewBitmap.getHeight(); 164 Bitmap b = Bitmap.createBitmap(width + blurSizeOutline, height + blurSizeOutline, 165 Bitmap.Config.ALPHA_8); 166 canvas.setBitmap(b); 167 168 Launcher launcher = Launcher.getLauncher(mView.getContext()); 169 int size = launcher.getDeviceProfile().iconSizePx; 170 171 Rect src = new Rect(0, 0, mPreviewBitmap.getWidth(), mPreviewBitmap.getHeight()); 172 Rect dst = new Rect(0, 0, size, size); 173 dst.offset(blurSizeOutline / 2, blurSizeOutline / 2); 174 canvas.drawBitmap(mPreviewBitmap, src, dst, new Paint(Paint.FILTER_BITMAP_FLAG)); 175 176 HolographicOutlineHelper.getInstance(mView.getContext()) 177 .applyExpensiveOutlineWithBlur(b, canvas); 178 179 canvas.setBitmap(null); 180 return b; 181 } 182 183 Workspace workspace = Launcher.getLauncher(mView.getContext()).getWorkspace(); 184 int[] size = workspace.estimateItemSize(mAddInfo, false, false); 185 186 int w = size[0]; 187 int h = size[1]; 188 final Bitmap b = Bitmap.createBitmap(w, h, Bitmap.Config.ALPHA_8); 189 canvas.setBitmap(b); 190 191 Rect src = new Rect(0, 0, mPreviewBitmap.getWidth(), mPreviewBitmap.getHeight()); 192 float scaleFactor = Math.min((w - blurSizeOutline) / (float) mPreviewBitmap.getWidth(), 193 (h - blurSizeOutline) / (float) mPreviewBitmap.getHeight()); 194 int scaledWidth = (int) (scaleFactor * mPreviewBitmap.getWidth()); 195 int scaledHeight = (int) (scaleFactor * mPreviewBitmap.getHeight()); 196 Rect dst = new Rect(0, 0, scaledWidth, scaledHeight); 197 198 // center the image 199 dst.offset((w - scaledWidth) / 2, (h - scaledHeight) / 2); 200 201 canvas.drawBitmap(mPreviewBitmap, src, dst, null); 202 HolographicOutlineHelper.getInstance(mView.getContext()) 203 .applyExpensiveOutlineWithBlur(b, canvas); 204 canvas.setBitmap(null); 205 206 return b; 207 } 208} 209