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.content.Context; 20 import android.content.pm.PackageInfo; 21 import android.content.pm.PackageManager.NameNotFoundException; 22 import android.database.Cursor; 23 import android.os.Build; 24 import android.os.ParcelFileDescriptor; 25 import android.text.TextUtils; 26 import android.util.Log; 27 28 import java.io.Closeable; 29 import java.io.InterruptedIOException; 30 31 public class Utils { 32 private static final String TAG = "Utils"; 33 private static final String DEBUG_TAG = "GalleryDebug"; 34 35 private static final long POLY64REV = 0x95AC9329AC4BC9B5L; 36 private static final long INITIALCRC = 0xFFFFFFFFFFFFFFFFL; 37 38 private static long[] sCrcTable = new long[256]; 39 40 private static final boolean IS_DEBUG_BUILD = 41 Build.TYPE.equals("eng") || Build.TYPE.equals("userdebug"); 42 43 private static final String MASK_STRING = "********************************"; 44 45 // Throws AssertionError if the input is false. 46 public static void assertTrue(boolean cond) { 47 if (!cond) { 48 throw new AssertionError(); 49 } 50 } 51 52 // Throws AssertionError with the message. We had a method having the form 53 // assertTrue(boolean cond, String message, Object ... args); 54 // However a call to that method will cause memory allocation even if the 55 // condition is false (due to autoboxing generated by "Object ... args"), 56 // so we don't use that anymore. 57 public static void fail(String message, Object ... args) { 58 throw new AssertionError( 59 args.length == 0 ? message : String.format(message, args)); 60 } 61 62 // Throws NullPointerException if the input is null. 63 public static <T> T checkNotNull(T object) { 64 if (object == null) throw new NullPointerException(); 65 return object; 66 } 67 68 // Returns true if two input Object are both null or equal 69 // to each other. 70 public static boolean equals(Object a, Object b) { 71 return (a == b) || (a == null ? false : a.equals(b)); 72 } 73 74 // Returns the next power of two. 75 // Returns the input if it is already power of 2. 76 // Throws IllegalArgumentException if the input is <= 0 or 77 // the answer overflows. 78 public static int nextPowerOf2(int n) { 79 if (n <= 0 || n > (1 << 30)) throw new IllegalArgumentException(); 80 n -= 1; 81 n |= n >> 16; 82 n |= n >> 8; 83 n |= n >> 4; 84 n |= n >> 2; 85 n |= n >> 1; 86 return n + 1; 87 } 88 89 // Returns the previous power of two. 90 // Returns the input if it is already power of 2. 91 // Throws IllegalArgumentException if the input is <= 0 92 public static int prevPowerOf2(int n) { 93 if (n <= 0) throw new IllegalArgumentException(); 94 return Integer.highestOneBit(n); 95 } 96 97 // Returns the input value x clamped to the range [min, max]. 98 public static int clamp(int x, int min, int max) { 99 if (x > max) return max; 100 if (x < min) return min; 101 return x; 102 } 103 104 // Returns the input value x clamped to the range [min, max]. 105 public static float clamp(float x, float min, float max) { 106 if (x > max) return max; 107 if (x < min) return min; 108 return x; 109 } 110 111 // Returns the input value x clamped to the range [min, max]. 112 public static long clamp(long x, long min, long max) { 113 if (x > max) return max; 114 if (x < min) return min; 115 return x; 116 } 117 118 public static boolean isOpaque(int color) { 119 return color >>> 24 == 0xFF; 120 } 121 122 public static void swap(int[] array, int i, int j) { 123 int temp = array[i]; 124 array[i] = array[j]; 125 array[j] = temp; 126 } 127 128 /** 129 * A function thats returns a 64-bit crc for string 130 * 131 * @param in input string 132 * @return a 64-bit crc value 133 */ 134 public static final long crc64Long(String in) { 135 if (in == null || in.length() == 0) { 136 return 0; 137 } 138 return crc64Long(getBytes(in)); 139 } 140 141 static { 142 // http://bioinf.cs.ucl.ac.uk/downloads/crc64/crc64.c 143 long part; 144 for (int i = 0; i < 256; i++) { 145 part = i; 146 for (int j = 0; j < 8; j++) { 147 long x = ((int) part & 1) != 0 ? POLY64REV : 0; 148 part = (part >> 1) ^ x; 149 } 150 sCrcTable[i] = part; 151 } 152 } 153 154 public static final long crc64Long(byte[] buffer) { 155 long crc = INITIALCRC; 156 for (int k = 0, n = buffer.length; k < n; ++k) { 157 crc = sCrcTable[(((int) crc) ^ buffer[k]) & 0xff] ^ (crc >> 8); 158 } 159 return crc; 160 } 161 162 public static byte[] getBytes(String in) { 163 byte[] result = new byte[in.length() * 2]; 164 int output = 0; 165 for (char ch : in.toCharArray()) { 166 result[output++] = (byte) (ch & 0xFF); 167 result[output++] = (byte) (ch >> 8); 168 } 169 return result; 170 } 171 172 public static void closeSilently(Closeable c) { 173 if (c == null) return; 174 try { 175 c.close(); 176 } catch (Throwable t) { 177 Log.w(TAG, "close fail", t); 178 } 179 } 180 181 public static int compare(long a, long b) { 182 return a < b ? -1 : a == b ? 0 : 1; 183 } 184 185 public static int ceilLog2(float value) { 186 int i; 187 for (i = 0; i < 31; i++) { 188 if ((1 << i) >= value) break; 189 } 190 return i; 191 } 192 193 public static int floorLog2(float value) { 194 int i; 195 for (i = 0; i < 31; i++) { 196 if ((1 << i) > value) break; 197 } 198 return i - 1; 199 } 200 201 public static void closeSilently(ParcelFileDescriptor fd) { 202 try { 203 if (fd != null) fd.close(); 204 } catch (Throwable t) { 205 Log.w(TAG, "fail to close", t); 206 } 207 } 208 209 public static void closeSilently(Cursor cursor) { 210 try { 211 if (cursor != null) cursor.close(); 212 } catch (Throwable t) { 213 Log.w(TAG, "fail to close", t); 214 } 215 } 216 217 public static float interpolateAngle( 218 float source, float target, float progress) { 219 // interpolate the angle from source to target 220 // We make the difference in the range of [-179, 180], this is the 221 // shortest path to change source to target. 222 float diff = target - source; 223 if (diff < 0) diff += 360f; 224 if (diff > 180) diff -= 360f; 225 226 float result = source + diff * progress; 227 return result < 0 ? result + 360f : result; 228 } 229 230 public static float interpolateScale( 231 float source, float target, float progress) { 232 return source + progress * (target - source); 233 } 234 235 public static String ensureNotNull(String value) { 236 return value == null ? "" : value; 237 } 238 239 public static float parseFloatSafely(String content, float defaultValue) { 240 if (content == null) return defaultValue; 241 try { 242 return Float.parseFloat(content); 243 } catch (NumberFormatException e) { 244 return defaultValue; 245 } 246 } 247 248 public static int parseIntSafely(String content, int defaultValue) { 249 if (content == null) return defaultValue; 250 try { 251 return Integer.parseInt(content); 252 } catch (NumberFormatException e) { 253 return defaultValue; 254 } 255 } 256 257 public static boolean isNullOrEmpty(String exifMake) { 258 return TextUtils.isEmpty(exifMake); 259 } 260 261 public static void waitWithoutInterrupt(Object object) { 262 try { 263 object.wait(); 264 } catch (InterruptedException e) { 265 Log.w(TAG, "unexpected interrupt: " + object); 266 } 267 } 268 269 public static boolean handleInterrruptedException(Throwable e) { 270 // A helper to deal with the interrupt exception 271 // If an interrupt detected, we will setup the bit again. 272 if (e instanceof InterruptedIOException 273 || e instanceof InterruptedException) { 274 Thread.currentThread().interrupt(); 275 return true; 276 } 277 return false; 278 } 279 280 /** 281 * @return String with special XML characters escaped. 282 */ 283 public static String escapeXml(String s) { 284 StringBuilder sb = new StringBuilder(); 285 for (int i = 0, len = s.length(); i < len; ++i) { 286 char c = s.charAt(i); 287 switch (c) { 288 case '<': sb.append("<"); break; 289 case '>': sb.append(">"); break; 290 case '\"': sb.append("""); break; 291 case '\'': sb.append("'"); break; 292 case '&': sb.append("&"); break; 293 default: sb.append(c); 294 } 295 } 296 return sb.toString(); 297 } 298 299 public static String getUserAgent(Context context) { 300 PackageInfo packageInfo; 301 try { 302 packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); 303 } catch (NameNotFoundException e) { 304 throw new IllegalStateException("getPackageInfo failed"); 305 } 306 return String.format("%s/%s; %s/%s/%s/%s; %s/%s/%s", 307 packageInfo.packageName, 308 packageInfo.versionName, 309 Build.BRAND, 310 Build.DEVICE, 311 Build.MODEL, 312 Build.ID, 313 Build.VERSION.SDK, 314 Build.VERSION.RELEASE, 315 Build.VERSION.INCREMENTAL); 316 } 317 318 public static String[] copyOf(String[] source, int newSize) { 319 String[] result = new String[newSize]; 320 newSize = Math.min(source.length, newSize); 321 System.arraycopy(source, 0, result, 0, newSize); 322 return result; 323 } 324 325 // Mask information for debugging only. It returns <code>info.toString()</code> directly 326 // for debugging build (i.e., 'eng' and 'userdebug') and returns a mask ("****") 327 // in release build to protect the information (e.g. for privacy issue). 328 public static String maskDebugInfo(Object info) { 329 if (info == null) return null; 330 String s = info.toString(); 331 int length = Math.min(s.length(), MASK_STRING.length()); 332 return IS_DEBUG_BUILD ? s : MASK_STRING.substring(0, length); 333 } 334 335 // This method should be ONLY used for debugging. 336 public static void debug(String message, Object ... args) { 337 Log.v(DEBUG_TAG, String.format(message, args)); 338 } 339 } 340