Util.java revision f2229614847f060be34593757ca402ae62c7cc7d
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.annotation.TargetApi; 20import android.app.Activity; 21import android.app.AlertDialog; 22import android.app.admin.DevicePolicyManager; 23import android.content.ActivityNotFoundException; 24import android.content.ContentResolver; 25import android.content.Context; 26import android.content.DialogInterface; 27import android.content.Intent; 28import android.graphics.Bitmap; 29import android.graphics.BitmapFactory; 30import android.graphics.Matrix; 31import android.graphics.Point; 32import android.graphics.Rect; 33import android.graphics.RectF; 34import android.hardware.Camera; 35import android.hardware.Camera.CameraInfo; 36import android.hardware.Camera.Parameters; 37import android.hardware.Camera.Size; 38import android.location.Location; 39import android.net.Uri; 40import android.os.Build; 41import android.os.ParcelFileDescriptor; 42import android.telephony.TelephonyManager; 43import android.util.DisplayMetrics; 44import android.util.FloatMath; 45import android.util.Log; 46import android.view.Display; 47import android.view.OrientationEventListener; 48import android.view.Surface; 49import android.view.View; 50import android.view.WindowManager; 51import android.view.animation.AlphaAnimation; 52import android.view.animation.Animation; 53 54import com.android.gallery3d.common.ApiHelper; 55 56import java.io.Closeable; 57import java.io.IOException; 58import java.lang.reflect.Method; 59import java.text.SimpleDateFormat; 60import java.util.Date; 61import java.util.List; 62import java.util.StringTokenizer; 63 64/** 65 * Collection of utility functions used in this package. 66 */ 67public class Util { 68 private static final String TAG = "Util"; 69 70 // Orientation hysteresis amount used in rounding, in degrees 71 public static final int ORIENTATION_HYSTERESIS = 5; 72 73 public static final String REVIEW_ACTION = "com.android.camera.action.REVIEW"; 74 // See android.hardware.Camera.ACTION_NEW_PICTURE. 75 public static final String ACTION_NEW_PICTURE = "android.hardware.action.NEW_PICTURE"; 76 // See android.hardware.Camera.ACTION_NEW_VIDEO. 77 public static final String ACTION_NEW_VIDEO = "android.hardware.action.NEW_VIDEO"; 78 79 // Fields from android.hardware.Camera.Parameters 80 public static final String FOCUS_MODE_CONTINUOUS_PICTURE = "continuous-picture"; 81 public static final String RECORDING_HINT = "recording-hint"; 82 private static final String AUTO_EXPOSURE_LOCK_SUPPORTED = "auto-exposure-lock-supported"; 83 private static final String AUTO_WHITE_BALANCE_LOCK_SUPPORTED = "auto-whitebalance-lock-supported"; 84 private static final String VIDEO_SNAPSHOT_SUPPORTED = "video-snapshot-supported"; 85 public static final String TRUE = "true"; 86 public static final String FALSE = "false"; 87 88 public static boolean isAutoExposureLockSupported(Parameters params) { 89 return TRUE.equals(params.get(AUTO_EXPOSURE_LOCK_SUPPORTED)); 90 } 91 92 public static boolean isAutoWhiteBalanceLockSupported(Parameters params) { 93 return TRUE.equals(params.get(AUTO_WHITE_BALANCE_LOCK_SUPPORTED)); 94 } 95 96 public static boolean isVideoSnapshotSupported(Parameters params) { 97 return TRUE.equals(params.get(VIDEO_SNAPSHOT_SUPPORTED)); 98 } 99 100 101 // Private intent extras. Test only. 102 private static final String EXTRAS_CAMERA_FACING = 103 "android.intent.extras.CAMERA_FACING"; 104 105 private static float sPixelDensity = 1; 106 private static ImageFileNamer sImageFileNamer; 107 108 private Util() { 109 } 110 111 public static void initialize(Context context) { 112 DisplayMetrics metrics = new DisplayMetrics(); 113 WindowManager wm = (WindowManager) 114 context.getSystemService(Context.WINDOW_SERVICE); 115 wm.getDefaultDisplay().getMetrics(metrics); 116 sPixelDensity = metrics.density; 117 sImageFileNamer = new ImageFileNamer( 118 context.getString(R.string.image_file_name_format)); 119 } 120 121 public static int dpToPixel(int dp) { 122 return Math.round(sPixelDensity * dp); 123 } 124 125 // Rotates the bitmap by the specified degree. 126 // If a new bitmap is created, the original bitmap is recycled. 127 public static Bitmap rotate(Bitmap b, int degrees) { 128 return rotateAndMirror(b, degrees, false); 129 } 130 131 // Rotates and/or mirrors the bitmap. If a new bitmap is created, the 132 // original bitmap is recycled. 133 public static Bitmap rotateAndMirror(Bitmap b, int degrees, boolean mirror) { 134 if ((degrees != 0 || mirror) && b != null) { 135 Matrix m = new Matrix(); 136 // Mirror first. 137 // horizontal flip + rotation = -rotation + horizontal flip 138 if (mirror) { 139 m.postScale(-1, 1); 140 degrees = (degrees + 360) % 360; 141 if (degrees == 0 || degrees == 180) { 142 m.postTranslate(b.getWidth(), 0); 143 } else if (degrees == 90 || degrees == 270) { 144 m.postTranslate(b.getHeight(), 0); 145 } else { 146 throw new IllegalArgumentException("Invalid degrees=" + degrees); 147 } 148 } 149 if (degrees != 0) { 150 // clockwise 151 m.postRotate(degrees, 152 (float) b.getWidth() / 2, (float) b.getHeight() / 2); 153 } 154 155 try { 156 Bitmap b2 = Bitmap.createBitmap( 157 b, 0, 0, b.getWidth(), b.getHeight(), m, true); 158 if (b != b2) { 159 b.recycle(); 160 b = b2; 161 } 162 } catch (OutOfMemoryError ex) { 163 // We have no memory to rotate. Return the original bitmap. 164 } 165 } 166 return b; 167 } 168 169 /* 170 * Compute the sample size as a function of minSideLength 171 * and maxNumOfPixels. 172 * minSideLength is used to specify that minimal width or height of a 173 * bitmap. 174 * maxNumOfPixels is used to specify the maximal size in pixels that is 175 * tolerable in terms of memory usage. 176 * 177 * The function returns a sample size based on the constraints. 178 * Both size and minSideLength can be passed in as -1 179 * which indicates no care of the corresponding constraint. 180 * The functions prefers returning a sample size that 181 * generates a smaller bitmap, unless minSideLength = -1. 182 * 183 * Also, the function rounds up the sample size to a power of 2 or multiple 184 * of 8 because BitmapFactory only honors sample size this way. 185 * For example, BitmapFactory downsamples an image by 2 even though the 186 * request is 3. So we round up the sample size to avoid OOM. 187 */ 188 public static int computeSampleSize(BitmapFactory.Options options, 189 int minSideLength, int maxNumOfPixels) { 190 int initialSize = computeInitialSampleSize(options, minSideLength, 191 maxNumOfPixels); 192 193 int roundedSize; 194 if (initialSize <= 8) { 195 roundedSize = 1; 196 while (roundedSize < initialSize) { 197 roundedSize <<= 1; 198 } 199 } else { 200 roundedSize = (initialSize + 7) / 8 * 8; 201 } 202 203 return roundedSize; 204 } 205 206 private static int computeInitialSampleSize(BitmapFactory.Options options, 207 int minSideLength, int maxNumOfPixels) { 208 double w = options.outWidth; 209 double h = options.outHeight; 210 211 int lowerBound = (maxNumOfPixels < 0) ? 1 : 212 (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels)); 213 int upperBound = (minSideLength < 0) ? 128 : 214 (int) Math.min(Math.floor(w / minSideLength), 215 Math.floor(h / minSideLength)); 216 217 if (upperBound < lowerBound) { 218 // return the larger one when there is no overlapping zone. 219 return lowerBound; 220 } 221 222 if (maxNumOfPixels < 0 && minSideLength < 0) { 223 return 1; 224 } else if (minSideLength < 0) { 225 return lowerBound; 226 } else { 227 return upperBound; 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, -1, 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 public static void closeSilently(Closeable c) { 256 if (c == null) return; 257 try { 258 c.close(); 259 } catch (Throwable t) { 260 // do nothing 261 } 262 } 263 264 public static void Assert(boolean cond) { 265 if (!cond) { 266 throw new AssertionError(); 267 } 268 } 269 270 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH) 271 private static void throwIfCameraDisabled(Activity activity) throws CameraDisabledException { 272 // Check if device policy has disabled the camera. 273 if (ApiHelper.HAS_GET_CAMERA_DISABLED) { 274 DevicePolicyManager dpm = (DevicePolicyManager) activity.getSystemService( 275 Context.DEVICE_POLICY_SERVICE); 276 if (dpm.getCameraDisabled(null)) { 277 throw new CameraDisabledException(); 278 } 279 } 280 } 281 282 public static CameraManager.CameraProxy openCamera(Activity activity, int cameraId) 283 throws CameraHardwareException, CameraDisabledException { 284 throwIfCameraDisabled(activity); 285 286 try { 287 return CameraHolder.instance().open(cameraId); 288 } catch (CameraHardwareException e) { 289 // In eng build, we throw the exception so that test tool 290 // can detect it and report it 291 if ("eng".equals(Build.TYPE)) { 292 throw new RuntimeException("openCamera failed", e); 293 } else { 294 throw e; 295 } 296 } 297 } 298 299 public static void showErrorAndFinish(final Activity activity, int msgId) { 300 DialogInterface.OnClickListener buttonListener = 301 new DialogInterface.OnClickListener() { 302 @Override 303 public void onClick(DialogInterface dialog, int which) { 304 activity.finish(); 305 } 306 }; 307 new AlertDialog.Builder(activity) 308 .setCancelable(false) 309 .setIconAttribute(android.R.attr.alertDialogIcon) 310 .setTitle(R.string.camera_error_title) 311 .setMessage(msgId) 312 .setNeutralButton(R.string.dialog_ok, buttonListener) 313 .show(); 314 } 315 316 public static <T> T checkNotNull(T object) { 317 if (object == null) throw new NullPointerException(); 318 return object; 319 } 320 321 public static boolean equals(Object a, Object b) { 322 return (a == b) || (a == null ? false : a.equals(b)); 323 } 324 325 public static int nextPowerOf2(int n) { 326 n -= 1; 327 n |= n >>> 16; 328 n |= n >>> 8; 329 n |= n >>> 4; 330 n |= n >>> 2; 331 n |= n >>> 1; 332 return n + 1; 333 } 334 335 public static float distance(float x, float y, float sx, float sy) { 336 float dx = x - sx; 337 float dy = y - sy; 338 return FloatMath.sqrt(dx * dx + dy * dy); 339 } 340 341 public static int clamp(int x, int min, int max) { 342 if (x > max) return max; 343 if (x < min) return min; 344 return x; 345 } 346 347 public static int getDisplayRotation(Activity activity) { 348 int rotation = activity.getWindowManager().getDefaultDisplay() 349 .getRotation(); 350 switch (rotation) { 351 case Surface.ROTATION_0: return 0; 352 case Surface.ROTATION_90: return 90; 353 case Surface.ROTATION_180: return 180; 354 case Surface.ROTATION_270: return 270; 355 } 356 return 0; 357 } 358 359 public static int getDisplayOrientation(int degrees, int cameraId) { 360 // See android.hardware.Camera.setDisplayOrientation for 361 // documentation. 362 Camera.CameraInfo info = new Camera.CameraInfo(); 363 Camera.getCameraInfo(cameraId, info); 364 int result; 365 if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { 366 result = (info.orientation + degrees) % 360; 367 result = (360 - result) % 360; // compensate the mirror 368 } else { // back-facing 369 result = (info.orientation - degrees + 360) % 360; 370 } 371 return result; 372 } 373 374 public static int getCameraOrientation(int cameraId) { 375 Camera.CameraInfo info = new Camera.CameraInfo(); 376 Camera.getCameraInfo(cameraId, info); 377 return info.orientation; 378 } 379 380 public static int roundOrientation(int orientation, int orientationHistory) { 381 boolean changeOrientation = false; 382 if (orientationHistory == OrientationEventListener.ORIENTATION_UNKNOWN) { 383 changeOrientation = true; 384 } else { 385 int dist = Math.abs(orientation - orientationHistory); 386 dist = Math.min( dist, 360 - dist ); 387 changeOrientation = ( dist >= 45 + ORIENTATION_HYSTERESIS ); 388 } 389 if (changeOrientation) { 390 return ((orientation + 45) / 90 * 90) % 360; 391 } 392 return orientationHistory; 393 } 394 395 @SuppressWarnings("deprecation") 396 @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2) 397 private static Point getDefaultDisplaySize(Activity activity, Point size) { 398 Display d = activity.getWindowManager().getDefaultDisplay(); 399 if (Build.VERSION.SDK_INT >= ApiHelper.VERSION_CODES.HONEYCOMB_MR2) { 400 d.getSize(size); 401 } else { 402 size.set(d.getWidth(), d.getHeight()); 403 } 404 return size; 405 } 406 407 public static Size getOptimalPreviewSize(Activity currentActivity, 408 List<Size> sizes, double targetRatio) { 409 // Use a very small tolerance because we want an exact match. 410 final double ASPECT_TOLERANCE = 0.001; 411 if (sizes == null) return null; 412 413 Size optimalSize = null; 414 double minDiff = Double.MAX_VALUE; 415 416 // Because of bugs of overlay and layout, we sometimes will try to 417 // layout the viewfinder in the portrait orientation and thus get the 418 // wrong size of preview surface. When we change the preview size, the 419 // new overlay will be created before the old one closed, which causes 420 // an exception. For now, just get the screen size. 421 Point point = getDefaultDisplaySize(currentActivity, new Point()); 422 int targetHeight = Math.min(point.x, point.y); 423 424 // Try to find an size match aspect ratio and size 425 for (Size size : sizes) { 426 double ratio = (double) size.width / size.height; 427 if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue; 428 if (Math.abs(size.height - targetHeight) < minDiff) { 429 optimalSize = size; 430 minDiff = Math.abs(size.height - targetHeight); 431 } 432 } 433 434 // Cannot find the one match the aspect ratio. This should not happen. 435 // Ignore the requirement. 436 if (optimalSize == null) { 437 Log.w(TAG, "No preview size match the aspect ratio"); 438 minDiff = Double.MAX_VALUE; 439 for (Size size : sizes) { 440 if (Math.abs(size.height - targetHeight) < minDiff) { 441 optimalSize = size; 442 minDiff = Math.abs(size.height - targetHeight); 443 } 444 } 445 } 446 return optimalSize; 447 } 448 449 // Returns the largest picture size which matches the given aspect ratio. 450 public static Size getOptimalVideoSnapshotPictureSize( 451 List<Size> sizes, double targetRatio) { 452 // Use a very small tolerance because we want an exact match. 453 final double ASPECT_TOLERANCE = 0.001; 454 if (sizes == null) return null; 455 456 Size optimalSize = null; 457 458 // Try to find a size matches aspect ratio and has the largest width 459 for (Size size : sizes) { 460 double ratio = (double) size.width / size.height; 461 if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue; 462 if (optimalSize == null || size.width > optimalSize.width) { 463 optimalSize = size; 464 } 465 } 466 467 // Cannot find one that matches the aspect ratio. This should not happen. 468 // Ignore the requirement. 469 if (optimalSize == null) { 470 Log.w(TAG, "No picture size match the aspect ratio"); 471 for (Size size : sizes) { 472 if (optimalSize == null || size.width > optimalSize.width) { 473 optimalSize = size; 474 } 475 } 476 } 477 return optimalSize; 478 } 479 480 public static void dumpParameters(Parameters parameters) { 481 String flattened = parameters.flatten(); 482 StringTokenizer tokenizer = new StringTokenizer(flattened, ";"); 483 Log.d(TAG, "Dump all camera parameters:"); 484 while (tokenizer.hasMoreElements()) { 485 Log.d(TAG, tokenizer.nextToken()); 486 } 487 } 488 489 /** 490 * Returns whether the device is voice-capable (meaning, it can do MMS). 491 */ 492 public static boolean isMmsCapable(Context context) { 493 TelephonyManager telephonyManager = (TelephonyManager) 494 context.getSystemService(Context.TELEPHONY_SERVICE); 495 if (telephonyManager == null) { 496 return false; 497 } 498 499 try { 500 Class<?> partypes[] = new Class[0]; 501 Method sIsVoiceCapable = TelephonyManager.class.getMethod( 502 "isVoiceCapable", partypes); 503 504 Object arglist[] = new Object[0]; 505 Object retobj = sIsVoiceCapable.invoke(telephonyManager, arglist); 506 return (Boolean) retobj; 507 } catch (java.lang.reflect.InvocationTargetException ite) { 508 // Failure, must be another device. 509 // Assume that it is voice capable. 510 } catch (IllegalAccessException iae) { 511 // Failure, must be an other device. 512 // Assume that it is voice capable. 513 } catch (NoSuchMethodException nsme) { 514 } 515 return true; 516 } 517 518 // This is for test only. Allow the camera to launch the specific camera. 519 public static int getCameraFacingIntentExtras(Activity currentActivity) { 520 int cameraId = -1; 521 522 int intentCameraId = 523 currentActivity.getIntent().getIntExtra(Util.EXTRAS_CAMERA_FACING, -1); 524 525 if (isFrontCameraIntent(intentCameraId)) { 526 // Check if the front camera exist 527 int frontCameraId = CameraHolder.instance().getFrontCameraId(); 528 if (frontCameraId != -1) { 529 cameraId = frontCameraId; 530 } 531 } else if (isBackCameraIntent(intentCameraId)) { 532 // Check if the back camera exist 533 int backCameraId = CameraHolder.instance().getBackCameraId(); 534 if (backCameraId != -1) { 535 cameraId = backCameraId; 536 } 537 } 538 return cameraId; 539 } 540 541 private static boolean isFrontCameraIntent(int intentCameraId) { 542 return (intentCameraId == android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT); 543 } 544 545 private static boolean isBackCameraIntent(int intentCameraId) { 546 return (intentCameraId == android.hardware.Camera.CameraInfo.CAMERA_FACING_BACK); 547 } 548 549 private static int sLocation[] = new int[2]; 550 551 // This method is not thread-safe. 552 public static boolean pointInView(float x, float y, View v) { 553 v.getLocationInWindow(sLocation); 554 return x >= sLocation[0] && x < (sLocation[0] + v.getWidth()) 555 && y >= sLocation[1] && y < (sLocation[1] + v.getHeight()); 556 } 557 558 public static int[] getRelativeLocation(View reference, View view) { 559 reference.getLocationInWindow(sLocation); 560 int referenceX = sLocation[0]; 561 int referenceY = sLocation[1]; 562 view.getLocationInWindow(sLocation); 563 sLocation[0] -= referenceX; 564 sLocation[1] -= referenceY; 565 return sLocation; 566 } 567 568 public static boolean isUriValid(Uri uri, ContentResolver resolver) { 569 if (uri == null) return false; 570 571 try { 572 ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "r"); 573 if (pfd == null) { 574 Log.e(TAG, "Fail to open URI. URI=" + uri); 575 return false; 576 } 577 pfd.close(); 578 } catch (IOException ex) { 579 return false; 580 } 581 return true; 582 } 583 584 public static void viewUri(Uri uri, Context context) { 585 if (!isUriValid(uri, context.getContentResolver())) { 586 Log.e(TAG, "Uri invalid. uri=" + uri); 587 return; 588 } 589 590 try { 591 context.startActivity(new Intent(Util.REVIEW_ACTION, uri)); 592 } catch (ActivityNotFoundException ex) { 593 try { 594 context.startActivity(new Intent(Intent.ACTION_VIEW, uri)); 595 } catch (ActivityNotFoundException e) { 596 Log.e(TAG, "review image fail. uri=" + uri, e); 597 } 598 } 599 } 600 601 public static void dumpRect(RectF rect, String msg) { 602 Log.v(TAG, msg + "=(" + rect.left + "," + rect.top 603 + "," + rect.right + "," + rect.bottom + ")"); 604 } 605 606 public static void rectFToRect(RectF rectF, Rect rect) { 607 rect.left = Math.round(rectF.left); 608 rect.top = Math.round(rectF.top); 609 rect.right = Math.round(rectF.right); 610 rect.bottom = Math.round(rectF.bottom); 611 } 612 613 public static void prepareMatrix(Matrix matrix, boolean mirror, int displayOrientation, 614 int viewWidth, int viewHeight) { 615 // Need mirror for front camera. 616 matrix.setScale(mirror ? -1 : 1, 1); 617 // This is the value for android.hardware.Camera.setDisplayOrientation. 618 matrix.postRotate(displayOrientation); 619 // Camera driver coordinates range from (-1000, -1000) to (1000, 1000). 620 // UI coordinates range from (0, 0) to (width, height). 621 matrix.postScale(viewWidth / 2000f, viewHeight / 2000f); 622 matrix.postTranslate(viewWidth / 2f, viewHeight / 2f); 623 } 624 625 public static String createJpegName(long dateTaken) { 626 synchronized (sImageFileNamer) { 627 return sImageFileNamer.generateName(dateTaken); 628 } 629 } 630 631 public static void broadcastNewPicture(Context context, Uri uri) { 632 context.sendBroadcast(new Intent(ACTION_NEW_PICTURE, uri)); 633 // Keep compatibility 634 context.sendBroadcast(new Intent("com.android.camera.NEW_PICTURE", uri)); 635 } 636 637 public static void fadeIn(View view, float startAlpha, float endAlpha, long duration) { 638 if (view.getVisibility() == View.VISIBLE) return; 639 640 view.setVisibility(View.VISIBLE); 641 Animation animation = new AlphaAnimation(startAlpha, endAlpha); 642 animation.setDuration(duration); 643 view.startAnimation(animation); 644 } 645 646 public static void fadeIn(View view) { 647 fadeIn(view, 0F, 1F, 400); 648 649 // We disabled the button in fadeOut(), so enable it here. 650 view.setEnabled(true); 651 } 652 653 public static void fadeOut(View view) { 654 if (view.getVisibility() != View.VISIBLE) return; 655 656 // Since the button is still clickable before fade-out animation 657 // ends, we disable the button first to block click. 658 view.setEnabled(false); 659 Animation animation = new AlphaAnimation(1F, 0F); 660 animation.setDuration(400); 661 view.startAnimation(animation); 662 view.setVisibility(View.GONE); 663 } 664 665 public static int getJpegRotation(int cameraId, int orientation) { 666 // See android.hardware.Camera.Parameters.setRotation for 667 // documentation. 668 int rotation = 0; 669 if (orientation != OrientationEventListener.ORIENTATION_UNKNOWN) { 670 CameraInfo info = CameraHolder.instance().getCameraInfo()[cameraId]; 671 if (info.facing == CameraInfo.CAMERA_FACING_FRONT) { 672 rotation = (info.orientation - orientation + 360) % 360; 673 } else { // back-facing camera 674 rotation = (info.orientation + orientation) % 360; 675 } 676 } 677 return rotation; 678 } 679 680 public static void setGpsParameters(Parameters parameters, Location loc) { 681 // Clear previous GPS location from the parameters. 682 parameters.removeGpsData(); 683 684 // We always encode GpsTimeStamp 685 parameters.setGpsTimestamp(System.currentTimeMillis() / 1000); 686 687 // Set GPS location. 688 if (loc != null) { 689 double lat = loc.getLatitude(); 690 double lon = loc.getLongitude(); 691 boolean hasLatLon = (lat != 0.0d) || (lon != 0.0d); 692 693 if (hasLatLon) { 694 Log.d(TAG, "Set gps location"); 695 parameters.setGpsLatitude(lat); 696 parameters.setGpsLongitude(lon); 697 parameters.setGpsProcessingMethod(loc.getProvider().toUpperCase()); 698 if (loc.hasAltitude()) { 699 parameters.setGpsAltitude(loc.getAltitude()); 700 } else { 701 // for NETWORK_PROVIDER location provider, we may have 702 // no altitude information, but the driver needs it, so 703 // we fake one. 704 parameters.setGpsAltitude(0); 705 } 706 if (loc.getTime() != 0) { 707 // Location.getTime() is UTC in milliseconds. 708 // gps-timestamp is UTC in seconds. 709 long utcTimeSeconds = loc.getTime() / 1000; 710 parameters.setGpsTimestamp(utcTimeSeconds); 711 } 712 } else { 713 loc = null; 714 } 715 } 716 } 717 718 private static class ImageFileNamer { 719 private SimpleDateFormat mFormat; 720 721 // The date (in milliseconds) used to generate the last name. 722 private long mLastDate; 723 724 // Number of names generated for the same second. 725 private int mSameSecondCount; 726 727 public ImageFileNamer(String format) { 728 mFormat = new SimpleDateFormat(format); 729 } 730 731 public String generateName(long dateTaken) { 732 Date date = new Date(dateTaken); 733 String result = mFormat.format(date); 734 735 // If the last name was generated for the same second, 736 // we append _1, _2, etc to the name. 737 if (dateTaken / 1000 == mLastDate / 1000) { 738 mSameSecondCount++; 739 result += "_" + mSameSecondCount; 740 } else { 741 mLastDate = dateTaken; 742 mSameSecondCount = 0; 743 } 744 745 return result; 746 } 747 } 748} 749