Utilities.java revision 9c1289cb3bfb74f86e53ec7ac6dd76bb39666b2d
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
17package com.android.launcher2;
18
19import android.graphics.drawable.BitmapDrawable;
20import android.graphics.drawable.Drawable;
21import android.graphics.drawable.PaintDrawable;
22import android.graphics.Bitmap;
23import android.graphics.Canvas;
24import android.graphics.Paint;
25import android.graphics.PaintFlagsDrawFilter;
26import android.graphics.PixelFormat;
27import android.graphics.Rect;
28import android.graphics.RectF;
29import android.graphics.Typeface;
30import android.text.Layout.Alignment;
31import android.text.StaticLayout;
32import android.text.TextPaint;
33import android.util.DisplayMetrics;
34import android.util.Log;
35import 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;
44
45    private static final Paint sPaint = new Paint();
46    private static final Rect sBounds = new Rect();
47    private static final Rect sOldBounds = new Rect();
48    private static Canvas sCanvas = new Canvas();
49
50    static {
51        sCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG,
52                Paint.FILTER_BITMAP_FLAG));
53    }
54
55    static Bitmap centerToFit(Bitmap bitmap, int width, int height, Context context) {
56        final int bitmapWidth = bitmap.getWidth();
57        final int bitmapHeight = bitmap.getHeight();
58
59        if (bitmapWidth < width || bitmapHeight < height) {
60            int color = context.getResources().getColor(R.color.window_background);
61
62            Bitmap centered = Bitmap.createBitmap(bitmapWidth < width ? width : bitmapWidth,
63                    bitmapHeight < height ? height : bitmapHeight, Bitmap.Config.RGB_565);
64            centered.setDensity(bitmap.getDensity());
65            Canvas canvas = new Canvas(centered);
66            canvas.drawColor(color);
67            canvas.drawBitmap(bitmap, (width - bitmapWidth) / 2.0f, (height - bitmapHeight) / 2.0f,
68                    null);
69
70            bitmap = centered;
71        }
72
73        return bitmap;
74    }
75
76    /**
77     * Returns a Drawable representing the thumbnail of the specified Drawable.
78     * The size of the thumbnail is defined by the dimension
79     * android.R.dimen.launcher_application_icon_size.
80     *
81     * @param icon The icon to get a thumbnail of.
82     * @param context The application's context.
83     * @param forceBitmap If this is true, the drawable will always be redrawn
84     *          into a new bitmap if it isn't already a BitmapDrawable or a
85     *          FastBitmapDrawable.
86     *
87     * @return A thumbnail for the specified icon or the icon itself if the
88     *         thumbnail could not be created.
89     */
90    static Drawable createIconThumbnail(Drawable icon, Context context, boolean forceBitmap) {
91        synchronized (sCanvas) { // we share the statics :-(
92            if (sIconWidth == -1) {
93                final Resources resources = context.getResources();
94                sIconWidth = (int)resources.getDimension(android.R.dimen.app_icon_size);
95                sIconHeight = sIconWidth;
96            }
97
98            int width = sIconWidth;
99            int height = sIconHeight;
100
101            float scale = 1.0f;
102            if (icon instanceof PaintDrawable) {
103                PaintDrawable painter = (PaintDrawable) icon;
104                painter.setIntrinsicWidth(width);
105                painter.setIntrinsicHeight(height);
106            } else if (icon instanceof BitmapDrawable) {
107                // Ensure the bitmap has a density.
108                BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
109                Bitmap bitmap = bitmapDrawable.getBitmap();
110                if (bitmap.getDensity() == Bitmap.DENSITY_NONE) {
111                    bitmapDrawable.setTargetDensity(context.getResources().getDisplayMetrics());
112                }
113            }
114            int iconWidth = icon.getIntrinsicWidth();
115            int iconHeight = icon.getIntrinsicHeight();
116
117            if (iconWidth > 0 && iconWidth > 0) {
118                if (width < iconWidth || height < iconHeight || scale != 1.0f) {
119                    final float ratio = (float) iconWidth / iconHeight;
120
121                    if (iconWidth > iconHeight) {
122                        height = (int) (width / ratio);
123                    } else if (iconHeight > iconWidth) {
124                        width = (int) (height * ratio);
125                    }
126
127                    final Bitmap.Config c = icon.getOpacity() != PixelFormat.OPAQUE ?
128                                Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
129                    final Bitmap thumb = Bitmap.createBitmap(sIconWidth, sIconHeight, c);
130                    final Canvas canvas = sCanvas;
131                    canvas.setBitmap(thumb);
132                    // Copy the old bounds to restore them later
133                    // If we were to do oldBounds = icon.getBounds(),
134                    // the call to setBounds() that follows would
135                    // change the same instance and we would lose the
136                    // old bounds
137                    sOldBounds.set(icon.getBounds());
138                    final int x = (sIconWidth - width) / 2;
139                    final int y = (sIconHeight - height) / 2;
140                    icon.setBounds(x, y, x + width, y + height);
141                    icon.draw(canvas);
142                    icon.setBounds(sOldBounds);
143                    icon = new FastBitmapDrawable(thumb);
144                } else if (iconWidth < width && iconHeight < height) {
145                    final Bitmap.Config c = Bitmap.Config.ARGB_8888;
146                    final Bitmap thumb = Bitmap.createBitmap(sIconWidth, sIconHeight, c);
147                    final Canvas canvas = sCanvas;
148                    canvas.setBitmap(thumb);
149                    sOldBounds.set(icon.getBounds());
150                    final int x = (width - iconWidth) / 2;
151                    final int y = (height - iconHeight) / 2;
152                    icon.setBounds(x, y, x + iconWidth, y + iconHeight);
153                    icon.draw(canvas);
154                    icon.setBounds(sOldBounds);
155                    icon = new FastBitmapDrawable(thumb);
156                }
157            }
158
159            if (forceBitmap) {
160                // no intrinsic size --> use default size
161                int w = sIconWidth;
162                int h = sIconHeight;
163                final Bitmap.Config c = Bitmap.Config.ARGB_8888;
164                final Bitmap thumb = Bitmap.createBitmap(roundToPow2(w), roundToPow2(h), c);
165                final Canvas canvas = sCanvas;
166                canvas.setBitmap(thumb);
167                sOldBounds.set(icon.getBounds());
168                icon.setBounds(0, 0, w, h);
169                icon.draw(canvas);
170                icon.setBounds(sOldBounds);
171                icon = new FastBitmapDrawable(thumb);
172            }
173
174            return icon;
175        }
176    }
177
178    /**
179     * Returns a Bitmap representing the thumbnail of the specified Bitmap.
180     * The size of the thumbnail is defined by the dimension
181     * android.R.dimen.launcher_application_icon_size.
182     *
183     * @param bitmap The bitmap to get a thumbnail of.
184     * @param context The application's context.
185     *
186     * @return A thumbnail for the specified bitmap or the bitmap itself if the
187     *         thumbnail could not be created.
188     */
189    static Bitmap createBitmapThumbnail(Bitmap bitmap, Context context) {
190        synchronized (sCanvas) { // we share the statics :-(
191            if (sIconWidth == -1) {
192                final Resources resources = context.getResources();
193                sIconWidth = sIconHeight = (int) resources.getDimension(
194                        android.R.dimen.app_icon_size);
195            }
196
197            int width = sIconWidth;
198            int height = sIconHeight;
199
200            final int bitmapWidth = bitmap.getWidth();
201            final int bitmapHeight = bitmap.getHeight();
202
203            if (width > 0 && height > 0 && (width < bitmapWidth || height < bitmapHeight)) {
204                final float ratio = (float) bitmapWidth / bitmapHeight;
205
206                if (bitmapWidth > bitmapHeight) {
207                    height = (int) (width / ratio);
208                } else if (bitmapHeight > bitmapWidth) {
209                    width = (int) (height * ratio);
210                }
211
212                final Bitmap.Config c = (width == sIconWidth && height == sIconHeight) ?
213                        bitmap.getConfig() : Bitmap.Config.ARGB_8888;
214                final Bitmap thumb = Bitmap.createBitmap(sIconWidth, sIconHeight, c);
215                final Canvas canvas = sCanvas;
216                final Paint paint = sPaint;
217                canvas.setBitmap(thumb);
218                paint.setDither(false);
219                paint.setFilterBitmap(true);
220                sBounds.set((sIconWidth - width) / 2, (sIconHeight - height) / 2, width, height);
221                sOldBounds.set(0, 0, bitmapWidth, bitmapHeight);
222                canvas.drawBitmap(bitmap, sOldBounds, sBounds, paint);
223                return thumb;
224            }
225
226            return bitmap;
227        }
228    }
229
230    static class BubbleText {
231        private static final int MAX_LINES = 2;
232        private TextPaint mTextPaint;
233        private Paint mRectPaint;
234
235        private float mBubblePadding;
236        private float mCornerRadius;
237        private RectF mBubbleRect = new RectF();
238
239        private float mTextWidth;
240        private int mLeading;
241        private int mFirstLineY;
242        private int mLineHeight;
243
244        private int mBitmapWidth;
245        private int mBitmapHeight;
246
247        BubbleText(Context context) {
248            final Resources resources = context.getResources();
249
250            final float scale = resources.getDisplayMetrics().density;
251
252            final float paddingLeft = 5.0f * scale;
253            final float paddingRight = 5.0f * scale;
254            final float cellWidth = resources.getDimension(R.dimen.workspace_cell_width);
255            final float bubbleWidth = cellWidth - paddingLeft - paddingRight;
256            mBubblePadding = 3.0f * scale;
257
258            RectF bubbleRect = mBubbleRect;
259            bubbleRect.left = 0;
260            bubbleRect.top = 0;
261            bubbleRect.right = (int)(bubbleWidth+0.5f);
262
263            mCornerRadius = BubbleTextView.CORNER_RADIUS * scale;
264            mTextWidth = bubbleWidth - mBubblePadding - mBubblePadding;
265
266            Paint rectPaint = mRectPaint = new Paint();
267            rectPaint.setColor(0xaa000000);
268            rectPaint.setAntiAlias(true);
269
270            TextPaint textPaint = mTextPaint = new TextPaint();
271            textPaint.setTypeface(Typeface.DEFAULT_BOLD);
272            textPaint.setTextSize(20);
273            textPaint.setColor(0xffffffff);
274            textPaint.setAntiAlias(true);
275
276            float ascent = -textPaint.ascent();
277            float descent = textPaint.descent();
278            float leading = 0.0f;//(ascent+descent) * 0.1f;
279            mLeading = (int)(leading + 0.5f);
280            mFirstLineY = (int)(leading + ascent + 0.5f);
281            mLineHeight = (int)(leading + ascent + descent + 0.5f);
282
283            mBitmapWidth = roundToPow2((int)(mBubbleRect.width() + 0.5f));
284            mBitmapHeight = roundToPow2((int)((MAX_LINES * mLineHeight) + leading + 0.5f));
285
286            Log.d(Launcher.LOG_TAG, "mBitmapWidth=" + mBitmapWidth + " mBitmapHeight="
287                    + mBitmapHeight + " w=" + ((int)(mBubbleRect.width() + 0.5f))
288                    + " h=" + ((int)((MAX_LINES * mLineHeight) + leading + 0.5f)));
289        }
290
291        /** You own the bitmap after this and you must call recycle on it. */
292        Bitmap createTextBitmap(String text) {
293            Bitmap b = Bitmap.createBitmap(mBitmapWidth, mBitmapHeight, Bitmap.Config.ARGB_8888);
294            Canvas c = new Canvas(b);
295
296            StaticLayout layout = new StaticLayout(text, mTextPaint, (int)mTextWidth,
297                    Alignment.ALIGN_CENTER, 1, 0, true);
298            int lineCount = layout.getLineCount();
299            if (lineCount > MAX_LINES) {
300                lineCount = MAX_LINES;
301            }
302            if (lineCount > 0) {
303                RectF bubbleRect = mBubbleRect;
304                bubbleRect.bottom = height(lineCount);
305                c.drawRoundRect(bubbleRect, mCornerRadius, mCornerRadius, mRectPaint);
306            }
307            for (int i=0; i<lineCount; i++) {
308                int x = (int)((mBubbleRect.width() - layout.getLineMax(i)) / 2.0f);
309                int y = mFirstLineY + (i * mLineHeight);
310                c.drawText(text.substring(layout.getLineStart(i), layout.getLineEnd(i)),
311                        x, y, mTextPaint);
312            }
313
314            return b;
315        }
316
317        private int height(int lineCount) {
318            return (int)((lineCount * mLineHeight) + mLeading + mLeading + 0.0f);
319        }
320
321        int getBubbleWidth() {
322            return (int)(mBubbleRect.width() + 0.5f);
323        }
324
325        int getMaxBubbleHeight() {
326            return height(MAX_LINES);
327        }
328
329        int getBitmapWidth() {
330            return mBitmapWidth;
331        }
332
333        int getBitmapHeight() {
334            return mBitmapHeight;
335        }
336    }
337
338    /** Only works for positive numbers. */
339    static int roundToPow2(int n) {
340        int orig = n;
341        n >>= 1;
342        int mask = 0x8000000;
343        while (mask != 0 && (n & mask) == 0) {
344            mask >>= 1;
345        }
346        while (mask != 0) {
347            n |= mask;
348            mask >>= 1;
349        }
350        n += 1;
351        if (n != orig) {
352            n <<= 1;
353        }
354        return n;
355    }
356}
357