Home | History | Annotate | Download | only in camera
      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 
     89     private Util() {
     90     }
     91 
     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 
    104     public static boolean isTabletUI() {
    105         return sIsTabletUI;
    106     }
    107 
    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.
    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.
    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      */
    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 
    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 
    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 
    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 
    251     public static void Assert(boolean cond) {
    252         if (!cond) {
    253             throw new AssertionError();
    254         }
    255     }
    256 
    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 
    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 
    295     public static <T> T checkNotNull(T object) {
    296         if (object == null) throw new NullPointerException();
    297         return object;
    298     }
    299 
    300     public static boolean equals(Object a, Object b) {
    301         return (a == b) || (a == null ? false : a.equals(b));
    302     }
    303 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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.
    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 
    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      */
    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.
    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 
    514     private static boolean isFrontCameraIntent(int intentCameraId) {
    515         return (intentCameraId == android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT);
    516     }
    517 
    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.
    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 
    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 
    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 
    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 
    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 
    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 
    588     public static String createJpegName(long dateTaken) {
    589         synchronized (sImageFileNamer) {
    590             return sImageFileNamer.generateName(dateTaken);
    591         }
    592     }
    593 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    697         public ImageFileNamer(String format) {
    698             mFormat = new SimpleDateFormat(format);
    699         }
    700 
    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