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