• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.Region.Op;
24 import android.graphics.drawable.Drawable;
25 import android.view.View;
26 
27 import com.android.launcher3.BubbleTextView;
28 import com.android.launcher3.Launcher;
29 import com.android.launcher3.LauncherAppWidgetHostView;
30 import com.android.launcher3.R;
31 import com.android.launcher3.config.FeatureFlags;
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     private final Rect mTempRect = new Rect();
40 
41     protected final View mView;
42 
43     // The padding added to the drag view during the preview generation.
44     public final int previewPadding;
45 
46     protected final int blurSizeOutline;
47 
48     public Bitmap generatedDragOutline;
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 
59         if (mView instanceof BubbleTextView) {
60             Drawable d = ((BubbleTextView) mView).getIcon();
61             Rect bounds = getDrawableBounds(d);
62             previewPadding = blurSizeOutline - bounds.left - bounds.top;
63         } else {
64             previewPadding = blurSizeOutline;
65         }
66     }
67 
68     /**
69      * Draws the {@link #mView} into the given {@param destCanvas}.
70      */
drawDragView(Canvas destCanvas)71     private void drawDragView(Canvas destCanvas) {
72         destCanvas.save();
73         if (mView instanceof BubbleTextView) {
74             Drawable d = ((BubbleTextView) mView).getIcon();
75             Rect bounds = getDrawableBounds(d);
76             destCanvas.translate(blurSizeOutline / 2 - bounds.left,
77                     blurSizeOutline / 2 - bounds.top);
78             d.draw(destCanvas);
79         } else {
80             final Rect clipRect = mTempRect;
81             mView.getDrawingRect(clipRect);
82 
83             boolean textVisible = false;
84             if (mView instanceof FolderIcon) {
85                 // For FolderIcons the text can bleed into the icon area, and so we need to
86                 // hide the text completely (which can't be achieved by clipping).
87                 if (((FolderIcon) mView).getTextVisible()) {
88                     ((FolderIcon) mView).setTextVisible(false);
89                     textVisible = true;
90                 }
91             }
92             destCanvas.translate(-mView.getScrollX() + blurSizeOutline / 2,
93                     -mView.getScrollY() + blurSizeOutline / 2);
94             destCanvas.clipRect(clipRect, Op.REPLACE);
95             mView.draw(destCanvas);
96 
97             // Restore text visibility of FolderIcon if necessary
98             if (textVisible) {
99                 ((FolderIcon) mView).setTextVisible(true);
100             }
101         }
102         destCanvas.restore();
103     }
104 
105     /**
106      * Returns a new bitmap to show when the {@link #mView} is being dragged around.
107      * Responsibility for the bitmap is transferred to the caller.
108      */
createDragBitmap(Canvas canvas)109     public Bitmap createDragBitmap(Canvas canvas) {
110         float scale = 1f;
111         int width = mView.getWidth();
112         int height = mView.getHeight();
113 
114         if (mView instanceof BubbleTextView) {
115             Drawable d = ((BubbleTextView) mView).getIcon();
116             Rect bounds = getDrawableBounds(d);
117             width = bounds.width();
118             height = bounds.height();
119         } else if (mView instanceof LauncherAppWidgetHostView) {
120             scale = ((LauncherAppWidgetHostView) mView).getScaleToFit();
121             width = (int) (mView.getWidth() * scale);
122             height = (int) (mView.getHeight() * scale);
123         }
124 
125         Bitmap b = Bitmap.createBitmap(width + blurSizeOutline, height + blurSizeOutline,
126                 Bitmap.Config.ARGB_8888);
127         canvas.setBitmap(b);
128 
129         canvas.save();
130         canvas.scale(scale, scale);
131         drawDragView(canvas);
132         canvas.restore();
133 
134         canvas.setBitmap(null);
135 
136         return b;
137     }
138 
generateDragOutline(Canvas canvas)139     public final void generateDragOutline(Canvas canvas) {
140         if (FeatureFlags.IS_DOGFOOD_BUILD && generatedDragOutline != null) {
141             throw new RuntimeException("Drag outline generated twice");
142         }
143 
144         generatedDragOutline = createDragOutline(canvas);
145     }
146 
147     /**
148      * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
149      * Responsibility for the bitmap is transferred to the caller.
150      */
createDragOutline(Canvas canvas)151     public Bitmap createDragOutline(Canvas canvas) {
152         float scale = 1f;
153         int width = mView.getWidth();
154         int height = mView.getHeight();
155 
156         if (mView instanceof LauncherAppWidgetHostView) {
157             scale = ((LauncherAppWidgetHostView) mView).getScaleToFit();
158             width = (int) Math.floor(mView.getWidth() * scale);
159             height = (int) Math.floor(mView.getHeight() * scale);
160         }
161 
162         Bitmap b = Bitmap.createBitmap(width + blurSizeOutline, height + blurSizeOutline,
163                 Bitmap.Config.ALPHA_8);
164         canvas.setBitmap(b);
165 
166         canvas.save();
167         canvas.scale(scale, scale);
168         drawDragView(canvas);
169         canvas.restore();
170 
171         HolographicOutlineHelper.getInstance(mView.getContext())
172                 .applyExpensiveOutlineWithBlur(b, canvas);
173 
174         canvas.setBitmap(null);
175         return b;
176     }
177 
getDrawableBounds(Drawable d)178     protected static Rect getDrawableBounds(Drawable d) {
179         Rect bounds = new Rect();
180         d.copyBounds(bounds);
181         if (bounds.width() == 0 || bounds.height() == 0) {
182             bounds.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
183         } else {
184             bounds.offsetTo(0, 0);
185         }
186         return bounds;
187     }
188 
getScaleAndPosition(Bitmap preview, int[] outPos)189     public float getScaleAndPosition(Bitmap preview, int[] outPos) {
190         float scale = Launcher.getLauncher(mView.getContext())
191                 .getDragLayer().getLocationInDragLayer(mView, outPos);
192         if (mView instanceof LauncherAppWidgetHostView) {
193             // App widgets are technically scaled, but are drawn at their expected size -- so the
194             // app widget scale should not affect the scale of the preview.
195             scale /= ((LauncherAppWidgetHostView) mView).getScaleToFit();
196         }
197 
198         outPos[0] = Math.round(outPos[0] -
199                 (preview.getWidth() - scale * mView.getWidth() * mView.getScaleX()) / 2);
200         outPos[1] = Math.round(outPos[1] - (1 - scale) * preview.getHeight() / 2
201                 - previewPadding / 2);
202         return scale;
203     }
204 }
205