Util.java revision 46d402ade45b258f4515c42b6940749364557fb3
1/* 2 * Copyright (C) 2009 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.camera; 18 19import android.app.Activity; 20import android.app.AlertDialog; 21import android.app.ProgressDialog; 22import android.content.ContentResolver; 23import android.content.DialogInterface; 24import android.content.Intent; 25import android.graphics.Bitmap; 26import android.graphics.BitmapFactory; 27import android.graphics.Canvas; 28import android.graphics.Matrix; 29import android.graphics.Rect; 30import android.net.Uri; 31import android.os.Handler; 32import android.os.ParcelFileDescriptor; 33import android.util.Log; 34import android.view.View; 35import android.view.animation.Animation; 36import android.view.animation.TranslateAnimation; 37 38import com.android.camera.gallery.IImage; 39 40import java.io.Closeable; 41import java.io.FileDescriptor; 42import java.io.IOException; 43 44/** 45 * Collection of utility functions used in this package. 46 */ 47public class Util { 48 private static final String TAG = "Util"; 49 public static final int DIRECTION_LEFT = 0; 50 public static final int DIRECTION_RIGHT = 1; 51 public static final int DIRECTION_UP = 2; 52 public static final int DIRECTION_DOWN = 3; 53 54 private Util() { 55 } 56 57 // Rotates the bitmap by the specified degree. 58 // If a new bitmap is created, the original bitmap is recycled. 59 public static Bitmap rotate(Bitmap b, int degrees) { 60 if (degrees != 0 && b != null) { 61 Matrix m = new Matrix(); 62 m.setRotate(degrees, 63 (float) b.getWidth() / 2, (float) b.getHeight() / 2); 64 try { 65 Bitmap b2 = Bitmap.createBitmap( 66 b, 0, 0, b.getWidth(), b.getHeight(), m, true); 67 if (b != b2) { 68 b.recycle(); 69 b = b2; 70 } 71 } catch (OutOfMemoryError ex) { 72 // We have no memory to rotate. Return the original bitmap. 73 } 74 } 75 return b; 76 } 77 78 /* 79 * Compute the sample size as a function of minSideLength 80 * and maxNumOfPixels. 81 * minSideLength is used to specify that minimal width or height of a 82 * bitmap. 83 * maxNumOfPixels is used to specify the maximal size in pixels that is 84 * tolerable in terms of memory usage. 85 * 86 * The function returns a sample size based on the constraints. 87 * Both size and minSideLength can be passed in as IImage.UNCONSTRAINED, 88 * which indicates no care of the corresponding constraint. 89 * The functions prefers returning a sample size that 90 * generates a smaller bitmap, unless minSideLength = IImage.UNCONSTRAINED. 91 * 92 * Also, the function rounds up the sample size to a power of 2 or multiple 93 * of 8 because BitmapFactory only honors sample size this way. 94 * For example, BitmapFactory downsamples an image by 2 even though the 95 * request is 3. So we round up the sample size to avoid OOM. 96 */ 97 public static int computeSampleSize(BitmapFactory.Options options, 98 int minSideLength, int maxNumOfPixels) { 99 int initialSize = computeInitialSampleSize(options, minSideLength, 100 maxNumOfPixels); 101 102 int roundedSize; 103 if (initialSize <= 8) { 104 roundedSize = 1; 105 while (roundedSize < initialSize) { 106 roundedSize <<= 1; 107 } 108 } else { 109 roundedSize = (initialSize + 7) / 8 * 8; 110 } 111 112 return roundedSize; 113 } 114 115 private static int computeInitialSampleSize(BitmapFactory.Options options, 116 int minSideLength, int maxNumOfPixels) { 117 double w = options.outWidth; 118 double h = options.outHeight; 119 120 int lowerBound = (maxNumOfPixels == IImage.UNCONSTRAINED) ? 1 : 121 (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels)); 122 int upperBound = (minSideLength == IImage.UNCONSTRAINED) ? 128 : 123 (int) Math.min(Math.floor(w / minSideLength), 124 Math.floor(h / minSideLength)); 125 126 if (upperBound < lowerBound) { 127 // return the larger one when there is no overlapping zone. 128 return lowerBound; 129 } 130 131 if ((maxNumOfPixels == IImage.UNCONSTRAINED) && 132 (minSideLength == IImage.UNCONSTRAINED)) { 133 return 1; 134 } else if (minSideLength == IImage.UNCONSTRAINED) { 135 return lowerBound; 136 } else { 137 return upperBound; 138 } 139 } 140 141 public static <T> int indexOf(T [] array, T s) { 142 for (int i = 0; i < array.length; i++) { 143 if (array[i].equals(s)) { 144 return i; 145 } 146 } 147 return -1; 148 } 149 150 public static void closeSilently(Closeable c) { 151 if (c == null) return; 152 try { 153 c.close(); 154 } catch (Throwable t) { 155 // do nothing 156 } 157 } 158 159 public static void closeSilently(ParcelFileDescriptor c) { 160 if (c == null) return; 161 try { 162 c.close(); 163 } catch (Throwable t) { 164 // do nothing 165 } 166 } 167 168 /** 169 * Make a bitmap from a given Uri. 170 * 171 * @param uri 172 */ 173 public static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels, 174 Uri uri, ContentResolver cr, boolean useNative) { 175 ParcelFileDescriptor input = null; 176 try { 177 input = cr.openFileDescriptor(uri, "r"); 178 BitmapFactory.Options options = null; 179 if (useNative) { 180 options = createNativeAllocOptions(); 181 } 182 return makeBitmap(minSideLength, maxNumOfPixels, uri, cr, input, 183 options); 184 } catch (IOException ex) { 185 return null; 186 } finally { 187 closeSilently(input); 188 } 189 } 190 191 public static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels, 192 ParcelFileDescriptor pfd, boolean useNative) { 193 BitmapFactory.Options options = null; 194 if (useNative) { 195 options = createNativeAllocOptions(); 196 } 197 return makeBitmap(minSideLength, maxNumOfPixels, null, null, pfd, 198 options); 199 } 200 201 public static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels, 202 Uri uri, ContentResolver cr, ParcelFileDescriptor pfd, 203 BitmapFactory.Options options) { 204 try { 205 if (pfd == null) pfd = makeInputStream(uri, cr); 206 if (pfd == null) return null; 207 if (options == null) options = new BitmapFactory.Options(); 208 209 FileDescriptor fd = pfd.getFileDescriptor(); 210 options.inJustDecodeBounds = true; 211 BitmapManager.instance().decodeFileDescriptor(fd, options); 212 if (options.mCancel || options.outWidth == -1 213 || options.outHeight == -1) { 214 return null; 215 } 216 options.inSampleSize = computeSampleSize( 217 options, minSideLength, maxNumOfPixels); 218 options.inJustDecodeBounds = false; 219 220 options.inDither = false; 221 options.inPreferredConfig = Bitmap.Config.ARGB_8888; 222 return BitmapManager.instance().decodeFileDescriptor(fd, options); 223 } catch (OutOfMemoryError ex) { 224 Log.e(TAG, "Got oom exception ", ex); 225 return null; 226 } finally { 227 closeSilently(pfd); 228 } 229 } 230 231 public static Bitmap makeBitmap(byte[] jpegData, int maxNumOfPixels) { 232 try { 233 BitmapFactory.Options options = new BitmapFactory.Options(); 234 options.inJustDecodeBounds = true; 235 BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length, 236 options); 237 if (options.mCancel || options.outWidth == -1 238 || options.outHeight == -1) { 239 return null; 240 } 241 options.inSampleSize = computeSampleSize( 242 options, IImage.UNCONSTRAINED, maxNumOfPixels); 243 options.inJustDecodeBounds = false; 244 245 options.inDither = false; 246 options.inPreferredConfig = Bitmap.Config.ARGB_8888; 247 return BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length, 248 options); 249 } catch (OutOfMemoryError ex) { 250 Log.e(TAG, "Got oom exception ", ex); 251 return null; 252 } 253 } 254 255 private static ParcelFileDescriptor makeInputStream( 256 Uri uri, ContentResolver cr) { 257 try { 258 return cr.openFileDescriptor(uri, "r"); 259 } catch (IOException ex) { 260 return null; 261 } 262 } 263 264 public static void Assert(boolean cond) { 265 if (!cond) { 266 throw new AssertionError(); 267 } 268 } 269 270 public static boolean equals(String a, String b) { 271 // return true if both string are null or the content equals 272 return a == b || a.equals(b); 273 } 274 275 // Returns an intent which is used for "set as" menu items. 276 public static Intent createSetAsIntent(IImage image) { 277 Uri u = image.fullSizeImageUri(); 278 Intent intent = new Intent(Intent.ACTION_ATTACH_DATA); 279 intent.setDataAndType(u, image.getMimeType()); 280 intent.putExtra("mimeType", image.getMimeType()); 281 return intent; 282 } 283 284 // Returns Options that set the puregeable flag for Bitmap decode. 285 public static BitmapFactory.Options createNativeAllocOptions() { 286 BitmapFactory.Options options = new BitmapFactory.Options(); 287 options.inNativeAlloc = true; 288 return options; 289 } 290 291 public static void showFatalErrorAndFinish( 292 final Activity activity, String title, String message) { 293 DialogInterface.OnClickListener buttonListener = 294 new DialogInterface.OnClickListener() { 295 public void onClick(DialogInterface dialog, int which) { 296 activity.finish(); 297 } 298 }; 299 new AlertDialog.Builder(activity) 300 .setCancelable(false) 301 .setIcon(android.R.drawable.ic_dialog_alert) 302 .setTitle(title) 303 .setMessage(message) 304 .setNeutralButton(R.string.details_ok, buttonListener) 305 .show(); 306 } 307 308 public static Animation slideOut(View view, int to) { 309 view.setVisibility(View.INVISIBLE); 310 Animation anim; 311 switch (to) { 312 case DIRECTION_LEFT: 313 anim = new TranslateAnimation(0, -view.getWidth(), 0, 0); 314 break; 315 case DIRECTION_RIGHT: 316 anim = new TranslateAnimation(0, view.getWidth(), 0, 0); 317 break; 318 case DIRECTION_UP: 319 anim = new TranslateAnimation(0, 0, 0, -view.getHeight()); 320 break; 321 case DIRECTION_DOWN: 322 anim = new TranslateAnimation(0, 0, 0, view.getHeight()); 323 break; 324 default: 325 throw new IllegalArgumentException(Integer.toString(to)); 326 } 327 anim.setDuration(500); 328 view.startAnimation(anim); 329 return anim; 330 } 331 332 public static Animation slideIn(View view, int from) { 333 view.setVisibility(View.VISIBLE); 334 Animation anim; 335 switch (from) { 336 case DIRECTION_LEFT: 337 anim = new TranslateAnimation(-view.getWidth(), 0, 0, 0); 338 break; 339 case DIRECTION_RIGHT: 340 anim = new TranslateAnimation(view.getWidth(), 0, 0, 0); 341 break; 342 case DIRECTION_UP: 343 anim = new TranslateAnimation(0, 0, -view.getHeight(), 0); 344 break; 345 case DIRECTION_DOWN: 346 anim = new TranslateAnimation(0, 0, view.getHeight(), 0); 347 break; 348 default: 349 throw new IllegalArgumentException(Integer.toString(from)); 350 } 351 anim.setDuration(500); 352 view.startAnimation(anim); 353 return anim; 354 } 355} 356