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.content.Context; 20 import android.graphics.Bitmap; 21 import android.graphics.Canvas; 22 import android.graphics.Rect; 23 import android.graphics.drawable.Drawable; 24 import android.view.View; 25 26 import androidx.annotation.Nullable; 27 28 import com.android.launcher3.R; 29 import com.android.launcher3.dragndrop.DraggableView; 30 import com.android.launcher3.icons.BitmapRenderer; 31 import com.android.launcher3.icons.FastBitmapDrawable; 32 import com.android.launcher3.util.SafeCloseable; 33 import com.android.launcher3.views.ActivityContext; 34 import com.android.launcher3.widget.LauncherAppWidgetHostView; 35 36 /** 37 * A utility class to generate preview bitmap for dragging. 38 */ 39 public class DragPreviewProvider { 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 final int blurSizeOutline; 49 DragPreviewProvider(View view)50 public DragPreviewProvider(View view) { 51 this(view, view.getContext()); 52 } 53 DragPreviewProvider(View view, Context context)54 public DragPreviewProvider(View view, Context context) { 55 mView = view; 56 blurSizeOutline = 57 context.getResources().getDimensionPixelSize(R.dimen.blur_size_medium_outline); 58 previewPadding = blurSizeOutline; 59 } 60 61 /** 62 * Draws the {@link #mView} into the given {@param destCanvas}. 63 */ drawDragView(Canvas destCanvas, float scale)64 protected void drawDragView(Canvas destCanvas, float scale) { 65 int saveCount = destCanvas.save(); 66 destCanvas.scale(scale, scale); 67 68 if (mView instanceof DraggableView) { 69 DraggableView dv = (DraggableView) mView; 70 try (SafeCloseable t = dv.prepareDrawDragView()) { 71 dv.getSourceVisualDragBounds(mTempRect); 72 destCanvas.translate(blurSizeOutline / 2 - mTempRect.left, 73 blurSizeOutline / 2 - mTempRect.top); 74 mView.draw(destCanvas); 75 } 76 } 77 destCanvas.restoreToCount(saveCount); 78 } 79 80 /** 81 * Returns a new drawable to show when the {@link #mView} is being dragged around. 82 * Responsibility for the drawable is transferred to the caller. 83 */ createDrawable()84 public Drawable createDrawable() { 85 if (mView instanceof LauncherAppWidgetHostView) { 86 return null; 87 } 88 89 int width = 0; 90 int height = 0; 91 // Assume scaleX == scaleY, which is always the case for workspace items. 92 float scale = mView.getScaleX(); 93 if (mView instanceof DraggableView) { 94 ((DraggableView) mView).getSourceVisualDragBounds(mTempRect); 95 width = mTempRect.width(); 96 height = mTempRect.height(); 97 } else { 98 width = mView.getWidth(); 99 height = mView.getHeight(); 100 } 101 102 return new FastBitmapDrawable( 103 BitmapRenderer.createHardwareBitmap(width + blurSizeOutline, 104 height + blurSizeOutline, (c) -> drawDragView(c, scale))); 105 } 106 107 /** 108 * Returns the content view if the content should be rendered directly in 109 * {@link com.android.launcher3.dragndrop.DragView}. Otherwise, returns null. 110 */ 111 @Nullable getContentView()112 public View getContentView() { 113 if (mView instanceof LauncherAppWidgetHostView) { 114 return mView; 115 } 116 return null; 117 } 118 getDrawableBounds(Drawable d)119 protected static Rect getDrawableBounds(Drawable d) { 120 Rect bounds = new Rect(); 121 d.copyBounds(bounds); 122 if (bounds.width() == 0 || bounds.height() == 0) { 123 bounds.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); 124 } else { 125 bounds.offsetTo(0, 0); 126 } 127 return bounds; 128 } 129 getScaleAndPosition(Drawable preview, int[] outPos)130 public float getScaleAndPosition(Drawable preview, int[] outPos) { 131 float scale = ActivityContext.lookupContext(mView.getContext()) 132 .getDragLayer().getLocationInDragLayer(mView, outPos); 133 if (mView instanceof LauncherAppWidgetHostView) { 134 // App widgets are technically scaled, but are drawn at their expected size -- so the 135 // app widget scale should not affect the scale of the preview. 136 scale /= ((LauncherAppWidgetHostView) mView).getScaleToFit(); 137 } 138 139 outPos[0] = Math.round(outPos[0] - 140 (preview.getIntrinsicWidth() - scale * mView.getWidth() * mView.getScaleX()) / 2); 141 outPos[1] = Math.round(outPos[1] - (1 - scale) * preview.getIntrinsicHeight() / 2 142 - previewPadding / 2); 143 return scale; 144 } 145 146 /** Returns the scale and position of a given view for drag-n-drop. */ getScaleAndPosition(View view, int[] outPos)147 public float getScaleAndPosition(View view, int[] outPos) { 148 float scale = ActivityContext.lookupContext(mView.getContext()) 149 .getDragLayer().getLocationInDragLayer(mView, outPos); 150 if (mView instanceof LauncherAppWidgetHostView) { 151 // App widgets are technically scaled, but are drawn at their expected size -- so the 152 // app widget scale should not affect the scale of the preview. 153 scale /= ((LauncherAppWidgetHostView) mView).getScaleToFit(); 154 } 155 156 outPos[0] = Math.round(outPos[0] 157 - (view.getWidth() - scale * mView.getWidth() * mView.getScaleX()) / 2); 158 outPos[1] = Math.round(outPos[1] - (1 - scale) * view.getHeight() / 2 - previewPadding / 2); 159 return scale; 160 } 161 convertPreviewToAlphaBitmap(Bitmap preview)162 protected Bitmap convertPreviewToAlphaBitmap(Bitmap preview) { 163 return preview.copy(Bitmap.Config.ALPHA_8, true); 164 } 165 } 166