1 /* 2 * Copyright (C) 2010 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.gallery3d.common; 18 19 import android.app.PendingIntent; 20 import android.content.Context; 21 import android.content.pm.PackageInfo; 22 import android.content.pm.PackageManager.NameNotFoundException; 23 import android.database.Cursor; 24 import android.os.Build; 25 import android.os.Environment; 26 import android.os.Parcel; 27 import android.os.ParcelFileDescriptor; 28 import android.os.StatFs; 29 import android.text.TextUtils; 30 import android.util.Log; 31 32 import java.io.Closeable; 33 import java.io.InterruptedIOException; 34 import java.util.Random; 35 36 public class Utils { 37 private static final String TAG = "Utils"; 38 private static final String DEBUG_TAG = "GalleryDebug"; 39 40 private static final long POLY64REV = 0x95AC9329AC4BC9B5L; 41 private static final long INITIALCRC = 0xFFFFFFFFFFFFFFFFL; 42 43 private static long[] sCrcTable = new long[256]; 44 45 private static final boolean IS_DEBUG_BUILD = 46 Build.TYPE.equals("eng") || Build.TYPE.equals("userdebug"); 47 48 private static final String MASK_STRING = "********************************"; 49 50 // Throws AssertionError if the input is false. 51 public static void assertTrue(boolean cond) { 52 if (!cond) { 53 throw new AssertionError(); 54 } 55 } 56 57 // Throws AssertionError if the input is false. 58 public static void assertTrue(boolean cond, String message, Object ... args) { 59 if (!cond) { 60 throw new AssertionError( 61 args.length == 0 ? message : String.format(message, args)); 62 } 63 } 64 65 // Throws NullPointerException if the input is null. 66 public static <T> T checkNotNull(T object) { 67 if (object == null) throw new NullPointerException(); 68 return object; 69 } 70 71 // Returns true if two input Object are both null or equal 72 // to each other. 73 public static boolean equals(Object a, Object b) { 74 return (a == b) || (a == null ? false : a.equals(b)); 75 } 76 77 // Returns true if the input is power of 2. 78 // Throws IllegalArgumentException if the input is <= 0. 79 public static boolean isPowerOf2(int n) { 80 if (n <= 0) throw new IllegalArgumentException(); 81 return (n & -n) == n; 82 } 83 84 // Returns the next power of two. 85 // Returns the input if it is already power of 2. 86 // Throws IllegalArgumentException if the input is <= 0 or 87 // the answer overflows. 88 public static int nextPowerOf2(int n) { 89 if (n <= 0 || n > (1 << 30)) throw new IllegalArgumentException(); 90 n -= 1; 91 n |= n >> 16; 92 n |= n >> 8; 93 n |= n >> 4; 94 n |= n >> 2; 95 n |= n >> 1; 96 return n + 1; 97 } 98 99 // Returns the previous power of two. 100 // Returns the input if it is already power of 2. 101 // Throws IllegalArgumentException if the input is <= 0 102 public static int prevPowerOf2(int n) { 103 if (n <= 0) throw new IllegalArgumentException(); 104 return Integer.highestOneBit(n); 105 } 106 107 // Returns the euclidean distance between (x, y) and (sx, sy). 108 public static float distance(float x, float y, float sx, float sy) { 109 float dx = x - sx; 110 float dy = y - sy; 111 return (float) Math.hypot(dx, dy); 112 } 113 114 // Returns the input value x clamped to the range [min, max]. 115 public static int clamp(int x, int min, int max) { 116 if (x > max) return max; 117 if (x < min) return min; 118 return x; 119 } 120 121 // Returns the input value x clamped to the range [min, max]. 122 public static float clamp(float x, float min, float max) { 123 if (x > max) return max; 124 if (x < min) return min; 125 return x; 126 } 127 128 // Returns the input value x clamped to the range [min, max]. 129 public static long clamp(long x, long min, long max) { 130 if (x > max) return max; 131 if (x < min) return min; 132 return x; 133 } 134 135 public static boolean isOpaque(int color) { 136 return color >>> 24 == 0xFF; 137 } 138 139 public static <T> void swap(T[] array, int i, int j) { 140 T temp = array[i]; 141 array[i] = array[j]; 142 array[j] = temp; 143 } 144 145 public static void swap(int[] array, int i, int j) { 146 int temp = array[i]; 147 array[i] = array[j]; 148 array[j] = temp; 149 } 150 151 /** 152 * A function thats returns a 64-bit crc for string 153 * 154 * @param in input string 155 * @return a 64-bit crc value 156 */ 157 public static final long crc64Long(String in) { 158 if (in == null || in.length() == 0) { 159 return 0; 160 } 161 return crc64Long(getBytes(in)); 162 } 163 164 static { 165 // http://bioinf.cs.ucl.ac.uk/downloads/crc64/crc64.c 166 long part; 167 for (int i = 0; i < 256; i++) { 168 part = i; 169 for (int j = 0; j < 8; j++) { 170 long x = ((int) part & 1) != 0 ? POLY64REV : 0; 171 part = (part >> 1) ^ x; 172 } 173 sCrcTable[i] = part; 174 } 175 } 176 177 public static final long crc64Long(byte[] buffer) { 178 long crc = INITIALCRC; 179 for (int k = 0, n = buffer.length; k < n; ++k) { 180 crc = sCrcTable[(((int) crc) ^ buffer[k]) & 0xff] ^ (crc >> 8); 181 } 182 return crc; 183 } 184 185 public static byte[] getBytes(String in) { 186 byte[] result = new byte[in.length() * 2]; 187 int output = 0; 188 for (char ch : in.toCharArray()) { 189 result[output++] = (byte) (ch & 0xFF); 190 result[output++] = (byte) (ch >> 8); 191 } 192 return result; 193 } 194 195 public static void closeSilently(Closeable c) { 196 if (c == null) return; 197 try { 198 c.close(); 199 } catch (Throwable t) { 200 Log.w(TAG, "close fail", t); 201 } 202 } 203 204 public static int compare(long a, long b) { 205 return a < b ? -1 : a == b ? 0 : 1; 206 } 207 208 public static int ceilLog2(float value) { 209 int i; 210 for (i = 0; i < 31; i++) { 211 if ((1 << i) >= value) break; 212 } 213 return i; 214 } 215 216 public static int floorLog2(float value) { 217 int i; 218 for (i = 0; i < 31; i++) { 219 if ((1 << i) > value) break; 220 } 221 return i - 1; 222 } 223 224 public static void closeSilently(ParcelFileDescriptor fd) { 225 try { 226 if (fd != null) fd.close(); 227 } catch (Throwable t) { 228 Log.w(TAG, "fail to close", t); 229 } 230 } 231 232 public static void closeSilently(Cursor cursor) { 233 try { 234 if (cursor != null) cursor.close(); 235 } catch (Throwable t) { 236 Log.w(TAG, "fail to close", t); 237 } 238 } 239 240 public static float interpolateAngle( 241 float source, float target, float progress) { 242 // interpolate the angle from source to target 243 // We make the difference in the range of [-179, 180], this is the 244 // shortest path to change source to target. 245 float diff = target - source; 246 if (diff < 0) diff += 360f; 247 if (diff > 180) diff -= 360f; 248 249 float result = source + diff * progress; 250 return result < 0 ? result + 360f : result; 251 } 252 253 public static float interpolateScale( 254 float source, float target, float progress) { 255 return source + progress * (target - source); 256 } 257 258 public static String ensureNotNull(String value) { 259 return value == null ? "" : value; 260 } 261 262 // Used for debugging. Should be removed before submitting. 263 public static void debug(String format, Object ... args) { 264 if (args.length == 0) { 265 Log.d(DEBUG_TAG, format); 266 } else { 267 Log.d(DEBUG_TAG, String.format(format, args)); 268 } 269 } 270 271 public static float parseFloatSafely(String content, float defaultValue) { 272 if (content == null) return defaultValue; 273 try { 274 return Float.parseFloat(content); 275 } catch (NumberFormatException e) { 276 return defaultValue; 277 } 278 } 279 280 public static int parseIntSafely(String content, int defaultValue) { 281 if (content == null) return defaultValue; 282 try { 283 return Integer.parseInt(content); 284 } catch (NumberFormatException e) { 285 return defaultValue; 286 } 287 } 288 289 public static boolean isNullOrEmpty(String exifMake) { 290 return TextUtils.isEmpty(exifMake); 291 } 292 293 public static boolean hasSpaceForSize(long size) { 294 String state = Environment.getExternalStorageState(); 295 if (!Environment.MEDIA_MOUNTED.equals(state)) { 296 return false; 297 } 298 299 String path = Environment.getExternalStorageDirectory().getPath(); 300 try { 301 StatFs stat = new StatFs(path); 302 return stat.getAvailableBlocks() * (long) stat.getBlockSize() > size; 303 } catch (Exception e) { 304 Log.i(TAG, "Fail to access external storage", e); 305 } 306 return false; 307 } 308 309 public static void waitWithoutInterrupt(Object object) { 310 try { 311 object.wait(); 312 } catch (InterruptedException e) { 313 Log.w(TAG, "unexpected interrupt: " + object); 314 } 315 } 316 317 public static void shuffle(int array[], Random random) { 318 for (int i = array.length; i > 0; --i) { 319 int t = random.nextInt(i); 320 if (t == i - 1) continue; 321 int tmp = array[i - 1]; 322 array[i - 1] = array[t]; 323 array[t] = tmp; 324 } 325 } 326 327 public static boolean handleInterrruptedException(Throwable e) { 328 // A helper to deal with the interrupt exception 329 // If an interrupt detected, we will setup the bit again. 330 if (e instanceof InterruptedIOException 331 || e instanceof InterruptedException) { 332 Thread.currentThread().interrupt(); 333 return true; 334 } 335 return false; 336 } 337 338 /** 339 * @return String with special XML characters escaped. 340 */ 341 public static String escapeXml(String s) { 342 StringBuilder sb = new StringBuilder(); 343 for (int i = 0, len = s.length(); i < len; ++i) { 344 char c = s.charAt(i); 345 switch (c) { 346 case '<': sb.append("<"); break; 347 case '>': sb.append(">"); break; 348 case '\"': sb.append("""); break; 349 case '\'': sb.append("'"); break; 350 case '&': sb.append("&"); break; 351 default: sb.append(c); 352 } 353 } 354 return sb.toString(); 355 } 356 357 public static String getUserAgent(Context context) { 358 PackageInfo packageInfo; 359 try { 360 packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); 361 } catch (NameNotFoundException e) { 362 throw new IllegalStateException("getPackageInfo failed"); 363 } 364 return String.format("%s/%s; %s/%s/%s/%s; %s/%s/%s", 365 packageInfo.packageName, 366 packageInfo.versionName, 367 Build.BRAND, 368 Build.DEVICE, 369 Build.MODEL, 370 Build.ID, 371 Build.VERSION.SDK, 372 Build.VERSION.RELEASE, 373 Build.VERSION.INCREMENTAL); 374 } 375 376 public static String[] copyOf(String[] source, int newSize) { 377 String[] result = new String[newSize]; 378 newSize = Math.min(source.length, newSize); 379 System.arraycopy(source, 0, result, 0, newSize); 380 return result; 381 } 382 383 public static PendingIntent deserializePendingIntent(byte[] rawPendingIntent) { 384 Parcel parcel = null; 385 try { 386 if (rawPendingIntent != null) { 387 parcel = Parcel.obtain(); 388 parcel.unmarshall(rawPendingIntent, 0, rawPendingIntent.length); 389 return PendingIntent.readPendingIntentOrNullFromParcel(parcel); 390 } else { 391 return null; 392 } 393 } catch (Exception e) { 394 throw new IllegalArgumentException("error parsing PendingIntent"); 395 } finally { 396 if (parcel != null) parcel.recycle(); 397 } 398 } 399 400 public static byte[] serializePendingIntent(PendingIntent pendingIntent) { 401 Parcel parcel = null; 402 try { 403 parcel = Parcel.obtain(); 404 PendingIntent.writePendingIntentOrNullToParcel(pendingIntent, parcel); 405 return parcel.marshall(); 406 } finally { 407 if (parcel != null) parcel.recycle(); 408 } 409 } 410 411 // Mask information for debugging only. It returns <code>info.toString()</code> directly 412 // for debugging build (i.e., 'eng' and 'userdebug') and returns a mask ("****") 413 // in release build to protect the information (e.g. for privacy issue). 414 public static String maskDebugInfo(Object info) { 415 if (info == null) return null; 416 String s = info.toString(); 417 int length = Math.min(s.length(), MASK_STRING.length()); 418 return IS_DEBUG_BUILD ? s : MASK_STRING.substring(0, length); 419 } 420 } 421