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