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