• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 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.launcher2;
18 
19 import android.graphics.drawable.BitmapDrawable;
20 import android.graphics.drawable.Drawable;
21 import android.graphics.drawable.PaintDrawable;
22 import android.graphics.Bitmap;
23 import android.graphics.BlurMaskFilter;
24 import android.graphics.Canvas;
25 import android.graphics.MaskFilter;
26 import android.graphics.Paint;
27 import android.graphics.PaintFlagsDrawFilter;
28 import android.graphics.PixelFormat;
29 import android.graphics.PorterDuff;
30 import android.graphics.Rect;
31 import android.graphics.RectF;
32 import android.graphics.TableMaskFilter;
33 import android.graphics.Typeface;
34 import android.text.Layout.Alignment;
35 import android.text.StaticLayout;
36 import android.text.TextPaint;
37 import android.util.DisplayMetrics;
38 import android.util.Log;
39 import android.content.res.Resources;
40 import android.content.Context;
41 
42 /**
43  * Various utilities shared amongst the Launcher's classes.
44  */
45 final class Utilities {
46     private static final String TAG = "Launcher.Utilities";
47 
48     private static final boolean TEXT_BURN = false;
49 
50     private static int sIconWidth = -1;
51     private static int sIconHeight = -1;
52     private static int sIconTextureWidth = -1;
53     private static int sIconTextureHeight = -1;
54 
55     private static final Paint sPaint = new Paint();
56     private static final Paint sBlurPaint = new Paint();
57     private static final Paint sGlowColorPressedPaint = new Paint();
58     private static final Paint sGlowColorFocusedPaint = new Paint();
59     private static final Paint sEmptyPaint = new Paint();
60     private static final Rect sBounds = new Rect();
61     private static final Rect sOldBounds = new Rect();
62     private static final Canvas sCanvas = new Canvas();
63 
64     static {
sCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG, Paint.FILTER_BITMAP_FLAG))65         sCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG,
66                 Paint.FILTER_BITMAP_FLAG));
67     }
68 
centerToFit(Bitmap bitmap, int width, int height, Context context)69     static Bitmap centerToFit(Bitmap bitmap, int width, int height, Context context) {
70         final int bitmapWidth = bitmap.getWidth();
71         final int bitmapHeight = bitmap.getHeight();
72 
73         if (bitmapWidth < width || bitmapHeight < height) {
74             int color = context.getResources().getColor(R.color.window_background);
75 
76             Bitmap centered = Bitmap.createBitmap(bitmapWidth < width ? width : bitmapWidth,
77                     bitmapHeight < height ? height : bitmapHeight, Bitmap.Config.RGB_565);
78             centered.setDensity(bitmap.getDensity());
79             Canvas canvas = new Canvas(centered);
80             canvas.drawColor(color);
81             canvas.drawBitmap(bitmap, (width - bitmapWidth) / 2.0f, (height - bitmapHeight) / 2.0f,
82                     null);
83 
84             bitmap = centered;
85         }
86 
87         return bitmap;
88     }
89 
90     /**
91      * Returns a Drawable representing the thumbnail of the specified Drawable.
92      * The size of the thumbnail is defined by the dimension
93      * android.R.dimen.launcher_application_icon_size.
94      *
95      * @param icon The icon to get a thumbnail of.
96      * @param context The application's context.
97      *
98      * @return A thumbnail for the specified icon or the icon itself if the
99      *         thumbnail could not be created.
100      */
createIconThumbnail(Drawable icon, Context context)101     static Drawable createIconThumbnail(Drawable icon, Context context) {
102         synchronized (sCanvas) { // we share the statics :-(
103             if (sIconWidth == -1) {
104                 initStatics(context);
105             }
106 
107             int width = sIconWidth;
108             int height = sIconHeight;
109 
110             if (icon instanceof PaintDrawable) {
111                 PaintDrawable painter = (PaintDrawable) icon;
112                 painter.setIntrinsicWidth(width);
113                 painter.setIntrinsicHeight(height);
114             } else if (icon instanceof BitmapDrawable) {
115                 // Ensure the bitmap has a density.
116                 BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
117                 Bitmap bitmap = bitmapDrawable.getBitmap();
118                 if (bitmap.getDensity() == Bitmap.DENSITY_NONE) {
119                     bitmapDrawable.setTargetDensity(context.getResources().getDisplayMetrics());
120                 }
121             }
122             int iconWidth = icon.getIntrinsicWidth();
123             int iconHeight = icon.getIntrinsicHeight();
124 
125             if (iconWidth > 0 && iconHeight > 0) {
126                 if (width < iconWidth || height < iconHeight) {
127                     final float ratio = (float) iconWidth / iconHeight;
128 
129                     if (iconWidth > iconHeight) {
130                         height = (int) (width / ratio);
131                     } else if (iconHeight > iconWidth) {
132                         width = (int) (height * ratio);
133                     }
134 
135                     final Bitmap.Config c = icon.getOpacity() != PixelFormat.OPAQUE ?
136                                 Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
137                     final Bitmap thumb = Bitmap.createBitmap(sIconWidth, sIconHeight, c);
138                     final Canvas canvas = sCanvas;
139                     canvas.setBitmap(thumb);
140                     // Copy the old bounds to restore them later
141                     // If we were to do oldBounds = icon.getBounds(),
142                     // the call to setBounds() that follows would
143                     // change the same instance and we would lose the
144                     // old bounds
145                     sOldBounds.set(icon.getBounds());
146                     final int x = (sIconWidth - width) / 2;
147                     final int y = (sIconHeight - height) / 2;
148                     icon.setBounds(x, y, x + width, y + height);
149                     icon.draw(canvas);
150                     icon.setBounds(sOldBounds);
151                     icon = new FastBitmapDrawable(thumb);
152                 } else if (iconWidth < width && iconHeight < height) {
153                     final Bitmap.Config c = Bitmap.Config.ARGB_8888;
154                     final Bitmap thumb = Bitmap.createBitmap(sIconWidth, sIconHeight, c);
155                     final Canvas canvas = sCanvas;
156                     canvas.setBitmap(thumb);
157                     sOldBounds.set(icon.getBounds());
158                     final int x = (width - iconWidth) / 2;
159                     final int y = (height - iconHeight) / 2;
160                     icon.setBounds(x, y, x + iconWidth, y + iconHeight);
161                     icon.draw(canvas);
162                     icon.setBounds(sOldBounds);
163                     icon = new FastBitmapDrawable(thumb);
164                 }
165             }
166 
167             return icon;
168         }
169     }
170 
171     static int sColors[] = { 0xffff0000, 0xff00ff00, 0xff0000ff };
172     static int sColorIndex = 0;
173 
174     /**
175      * Returns a bitmap suitable for the all apps view.  The bitmap will be a power
176      * of two sized ARGB_8888 bitmap that can be used as a gl texture.
177      */
createAllAppsBitmap(Drawable icon, Context context)178     static Bitmap createAllAppsBitmap(Drawable icon, Context context) {
179         synchronized (sCanvas) { // we share the statics :-(
180             if (sIconWidth == -1) {
181                 initStatics(context);
182             }
183 
184             int width = sIconWidth;
185             int height = sIconHeight;
186 
187             if (icon instanceof PaintDrawable) {
188                 PaintDrawable painter = (PaintDrawable) icon;
189                 painter.setIntrinsicWidth(width);
190                 painter.setIntrinsicHeight(height);
191             } else if (icon instanceof BitmapDrawable) {
192                 // Ensure the bitmap has a density.
193                 BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
194                 Bitmap bitmap = bitmapDrawable.getBitmap();
195                 if (bitmap.getDensity() == Bitmap.DENSITY_NONE) {
196                     bitmapDrawable.setTargetDensity(context.getResources().getDisplayMetrics());
197                 }
198             }
199             int sourceWidth = icon.getIntrinsicWidth();
200             int sourceHeight = icon.getIntrinsicHeight();
201 
202             if (sourceWidth > 0 && sourceWidth > 0) {
203                 // There are intrinsic sizes.
204                 if (width < sourceWidth || height < sourceHeight) {
205                     // It's too big, scale it down.
206                     final float ratio = (float) sourceWidth / sourceHeight;
207                     if (sourceWidth > sourceHeight) {
208                         height = (int) (width / ratio);
209                     } else if (sourceHeight > sourceWidth) {
210                         width = (int) (height * ratio);
211                     }
212                 } else if (sourceWidth < width && sourceHeight < height) {
213                     // It's small, use the size they gave us.
214                     width = sourceWidth;
215                     height = sourceHeight;
216                 }
217             }
218 
219             // no intrinsic size --> use default size
220             int textureWidth = sIconTextureWidth;
221             int textureHeight = sIconTextureHeight;
222 
223             final Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight,
224                     Bitmap.Config.ARGB_8888);
225             final Canvas canvas = sCanvas;
226             canvas.setBitmap(bitmap);
227 
228             final int left = (textureWidth-width) / 2;
229             final int top = (textureHeight-height) / 2;
230 
231             if (false) {
232                 // draw a big box for the icon for debugging
233                 canvas.drawColor(sColors[sColorIndex]);
234                 if (++sColorIndex >= sColors.length) sColorIndex = 0;
235                 Paint debugPaint = new Paint();
236                 debugPaint.setColor(0xffcccc00);
237                 canvas.drawRect(left, top, left+width, top+height, debugPaint);
238             }
239 
240             sOldBounds.set(icon.getBounds());
241             icon.setBounds(left, top, left+width, top+height);
242             icon.draw(canvas);
243             icon.setBounds(sOldBounds);
244 
245             return bitmap;
246         }
247     }
248 
drawSelectedAllAppsBitmap(Canvas dest, int destWidth, int destHeight, boolean pressed, Bitmap src)249     static void drawSelectedAllAppsBitmap(Canvas dest, int destWidth, int destHeight,
250             boolean pressed, Bitmap src) {
251         synchronized (sCanvas) { // we share the statics :-(
252             if (sIconWidth == -1) {
253                 // We can't have gotten to here without src being initialized, which
254                 // comes from this file already.  So just assert.
255                 //initStatics(context);
256                 throw new RuntimeException("Assertion failed: Utilities not initialized");
257             }
258 
259             dest.drawColor(0, PorterDuff.Mode.CLEAR);
260 
261             int[] xy = new int[2];
262             Bitmap mask = src.extractAlpha(sBlurPaint, xy);
263 
264             float px = (destWidth - src.getWidth()) / 2;
265             float py = (destHeight - src.getHeight()) / 2;
266             dest.drawBitmap(mask, px + xy[0], py + xy[1],
267                     pressed ? sGlowColorPressedPaint : sGlowColorFocusedPaint);
268 
269             mask.recycle();
270         }
271     }
272 
273     /**
274      * Returns a Bitmap representing the thumbnail of the specified Bitmap.
275      * The size of the thumbnail is defined by the dimension
276      * android.R.dimen.launcher_application_icon_size.
277      *
278      * @param bitmap The bitmap to get a thumbnail of.
279      * @param context The application's context.
280      *
281      * @return A thumbnail for the specified bitmap or the bitmap itself if the
282      *         thumbnail could not be created.
283      */
createBitmapThumbnail(Bitmap bitmap, Context context)284     static Bitmap createBitmapThumbnail(Bitmap bitmap, Context context) {
285         synchronized (sCanvas) { // we share the statics :-(
286             if (sIconWidth == -1) {
287                 initStatics(context);
288             }
289 
290             int width = sIconWidth;
291             int height = sIconHeight;
292 
293             final int bitmapWidth = bitmap.getWidth();
294             final int bitmapHeight = bitmap.getHeight();
295 
296             if (width > 0 && height > 0) {
297                 if (width < bitmapWidth || height < bitmapHeight) {
298                     final float ratio = (float) bitmapWidth / bitmapHeight;
299 
300                     if (bitmapWidth > bitmapHeight) {
301                         height = (int) (width / ratio);
302                     } else if (bitmapHeight > bitmapWidth) {
303                         width = (int) (height * ratio);
304                     }
305 
306                     final Bitmap.Config c = (width == sIconWidth && height == sIconHeight) ?
307                             bitmap.getConfig() : Bitmap.Config.ARGB_8888;
308                     final Bitmap thumb = Bitmap.createBitmap(sIconWidth, sIconHeight, c);
309                     final Canvas canvas = sCanvas;
310                     final Paint paint = sPaint;
311                     canvas.setBitmap(thumb);
312                     paint.setDither(false);
313                     paint.setFilterBitmap(true);
314                     sBounds.set((sIconWidth - width) / 2, (sIconHeight - height) / 2, width, height);
315                     sOldBounds.set(0, 0, bitmapWidth, bitmapHeight);
316                     canvas.drawBitmap(bitmap, sOldBounds, sBounds, paint);
317                     return thumb;
318                 } else if (bitmapWidth < width || bitmapHeight < height) {
319                     final Bitmap.Config c = Bitmap.Config.ARGB_8888;
320                     final Bitmap thumb = Bitmap.createBitmap(sIconWidth, sIconHeight, c);
321                     final Canvas canvas = sCanvas;
322                     final Paint paint = sPaint;
323                     canvas.setBitmap(thumb);
324                     paint.setDither(false);
325                     paint.setFilterBitmap(true);
326                     canvas.drawBitmap(bitmap, (sIconWidth - bitmapWidth) / 2,
327                             (sIconHeight - bitmapHeight) / 2, paint);
328                     return thumb;
329                 }
330             }
331 
332             return bitmap;
333         }
334     }
335 
initStatics(Context context)336     private static void initStatics(Context context) {
337         final Resources resources = context.getResources();
338         final DisplayMetrics metrics = resources.getDisplayMetrics();
339         final float density = metrics.density;
340 
341         sIconWidth = sIconHeight = (int) resources.getDimension(android.R.dimen.app_icon_size);
342         sIconTextureWidth = sIconTextureHeight = roundToPow2(sIconWidth);
343 
344         sBlurPaint.setMaskFilter(new BlurMaskFilter(5 * density, BlurMaskFilter.Blur.NORMAL));
345         sGlowColorPressedPaint.setColor(0xffffc300);
346         sGlowColorPressedPaint.setMaskFilter(TableMaskFilter.CreateClipTable(0, 30));
347         sGlowColorFocusedPaint.setColor(0xffff8e00);
348         sGlowColorFocusedPaint.setMaskFilter(TableMaskFilter.CreateClipTable(0, 30));
349     }
350 
351     static class BubbleText {
352         private static final int MAX_LINES = 2;
353         private TextPaint mTextPaint;
354 
355         private float mBubblePadding;
356         private RectF mBubbleRect = new RectF();
357 
358         private float mTextWidth;
359         private int mLeading;
360         private int mFirstLineY;
361         private int mLineHeight;
362 
363         private int mBitmapWidth;
364         private int mBitmapHeight;
365 
BubbleText(Context context)366         BubbleText(Context context) {
367             final Resources resources = context.getResources();
368 
369             final float scale = resources.getDisplayMetrics().density;
370 
371             final float paddingLeft = 5.0f * scale;
372             final float paddingRight = 5.0f * scale;
373             final float cellWidth = resources.getDimension(R.dimen.workspace_cell_width);
374             final float bubbleWidth = cellWidth - paddingLeft - paddingRight;
375             mBubblePadding = 3.0f * scale;
376 
377             RectF bubbleRect = mBubbleRect;
378             bubbleRect.left = 0;
379             bubbleRect.top = 0;
380             bubbleRect.right = (int)(bubbleWidth+0.5f);
381 
382             mTextWidth = bubbleWidth - mBubblePadding - mBubblePadding;
383 
384             Paint rectPaint = new Paint();
385             rectPaint.setColor(0xff000000);
386             rectPaint.setAntiAlias(true);
387 
388             TextPaint textPaint = mTextPaint = new TextPaint();
389             textPaint.setTypeface(Typeface.DEFAULT);
390             textPaint.setTextSize(13*scale);
391             textPaint.setColor(0xffffffff);
392             textPaint.setAntiAlias(true);
393             if (TEXT_BURN) {
394                 textPaint.setShadowLayer(8, 0, 0, 0xff000000);
395             }
396 
397             float ascent = -textPaint.ascent();
398             float descent = textPaint.descent();
399             float leading = 0.0f;//(ascent+descent) * 0.1f;
400             mLeading = (int)(leading + 0.5f);
401             mFirstLineY = (int)(leading + ascent + 0.5f);
402             mLineHeight = (int)(leading + ascent + descent + 0.5f);
403 
404             mBitmapWidth = roundToPow2((int)(mBubbleRect.width() + 0.5f));
405             mBitmapHeight = roundToPow2((int)((MAX_LINES * mLineHeight) + leading + 0.5f));
406 
407             mBubbleRect.offsetTo((mBitmapWidth-mBubbleRect.width())/2, 0);
408 
409             if (false) {
410                 Log.d(TAG, "mBitmapWidth=" + mBitmapWidth + " mBitmapHeight="
411                         + mBitmapHeight + " w=" + ((int)(mBubbleRect.width() + 0.5f))
412                         + " h=" + ((int)((MAX_LINES * mLineHeight) + leading + 0.5f)));
413             }
414         }
415 
416         /** You own the bitmap after this and you must call recycle on it. */
createTextBitmap(String text)417         Bitmap createTextBitmap(String text) {
418             Bitmap b = Bitmap.createBitmap(mBitmapWidth, mBitmapHeight, Bitmap.Config.ARGB_8888);
419             Canvas c = new Canvas(b);
420 
421             StaticLayout layout = new StaticLayout(text, mTextPaint, (int)mTextWidth,
422                     Alignment.ALIGN_CENTER, 1, 0, true);
423             int lineCount = layout.getLineCount();
424             if (lineCount > MAX_LINES) {
425                 lineCount = MAX_LINES;
426             }
427             //if (!TEXT_BURN && lineCount > 0) {
428                 //RectF bubbleRect = mBubbleRect;
429                 //bubbleRect.bottom = height(lineCount);
430                 //c.drawRoundRect(bubbleRect, mCornerRadius, mCornerRadius, mRectPaint);
431             //}
432             for (int i=0; i<lineCount; i++) {
433                 //int x = (int)((mBubbleRect.width() - layout.getLineMax(i)) / 2.0f);
434                 //int y = mFirstLineY + (i * mLineHeight);
435                 int x = (int)(mBubbleRect.left
436                         + ((mBubbleRect.width() - layout.getLineMax(i)) / 2.0f));
437                 int y = mFirstLineY + (i * mLineHeight);
438                 c.drawText(text.substring(layout.getLineStart(i), layout.getLineEnd(i)),
439                         x, y, mTextPaint);
440             }
441 
442             return b;
443         }
444 
height(int lineCount)445         private int height(int lineCount) {
446             return (int)((lineCount * mLineHeight) + mLeading + mLeading + 0.0f);
447         }
448 
getBubbleWidth()449         int getBubbleWidth() {
450             return (int)(mBubbleRect.width() + 0.5f);
451         }
452 
getMaxBubbleHeight()453         int getMaxBubbleHeight() {
454             return height(MAX_LINES);
455         }
456 
getBitmapWidth()457         int getBitmapWidth() {
458             return mBitmapWidth;
459         }
460 
getBitmapHeight()461         int getBitmapHeight() {
462             return mBitmapHeight;
463         }
464     }
465 
466     /** Only works for positive numbers. */
roundToPow2(int n)467     static int roundToPow2(int n) {
468         int orig = n;
469         n >>= 1;
470         int mask = 0x8000000;
471         while (mask != 0 && (n & mask) == 0) {
472             mask >>= 1;
473         }
474         while (mask != 0) {
475             n |= mask;
476             mask >>= 1;
477         }
478         n += 1;
479         if (n != orig) {
480             n <<= 1;
481         }
482         return n;
483     }
484 }
485