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.graphics; 18 19 import android.graphics.Bitmap; 20 import android.graphics.Canvas; 21 import android.graphics.Rect; 22 import android.graphics.Region.Op; 23 import android.graphics.drawable.Drawable; 24 import android.view.View; 25 import android.widget.TextView; 26 27 import com.android.launcher3.HolographicOutlineHelper; 28 import com.android.launcher3.Launcher; 29 import com.android.launcher3.PreloadIconDrawable; 30 import com.android.launcher3.Workspace; 31 import com.android.launcher3.config.ProviderConfig; 32 import com.android.launcher3.folder.FolderIcon; 33 34 /** 35 * A utility class to generate preview bitmap for dragging. 36 */ 37 public class DragPreviewProvider { 38 39 public static final int DRAG_BITMAP_PADDING = 2; 40 41 private final Rect mTempRect = new Rect(); 42 43 protected final View mView; 44 45 // The padding added to the drag view during the preview generation. 46 public final int previewPadding; 47 48 public Bitmap gerenatedDragOutline; 49 DragPreviewProvider(View view)50 public DragPreviewProvider(View view) { 51 mView = view; 52 53 if (mView instanceof TextView) { 54 Drawable d = Workspace.getTextViewIcon((TextView) mView); 55 Rect bounds = getDrawableBounds(d); 56 previewPadding = DRAG_BITMAP_PADDING - bounds.left - bounds.top; 57 } else { 58 previewPadding = DRAG_BITMAP_PADDING; 59 } 60 } 61 62 /** 63 * Draws the {@link #mView} into the given {@param destCanvas}. 64 */ drawDragView(Canvas destCanvas)65 private void drawDragView(Canvas destCanvas) { 66 destCanvas.save(); 67 if (mView instanceof TextView) { 68 Drawable d = Workspace.getTextViewIcon((TextView) mView); 69 Rect bounds = getDrawableBounds(d); 70 destCanvas.translate(DRAG_BITMAP_PADDING / 2 - bounds.left, 71 DRAG_BITMAP_PADDING / 2 - bounds.top); 72 d.draw(destCanvas); 73 } else { 74 final Rect clipRect = mTempRect; 75 mView.getDrawingRect(clipRect); 76 77 boolean textVisible = false; 78 if (mView instanceof FolderIcon) { 79 // For FolderIcons the text can bleed into the icon area, and so we need to 80 // hide the text completely (which can't be achieved by clipping). 81 if (((FolderIcon) mView).getTextVisible()) { 82 ((FolderIcon) mView).setTextVisible(false); 83 textVisible = true; 84 } 85 } 86 destCanvas.translate(-mView.getScrollX() + DRAG_BITMAP_PADDING / 2, 87 -mView.getScrollY() + DRAG_BITMAP_PADDING / 2); 88 destCanvas.clipRect(clipRect, Op.REPLACE); 89 mView.draw(destCanvas); 90 91 // Restore text visibility of FolderIcon if necessary 92 if (textVisible) { 93 ((FolderIcon) mView).setTextVisible(true); 94 } 95 } 96 destCanvas.restore(); 97 } 98 99 /** 100 * Returns a new bitmap to show when the {@link #mView} is being dragged around. 101 * Responsibility for the bitmap is transferred to the caller. 102 */ createDragBitmap(Canvas canvas)103 public Bitmap createDragBitmap(Canvas canvas) { 104 Bitmap b; 105 106 if (mView instanceof TextView) { 107 Drawable d = Workspace.getTextViewIcon((TextView) mView); 108 Rect bounds = getDrawableBounds(d); 109 b = Bitmap.createBitmap(bounds.width() + DRAG_BITMAP_PADDING, 110 bounds.height() + DRAG_BITMAP_PADDING, Bitmap.Config.ARGB_8888); 111 } else { 112 b = Bitmap.createBitmap(mView.getWidth() + DRAG_BITMAP_PADDING, 113 mView.getHeight() + DRAG_BITMAP_PADDING, Bitmap.Config.ARGB_8888); 114 } 115 116 canvas.setBitmap(b); 117 drawDragView(canvas); 118 canvas.setBitmap(null); 119 120 return b; 121 } 122 generateDragOutline(Canvas canvas)123 public final void generateDragOutline(Canvas canvas) { 124 if (ProviderConfig.IS_DOGFOOD_BUILD && gerenatedDragOutline != null) { 125 throw new RuntimeException("Drag outline generated twice"); 126 } 127 128 gerenatedDragOutline = createDragOutline(canvas); 129 } 130 131 /** 132 * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location. 133 * Responsibility for the bitmap is transferred to the caller. 134 */ createDragOutline(Canvas canvas)135 public Bitmap createDragOutline(Canvas canvas) { 136 final Bitmap b = Bitmap.createBitmap(mView.getWidth() + DRAG_BITMAP_PADDING, 137 mView.getHeight() + DRAG_BITMAP_PADDING, Bitmap.Config.ALPHA_8); 138 canvas.setBitmap(b); 139 drawDragView(canvas); 140 HolographicOutlineHelper.obtain(mView.getContext()) 141 .applyExpensiveOutlineWithBlur(b, canvas); 142 canvas.setBitmap(null); 143 return b; 144 } 145 getDrawableBounds(Drawable d)146 protected static Rect getDrawableBounds(Drawable d) { 147 Rect bounds = new Rect(); 148 d.copyBounds(bounds); 149 if (bounds.width() == 0 || bounds.height() == 0) { 150 bounds.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); 151 } else { 152 bounds.offsetTo(0, 0); 153 } 154 if (d instanceof PreloadIconDrawable) { 155 int inset = -((PreloadIconDrawable) d).getOutset(); 156 bounds.inset(inset, inset); 157 } 158 return bounds; 159 } 160 getScaleAndPosition(Bitmap preview, int[] outPos)161 public float getScaleAndPosition(Bitmap preview, int[] outPos) { 162 float scale = Launcher.getLauncher(mView.getContext()) 163 .getDragLayer().getLocationInDragLayer(mView, outPos); 164 outPos[0] = Math.round(outPos[0] - (preview.getWidth() - scale * mView.getWidth()) / 2); 165 outPos[1] = Math.round(outPos[1] - (1 - scale) * preview.getHeight() / 2 - previewPadding / 2); 166 return scale; 167 } 168 } 169