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 17 package com.android.camera; 18 19 import android.app.Activity; 20 import android.app.AlertDialog; 21 import android.content.DialogInterface; 22 import android.graphics.Bitmap; 23 import android.graphics.BitmapFactory; 24 import android.graphics.Matrix; 25 import android.util.Log; 26 import android.view.View; 27 import android.view.animation.Animation; 28 import android.view.animation.TranslateAnimation; 29 30 import com.android.camera.gallery.IImage; 31 import com.android.camera.R; 32 33 import java.io.Closeable; 34 35 /** 36 * Collection of utility functions used in this package. 37 */ 38 public class Util { 39 private static final String TAG = "Util"; 40 public static final int DIRECTION_LEFT = 0; 41 public static final int DIRECTION_RIGHT = 1; 42 public static final int DIRECTION_UP = 2; 43 public static final int DIRECTION_DOWN = 3; 44 45 public static final String REVIEW_ACTION = "com.cooliris.media.action.REVIEW"; 46 47 private Util() { 48 } 49 50 // Rotates the bitmap by the specified degree. 51 // If a new bitmap is created, the original bitmap is recycled. 52 public static Bitmap rotate(Bitmap b, int degrees) { 53 if (degrees != 0 && b != null) { 54 Matrix m = new Matrix(); 55 m.setRotate(degrees, 56 (float) b.getWidth() / 2, (float) b.getHeight() / 2); 57 try { 58 Bitmap b2 = Bitmap.createBitmap( 59 b, 0, 0, b.getWidth(), b.getHeight(), m, true); 60 if (b != b2) { 61 b.recycle(); 62 b = b2; 63 } 64 } catch (OutOfMemoryError ex) { 65 // We have no memory to rotate. Return the original bitmap. 66 } 67 } 68 return b; 69 } 70 71 /* 72 * Compute the sample size as a function of minSideLength 73 * and maxNumOfPixels. 74 * minSideLength is used to specify that minimal width or height of a 75 * bitmap. 76 * maxNumOfPixels is used to specify the maximal size in pixels that is 77 * tolerable in terms of memory usage. 78 * 79 * The function returns a sample size based on the constraints. 80 * Both size and minSideLength can be passed in as IImage.UNCONSTRAINED, 81 * which indicates no care of the corresponding constraint. 82 * The functions prefers returning a sample size that 83 * generates a smaller bitmap, unless minSideLength = IImage.UNCONSTRAINED. 84 * 85 * Also, the function rounds up the sample size to a power of 2 or multiple 86 * of 8 because BitmapFactory only honors sample size this way. 87 * For example, BitmapFactory downsamples an image by 2 even though the 88 * request is 3. So we round up the sample size to avoid OOM. 89 */ 90 public static int computeSampleSize(BitmapFactory.Options options, 91 int minSideLength, int maxNumOfPixels) { 92 int initialSize = computeInitialSampleSize(options, minSideLength, 93 maxNumOfPixels); 94 95 int roundedSize; 96 if (initialSize <= 8) { 97 roundedSize = 1; 98 while (roundedSize < initialSize) { 99 roundedSize <<= 1; 100 } 101 } else { 102 roundedSize = (initialSize + 7) / 8 * 8; 103 } 104 105 return roundedSize; 106 } 107 108 private static int computeInitialSampleSize(BitmapFactory.Options options, 109 int minSideLength, int maxNumOfPixels) { 110 double w = options.outWidth; 111 double h = options.outHeight; 112 113 int lowerBound = (maxNumOfPixels == IImage.UNCONSTRAINED) ? 1 : 114 (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels)); 115 int upperBound = (minSideLength == IImage.UNCONSTRAINED) ? 128 : 116 (int) Math.min(Math.floor(w / minSideLength), 117 Math.floor(h / minSideLength)); 118 119 if (upperBound < lowerBound) { 120 // return the larger one when there is no overlapping zone. 121 return lowerBound; 122 } 123 124 if ((maxNumOfPixels == IImage.UNCONSTRAINED) && 125 (minSideLength == IImage.UNCONSTRAINED)) { 126 return 1; 127 } else if (minSideLength == IImage.UNCONSTRAINED) { 128 return lowerBound; 129 } else { 130 return upperBound; 131 } 132 } 133 134 public static <T> int indexOf(T [] array, T s) { 135 for (int i = 0; i < array.length; i++) { 136 if (array[i].equals(s)) { 137 return i; 138 } 139 } 140 return -1; 141 } 142 143 public static void closeSilently(Closeable c) { 144 if (c == null) return; 145 try { 146 c.close(); 147 } catch (Throwable t) { 148 // do nothing 149 } 150 } 151 152 public static Bitmap makeBitmap(byte[] jpegData, int maxNumOfPixels) { 153 try { 154 BitmapFactory.Options options = new BitmapFactory.Options(); 155 options.inJustDecodeBounds = true; 156 BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length, 157 options); 158 if (options.mCancel || options.outWidth == -1 159 || options.outHeight == -1) { 160 return null; 161 } 162 options.inSampleSize = computeSampleSize( 163 options, IImage.UNCONSTRAINED, maxNumOfPixels); 164 options.inJustDecodeBounds = false; 165 166 options.inDither = false; 167 options.inPreferredConfig = Bitmap.Config.ARGB_8888; 168 return BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length, 169 options); 170 } catch (OutOfMemoryError ex) { 171 Log.e(TAG, "Got oom exception ", ex); 172 return null; 173 } 174 } 175 176 public static void Assert(boolean cond) { 177 if (!cond) { 178 throw new AssertionError(); 179 } 180 } 181 182 public static void showFatalErrorAndFinish( 183 final Activity activity, String title, String message) { 184 DialogInterface.OnClickListener buttonListener = 185 new DialogInterface.OnClickListener() { 186 public void onClick(DialogInterface dialog, int which) { 187 activity.finish(); 188 } 189 }; 190 new AlertDialog.Builder(activity) 191 .setCancelable(false) 192 .setIcon(android.R.drawable.ic_dialog_alert) 193 .setTitle(title) 194 .setMessage(message) 195 .setNeutralButton(R.string.details_ok, buttonListener) 196 .show(); 197 } 198 199 public static Animation slideOut(View view, int to) { 200 view.setVisibility(View.INVISIBLE); 201 Animation anim; 202 switch (to) { 203 case DIRECTION_LEFT: 204 anim = new TranslateAnimation(0, -view.getWidth(), 0, 0); 205 break; 206 case DIRECTION_RIGHT: 207 anim = new TranslateAnimation(0, view.getWidth(), 0, 0); 208 break; 209 case DIRECTION_UP: 210 anim = new TranslateAnimation(0, 0, 0, -view.getHeight()); 211 break; 212 case DIRECTION_DOWN: 213 anim = new TranslateAnimation(0, 0, 0, view.getHeight()); 214 break; 215 default: 216 throw new IllegalArgumentException(Integer.toString(to)); 217 } 218 anim.setDuration(500); 219 view.startAnimation(anim); 220 return anim; 221 } 222 223 public static Animation slideIn(View view, int from) { 224 view.setVisibility(View.VISIBLE); 225 Animation anim; 226 switch (from) { 227 case DIRECTION_LEFT: 228 anim = new TranslateAnimation(-view.getWidth(), 0, 0, 0); 229 break; 230 case DIRECTION_RIGHT: 231 anim = new TranslateAnimation(view.getWidth(), 0, 0, 0); 232 break; 233 case DIRECTION_UP: 234 anim = new TranslateAnimation(0, 0, -view.getHeight(), 0); 235 break; 236 case DIRECTION_DOWN: 237 anim = new TranslateAnimation(0, 0, view.getHeight(), 0); 238 break; 239 default: 240 throw new IllegalArgumentException(Integer.toString(from)); 241 } 242 anim.setDuration(500); 243 view.startAnimation(anim); 244 return anim; 245 } 246 247 public static <T> T checkNotNull(T object) { 248 if (object == null) throw new NullPointerException(); 249 return object; 250 } 251 252 public static boolean equals(Object a, Object b) { 253 return (a == b) || (a == null ? false : a.equals(b)); 254 } 255 256 public static boolean isPowerOf2(int n) { 257 return (n & -n) == n; 258 } 259 260 public static int nextPowerOf2(int n) { 261 n -= 1; 262 n |= n >>> 16; 263 n |= n >>> 8; 264 n |= n >>> 4; 265 n |= n >>> 2; 266 n |= n >>> 1; 267 return n + 1; 268 } 269 270 public static float distance(float x, float y, float sx, float sy) { 271 float dx = x - sx; 272 float dy = y - sy; 273 return (float) Math.sqrt(dx * dx + dy * dy); 274 } 275 276 public static int clamp(int x, int min, int max) { 277 if (x > max) return max; 278 if (x < min) return min; 279 return x; 280 } 281 } 282