blob: 073b1792779801519f0419f3ef64aabf670beb9c [file] [log] [blame]
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001/*
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
Joe Onoratoa5902522009-07-30 13:37:37 -070017package com.android.launcher2;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080018
Mitsuru Oshima583ed3b2009-05-12 19:19:10 -070019import android.graphics.drawable.BitmapDrawable;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080020import android.graphics.drawable.Drawable;
21import android.graphics.drawable.PaintDrawable;
22import android.graphics.Bitmap;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080023import android.graphics.Canvas;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080024import android.graphics.Paint;
Joe Onoratobf15cb42009-08-07 14:33:40 -070025import android.graphics.PaintFlagsDrawFilter;
26import android.graphics.PixelFormat;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080027import android.graphics.Rect;
Joe Onoratobf15cb42009-08-07 14:33:40 -070028import android.graphics.RectF;
29import android.graphics.Typeface;
30import android.text.Layout.Alignment;
31import android.text.StaticLayout;
32import android.text.TextPaint;
Dianne Hackborn32ce7f12009-07-22 21:56:50 -070033import android.util.DisplayMetrics;
Joe Onoratobf15cb42009-08-07 14:33:40 -070034import android.util.Log;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080035import android.content.res.Resources;
36import android.content.Context;
37
38/**
39 * Various utilities shared amongst the Launcher's classes.
40 */
41final class Utilities {
42 private static int sIconWidth = -1;
43 private static int sIconHeight = -1;
Joe Onorato6665c0f2009-09-02 15:27:24 -070044 private static int sIconTextureWidth = -1;
45 private static int sIconTextureHeight = -1;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080046
47 private static final Paint sPaint = new Paint();
48 private static final Rect sBounds = new Rect();
49 private static final Rect sOldBounds = new Rect();
50 private static Canvas sCanvas = new Canvas();
51
52 static {
53 sCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG,
54 Paint.FILTER_BITMAP_FLAG));
55 }
56
57 static Bitmap centerToFit(Bitmap bitmap, int width, int height, Context context) {
58 final int bitmapWidth = bitmap.getWidth();
59 final int bitmapHeight = bitmap.getHeight();
60
61 if (bitmapWidth < width || bitmapHeight < height) {
62 int color = context.getResources().getColor(R.color.window_background);
63
64 Bitmap centered = Bitmap.createBitmap(bitmapWidth < width ? width : bitmapWidth,
65 bitmapHeight < height ? height : bitmapHeight, Bitmap.Config.RGB_565);
Dianne Hackborn32ce7f12009-07-22 21:56:50 -070066 centered.setDensity(bitmap.getDensity());
The Android Open Source Project31dd5032009-03-03 19:32:27 -080067 Canvas canvas = new Canvas(centered);
68 canvas.drawColor(color);
69 canvas.drawBitmap(bitmap, (width - bitmapWidth) / 2.0f, (height - bitmapHeight) / 2.0f,
70 null);
71
72 bitmap = centered;
73 }
74
75 return bitmap;
76 }
77
78 /**
79 * Returns a Drawable representing the thumbnail of the specified Drawable.
80 * The size of the thumbnail is defined by the dimension
81 * android.R.dimen.launcher_application_icon_size.
82 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -080083 * @param icon The icon to get a thumbnail of.
84 * @param context The application's context.
85 *
86 * @return A thumbnail for the specified icon or the icon itself if the
87 * thumbnail could not be created.
88 */
Joe Onorato6665c0f2009-09-02 15:27:24 -070089 static Drawable createIconThumbnail(Drawable icon, Context context) {
Joe Onorato9c1289c2009-08-17 11:03:03 -040090 synchronized (sCanvas) { // we share the statics :-(
91 if (sIconWidth == -1) {
Joe Onorato6665c0f2009-09-02 15:27:24 -070092 initStatics(context);
Mitsuru Oshima583ed3b2009-05-12 19:19:10 -070093 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -080094
Joe Onorato9c1289c2009-08-17 11:03:03 -040095 int width = sIconWidth;
96 int height = sIconHeight;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080097
Joe Onorato9c1289c2009-08-17 11:03:03 -040098 float scale = 1.0f;
99 if (icon instanceof PaintDrawable) {
100 PaintDrawable painter = (PaintDrawable) icon;
101 painter.setIntrinsicWidth(width);
102 painter.setIntrinsicHeight(height);
103 } else if (icon instanceof BitmapDrawable) {
104 // Ensure the bitmap has a density.
105 BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
106 Bitmap bitmap = bitmapDrawable.getBitmap();
107 if (bitmap.getDensity() == Bitmap.DENSITY_NONE) {
108 bitmapDrawable.setTargetDensity(context.getResources().getDisplayMetrics());
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800109 }
Joe Onorato9c1289c2009-08-17 11:03:03 -0400110 }
111 int iconWidth = icon.getIntrinsicWidth();
112 int iconHeight = icon.getIntrinsicHeight();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800113
Joe Onorato9c1289c2009-08-17 11:03:03 -0400114 if (iconWidth > 0 && iconWidth > 0) {
115 if (width < iconWidth || height < iconHeight || scale != 1.0f) {
116 final float ratio = (float) iconWidth / iconHeight;
117
118 if (iconWidth > iconHeight) {
119 height = (int) (width / ratio);
120 } else if (iconHeight > iconWidth) {
121 width = (int) (height * ratio);
122 }
123
124 final Bitmap.Config c = icon.getOpacity() != PixelFormat.OPAQUE ?
125 Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
126 final Bitmap thumb = Bitmap.createBitmap(sIconWidth, sIconHeight, c);
127 final Canvas canvas = sCanvas;
128 canvas.setBitmap(thumb);
129 // Copy the old bounds to restore them later
130 // If we were to do oldBounds = icon.getBounds(),
131 // the call to setBounds() that follows would
132 // change the same instance and we would lose the
133 // old bounds
134 sOldBounds.set(icon.getBounds());
135 final int x = (sIconWidth - width) / 2;
136 final int y = (sIconHeight - height) / 2;
137 icon.setBounds(x, y, x + width, y + height);
138 icon.draw(canvas);
139 icon.setBounds(sOldBounds);
140 icon = new FastBitmapDrawable(thumb);
141 } else if (iconWidth < width && iconHeight < height) {
142 final Bitmap.Config c = Bitmap.Config.ARGB_8888;
143 final Bitmap thumb = Bitmap.createBitmap(sIconWidth, sIconHeight, c);
144 final Canvas canvas = sCanvas;
145 canvas.setBitmap(thumb);
146 sOldBounds.set(icon.getBounds());
147 final int x = (width - iconWidth) / 2;
148 final int y = (height - iconHeight) / 2;
149 icon.setBounds(x, y, x + iconWidth, y + iconHeight);
150 icon.draw(canvas);
151 icon.setBounds(sOldBounds);
152 icon = new FastBitmapDrawable(thumb);
153 }
154 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800155
Joe Onorato9c1289c2009-08-17 11:03:03 -0400156 return icon;
157 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800158 }
159
Joe Onorato6665c0f2009-09-02 15:27:24 -0700160 static int sColors[] = { 0xffff0000, 0xff00ff00, 0xff0000ff };
161 static int sColorIndex = 0;
162
163 /**
164 * Returns a bitmap suitable for the all apps view. The bitmap will be a power
165 * of two sized ARGB_8888 bitmap that can be used as a gl texture.
166 */
167 static Bitmap createAllAppsBitmap(Drawable icon, Context context) {
168 synchronized (sCanvas) { // we share the statics :-(
169 if (sIconWidth == -1) {
170 initStatics(context);
171 }
172
173 int width = sIconWidth;
174 int height = sIconHeight;
175
176 float scale = 1.0f;
177 if (icon instanceof PaintDrawable) {
178 PaintDrawable painter = (PaintDrawable) icon;
179 painter.setIntrinsicWidth(width);
180 painter.setIntrinsicHeight(height);
181 } else if (icon instanceof BitmapDrawable) {
182 // Ensure the bitmap has a density.
183 BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
184 Bitmap bitmap = bitmapDrawable.getBitmap();
185 if (bitmap.getDensity() == Bitmap.DENSITY_NONE) {
186 bitmapDrawable.setTargetDensity(context.getResources().getDisplayMetrics());
187 }
188 }
189 int sourceWidth = icon.getIntrinsicWidth();
190 int sourceHeight = icon.getIntrinsicHeight();
191
192 if (sourceWidth > 0 && sourceWidth > 0) {
193 // There are intrinsic sizes.
194 if (width < sourceWidth || height < sourceHeight || scale != 1.0f) {
195 // It's too big, scale it down.
196 final float ratio = (float) sourceWidth / sourceHeight;
197 if (sourceWidth > sourceHeight) {
198 height = (int) (width / ratio);
199 } else if (sourceHeight > sourceWidth) {
200 width = (int) (height * ratio);
201 }
202 } else if (sourceWidth < width && sourceHeight < height) {
203 // It's small, use the size they gave us.
204 width = sourceWidth;
205 height = sourceWidth;
206 }
207 }
208
209 // no intrinsic size --> use default size
210 int textureWidth = sIconTextureWidth;
211 int textureHeight = sIconTextureHeight;
212
213 final Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight,
214 Bitmap.Config.ARGB_8888);
215 final Canvas canvas = sCanvas;
216 canvas.setBitmap(bitmap);
217
218 final int left = (textureWidth-width) / 2;
219 final int top = (textureHeight-height) / 2;
220
221 if (false) {
222 // draw a big box for the icon for debugging
223 canvas.drawColor(sColors[sColorIndex]);
224 if (++sColorIndex >= sColors.length) sColorIndex = 0;
225 Paint debugPaint = new Paint();
226 debugPaint.setColor(0xffcccc00);
227 canvas.drawRect(left, top, left+width, top+height, debugPaint);
228 }
229
230 sOldBounds.set(icon.getBounds());
231 icon.setBounds(left, top, left+width, top+height);
232 icon.draw(canvas);
233 icon.setBounds(sOldBounds);
234
235 return bitmap;
236 }
237 }
238
239
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800240 /**
241 * Returns a Bitmap representing the thumbnail of the specified Bitmap.
242 * The size of the thumbnail is defined by the dimension
243 * android.R.dimen.launcher_application_icon_size.
244 *
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800245 * @param bitmap The bitmap to get a thumbnail of.
246 * @param context The application's context.
247 *
248 * @return A thumbnail for the specified bitmap or the bitmap itself if the
249 * thumbnail could not be created.
250 */
251 static Bitmap createBitmapThumbnail(Bitmap bitmap, Context context) {
Joe Onorato9c1289c2009-08-17 11:03:03 -0400252 synchronized (sCanvas) { // we share the statics :-(
253 if (sIconWidth == -1) {
Joe Onorato6665c0f2009-09-02 15:27:24 -0700254 initStatics(context);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800255 }
256
Joe Onorato9c1289c2009-08-17 11:03:03 -0400257 int width = sIconWidth;
258 int height = sIconHeight;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800259
Joe Onorato9c1289c2009-08-17 11:03:03 -0400260 final int bitmapWidth = bitmap.getWidth();
261 final int bitmapHeight = bitmap.getHeight();
262
263 if (width > 0 && height > 0 && (width < bitmapWidth || height < bitmapHeight)) {
264 final float ratio = (float) bitmapWidth / bitmapHeight;
265
266 if (bitmapWidth > bitmapHeight) {
267 height = (int) (width / ratio);
268 } else if (bitmapHeight > bitmapWidth) {
269 width = (int) (height * ratio);
270 }
271
272 final Bitmap.Config c = (width == sIconWidth && height == sIconHeight) ?
273 bitmap.getConfig() : Bitmap.Config.ARGB_8888;
274 final Bitmap thumb = Bitmap.createBitmap(sIconWidth, sIconHeight, c);
275 final Canvas canvas = sCanvas;
276 final Paint paint = sPaint;
277 canvas.setBitmap(thumb);
278 paint.setDither(false);
279 paint.setFilterBitmap(true);
280 sBounds.set((sIconWidth - width) / 2, (sIconHeight - height) / 2, width, height);
281 sOldBounds.set(0, 0, bitmapWidth, bitmapHeight);
282 canvas.drawBitmap(bitmap, sOldBounds, sBounds, paint);
283 return thumb;
284 }
285
286 return bitmap;
287 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800288 }
Joe Onoratobf15cb42009-08-07 14:33:40 -0700289
Joe Onorato6665c0f2009-09-02 15:27:24 -0700290 private static void initStatics(Context context) {
291 final Resources resources = context.getResources();
292 sIconWidth = sIconHeight = (int) resources.getDimension(android.R.dimen.app_icon_size);
293 sIconTextureWidth = sIconTextureHeight = roundToPow2(sIconWidth);
294 }
295
Joe Onoratobf15cb42009-08-07 14:33:40 -0700296 static class BubbleText {
297 private static final int MAX_LINES = 2;
298 private TextPaint mTextPaint;
299 private Paint mRectPaint;
300
301 private float mBubblePadding;
302 private float mCornerRadius;
303 private RectF mBubbleRect = new RectF();
304
305 private float mTextWidth;
306 private int mLeading;
307 private int mFirstLineY;
308 private int mLineHeight;
309
310 private int mBitmapWidth;
311 private int mBitmapHeight;
312
313 BubbleText(Context context) {
314 final Resources resources = context.getResources();
315
316 final float scale = resources.getDisplayMetrics().density;
317
318 final float paddingLeft = 5.0f * scale;
319 final float paddingRight = 5.0f * scale;
320 final float cellWidth = resources.getDimension(R.dimen.workspace_cell_width);
321 final float bubbleWidth = cellWidth - paddingLeft - paddingRight;
Joe Onorato43e7bcf2009-08-08 18:53:53 -0700322 mBubblePadding = 3.0f * scale;
Joe Onoratobf15cb42009-08-07 14:33:40 -0700323
324 RectF bubbleRect = mBubbleRect;
325 bubbleRect.left = 0;
326 bubbleRect.top = 0;
327 bubbleRect.right = (int)(bubbleWidth+0.5f);
328
329 mCornerRadius = BubbleTextView.CORNER_RADIUS * scale;
330 mTextWidth = bubbleWidth - mBubblePadding - mBubblePadding;
331
332 Paint rectPaint = mRectPaint = new Paint();
Joe Onoratoefabe002009-08-28 09:38:18 -0700333 rectPaint.setColor(0xff000000);
Joe Onorato43e7bcf2009-08-08 18:53:53 -0700334 rectPaint.setAntiAlias(true);
Joe Onoratobf15cb42009-08-07 14:33:40 -0700335
Joe Onoratoefabe002009-08-28 09:38:18 -0700336 Log.d(Launcher.LOG_TAG, "scale=" + scale + " textSize=" + (13*scale));
337
Joe Onoratobf15cb42009-08-07 14:33:40 -0700338 TextPaint textPaint = mTextPaint = new TextPaint();
Joe Onoratoefabe002009-08-28 09:38:18 -0700339 textPaint.setTypeface(Typeface.DEFAULT);
340 textPaint.setTextSize(13*scale);
Joe Onoratobf15cb42009-08-07 14:33:40 -0700341 textPaint.setColor(0xffffffff);
Joe Onorato43e7bcf2009-08-08 18:53:53 -0700342 textPaint.setAntiAlias(true);
Joe Onoratoc567acb2009-08-31 14:34:43 -0700343 //textPaint.setShadowLayer(8, 0, 0, 0xff000000);
Joe Onoratobf15cb42009-08-07 14:33:40 -0700344
345 float ascent = -textPaint.ascent();
346 float descent = textPaint.descent();
Joe Onorato43e7bcf2009-08-08 18:53:53 -0700347 float leading = 0.0f;//(ascent+descent) * 0.1f;
Joe Onoratobf15cb42009-08-07 14:33:40 -0700348 mLeading = (int)(leading + 0.5f);
349 mFirstLineY = (int)(leading + ascent + 0.5f);
350 mLineHeight = (int)(leading + ascent + descent + 0.5f);
351
Joe Onoratobf15cb42009-08-07 14:33:40 -0700352 mBitmapWidth = roundToPow2((int)(mBubbleRect.width() + 0.5f));
Joe Onorato43e7bcf2009-08-08 18:53:53 -0700353 mBitmapHeight = roundToPow2((int)((MAX_LINES * mLineHeight) + leading + 0.5f));
Joe Onoratobf15cb42009-08-07 14:33:40 -0700354
355 Log.d(Launcher.LOG_TAG, "mBitmapWidth=" + mBitmapWidth + " mBitmapHeight="
356 + mBitmapHeight + " w=" + ((int)(mBubbleRect.width() + 0.5f))
Joe Onorato43e7bcf2009-08-08 18:53:53 -0700357 + " h=" + ((int)((MAX_LINES * mLineHeight) + leading + 0.5f)));
Joe Onoratobf15cb42009-08-07 14:33:40 -0700358 }
359
360 /** You own the bitmap after this and you must call recycle on it. */
361 Bitmap createTextBitmap(String text) {
362 Bitmap b = Bitmap.createBitmap(mBitmapWidth, mBitmapHeight, Bitmap.Config.ARGB_8888);
363 Canvas c = new Canvas(b);
364
365 StaticLayout layout = new StaticLayout(text, mTextPaint, (int)mTextWidth,
366 Alignment.ALIGN_CENTER, 1, 0, true);
367 int lineCount = layout.getLineCount();
368 if (lineCount > MAX_LINES) {
369 lineCount = MAX_LINES;
370 }
371 if (lineCount > 0) {
372 RectF bubbleRect = mBubbleRect;
Joe Onorato43e7bcf2009-08-08 18:53:53 -0700373 bubbleRect.bottom = height(lineCount);
Joe Onoratobf15cb42009-08-07 14:33:40 -0700374 c.drawRoundRect(bubbleRect, mCornerRadius, mCornerRadius, mRectPaint);
Joe Onoratobf15cb42009-08-07 14:33:40 -0700375 }
376 for (int i=0; i<lineCount; i++) {
377 int x = (int)((mBubbleRect.width() - layout.getLineMax(i)) / 2.0f);
378 int y = mFirstLineY + (i * mLineHeight);
379 c.drawText(text.substring(layout.getLineStart(i), layout.getLineEnd(i)),
380 x, y, mTextPaint);
381 }
382
383 return b;
384 }
Joe Onorato43e7bcf2009-08-08 18:53:53 -0700385
386 private int height(int lineCount) {
387 return (int)((lineCount * mLineHeight) + mLeading + mLeading + 0.0f);
388 }
389
390 int getBubbleWidth() {
391 return (int)(mBubbleRect.width() + 0.5f);
392 }
393
394 int getMaxBubbleHeight() {
395 return height(MAX_LINES);
396 }
397
398 int getBitmapWidth() {
399 return mBitmapWidth;
400 }
401
402 int getBitmapHeight() {
403 return mBitmapHeight;
404 }
Joe Onoratobf15cb42009-08-07 14:33:40 -0700405 }
406
407 /** Only works for positive numbers. */
408 static int roundToPow2(int n) {
409 int orig = n;
410 n >>= 1;
411 int mask = 0x8000000;
412 while (mask != 0 && (n & mask) == 0) {
413 mask >>= 1;
414 }
415 while (mask != 0) {
416 n |= mask;
417 mask >>= 1;
418 }
419 n += 1;
420 if (n != orig) {
421 n <<= 1;
422 }
423 return n;
424 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800425}