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