Home | History | Annotate | Download | only in util
      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.util;
     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.ComponentName;
     24 import android.content.ContentResolver;
     25 import android.content.Context;
     26 import android.content.DialogInterface;
     27 import android.content.Intent;
     28 import android.content.res.TypedArray;
     29 import android.graphics.Bitmap;
     30 import android.graphics.BitmapFactory;
     31 import android.graphics.Matrix;
     32 import android.graphics.Point;
     33 import android.graphics.PointF;
     34 import android.graphics.Rect;
     35 import android.graphics.RectF;
     36 import android.hardware.camera2.CameraCharacteristics;
     37 import android.hardware.camera2.CameraMetadata;
     38 import android.location.Location;
     39 import android.net.Uri;
     40 import android.os.ParcelFileDescriptor;
     41 import android.telephony.TelephonyManager;
     42 import android.util.DisplayMetrics;
     43 import android.util.FloatMath;
     44 import android.util.TypedValue;
     45 import android.view.Display;
     46 import android.view.OrientationEventListener;
     47 import android.view.Surface;
     48 import android.view.View;
     49 import android.view.WindowManager;
     50 import android.view.animation.AlphaAnimation;
     51 import android.view.animation.Animation;
     52 import android.widget.Toast;
     53 
     54 import com.android.camera.CameraActivity;
     55 import com.android.camera.CameraDisabledException;
     56 import com.android.camera.debug.Log;
     57 import com.android.camera.filmstrip.ImageData;
     58 import com.android.camera2.R;
     59 import com.android.ex.camera2.portability.CameraCapabilities;
     60 import com.android.ex.camera2.portability.CameraSettings;
     61 
     62 import java.io.Closeable;
     63 import java.io.IOException;
     64 import java.lang.reflect.Method;
     65 import java.text.SimpleDateFormat;
     66 import java.util.Date;
     67 import java.util.List;
     68 import java.util.Locale;
     69 
     70 /**
     71  * Collection of utility functions used in this package.
     72  */
     73 public class CameraUtil {
     74     private static final Log.Tag TAG = new Log.Tag("Util");
     75 
     76     // For calculate the best fps range for still image capture.
     77     private final static int MAX_PREVIEW_FPS_TIMES_1000 = 400000;
     78     private final static int PREFERRED_PREVIEW_FPS_TIMES_1000 = 30000;
     79 
     80     // For creating crop intents.
     81     public static final String KEY_RETURN_DATA = "return-data";
     82     public static final String KEY_SHOW_WHEN_LOCKED = "showWhenLocked";
     83 
     84     /** Orientation hysteresis amount used in rounding, in degrees. */
     85     public static final int ORIENTATION_HYSTERESIS = 5;
     86 
     87     public static final String REVIEW_ACTION = "com.android.camera.action.REVIEW";
     88     /** See android.hardware.Camera.ACTION_NEW_PICTURE. */
     89     public static final String ACTION_NEW_PICTURE = "android.hardware.action.NEW_PICTURE";
     90     /** See android.hardware.Camera.ACTION_NEW_VIDEO. */
     91     public static final String ACTION_NEW_VIDEO = "android.hardware.action.NEW_VIDEO";
     92 
     93     /**
     94      * Broadcast Action: The camera application has become active in
     95      * picture-taking mode.
     96      */
     97     public static final String ACTION_CAMERA_STARTED = "com.android.camera.action.CAMERA_STARTED";
     98     /**
     99      * Broadcast Action: The camera application is no longer in active
    100      * picture-taking mode.
    101      */
    102     public static final String ACTION_CAMERA_STOPPED = "com.android.camera.action.CAMERA_STOPPED";
    103     /**
    104      * When the camera application is active in picture-taking mode, it listens
    105      * for this intent, which upon receipt will trigger the shutter to capture a
    106      * new picture, as if the user had pressed the shutter button.
    107      */
    108     public static final String ACTION_CAMERA_SHUTTER_CLICK =
    109             "com.android.camera.action.SHUTTER_CLICK";
    110 
    111     // Fields for the show-on-maps-functionality
    112     private static final String MAPS_PACKAGE_NAME = "com.google.android.apps.maps";
    113     private static final String MAPS_CLASS_NAME = "com.google.android.maps.MapsActivity";
    114 
    115     /** Has to be in sync with the receiving MovieActivity. */
    116     public static final String KEY_TREAT_UP_AS_BACK = "treat-up-as-back";
    117 
    118     /** Private intent extras. Test only. */
    119     private static final String EXTRAS_CAMERA_FACING =
    120             "android.intent.extras.CAMERA_FACING";
    121 
    122     private static float sPixelDensity = 1;
    123     private static ImageFileNamer sImageFileNamer;
    124 
    125     private CameraUtil() {
    126     }
    127 
    128     public static void initialize(Context context) {
    129         DisplayMetrics metrics = new DisplayMetrics();
    130         WindowManager wm = (WindowManager)
    131                 context.getSystemService(Context.WINDOW_SERVICE);
    132         wm.getDefaultDisplay().getMetrics(metrics);
    133         sPixelDensity = metrics.density;
    134         sImageFileNamer = new ImageFileNamer(
    135                 context.getString(R.string.image_file_name_format));
    136     }
    137 
    138     public static int dpToPixel(int dp) {
    139         return Math.round(sPixelDensity * dp);
    140     }
    141 
    142     /**
    143      * Rotates the bitmap by the specified degree. If a new bitmap is created,
    144      * the original bitmap is recycled.
    145      */
    146     public static Bitmap rotate(Bitmap b, int degrees) {
    147         return rotateAndMirror(b, degrees, false);
    148     }
    149 
    150     /**
    151      * Rotates and/or mirrors the bitmap. If a new bitmap is created, the
    152      * original bitmap is recycled.
    153      */
    154     public static Bitmap rotateAndMirror(Bitmap b, int degrees, boolean mirror) {
    155         if ((degrees != 0 || mirror) && b != null) {
    156             Matrix m = new Matrix();
    157             // Mirror first.
    158             // horizontal flip + rotation = -rotation + horizontal flip
    159             if (mirror) {
    160                 m.postScale(-1, 1);
    161                 degrees = (degrees + 360) % 360;
    162                 if (degrees == 0 || degrees == 180) {
    163                     m.postTranslate(b.getWidth(), 0);
    164                 } else if (degrees == 90 || degrees == 270) {
    165                     m.postTranslate(b.getHeight(), 0);
    166                 } else {
    167                     throw new IllegalArgumentException("Invalid degrees=" + degrees);
    168                 }
    169             }
    170             if (degrees != 0) {
    171                 // clockwise
    172                 m.postRotate(degrees,
    173                         (float) b.getWidth() / 2, (float) b.getHeight() / 2);
    174             }
    175 
    176             try {
    177                 Bitmap b2 = Bitmap.createBitmap(
    178                         b, 0, 0, b.getWidth(), b.getHeight(), m, true);
    179                 if (b != b2) {
    180                     b.recycle();
    181                     b = b2;
    182                 }
    183             } catch (OutOfMemoryError ex) {
    184                 // We have no memory to rotate. Return the original bitmap.
    185             }
    186         }
    187         return b;
    188     }
    189 
    190     /**
    191      * Compute the sample size as a function of minSideLength and
    192      * maxNumOfPixels. minSideLength is used to specify that minimal width or
    193      * height of a bitmap. maxNumOfPixels is used to specify the maximal size in
    194      * pixels that is tolerable in terms of memory usage. The function returns a
    195      * sample size based on the constraints.
    196      * <p>
    197      * Both size and minSideLength can be passed in as -1 which indicates no
    198      * care of the corresponding constraint. The functions prefers returning a
    199      * sample size that generates a smaller bitmap, unless minSideLength = -1.
    200      * <p>
    201      * Also, the function rounds up the sample size to a power of 2 or multiple
    202      * of 8 because BitmapFactory only honors sample size this way. For example,
    203      * BitmapFactory downsamples an image by 2 even though the request is 3. So
    204      * we round up the sample size to avoid OOM.
    205      */
    206     public static int computeSampleSize(BitmapFactory.Options options,
    207     int minSideLength, int maxNumOfPixels) {
    208         int initialSize = computeInitialSampleSize(options, minSideLength,
    209       maxNumOfPixels);
    210 
    211         int roundedSize;
    212         if (initialSize <= 8) {
    213             roundedSize = 1;
    214             while (roundedSize < initialSize) {
    215                 roundedSize <<= 1;
    216             }
    217         } else {
    218             roundedSize = (initialSize + 7) / 8 * 8;
    219         }
    220 
    221         return roundedSize;
    222     }
    223 
    224     private static int computeInitialSampleSize(BitmapFactory.Options options,
    225             int minSideLength, int maxNumOfPixels) {
    226         double w = options.outWidth;
    227         double h = options.outHeight;
    228 
    229         int lowerBound = (maxNumOfPixels < 0) ? 1 :
    230                 (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
    231         int upperBound = (minSideLength < 0) ? 128 :
    232                 (int) Math.min(Math.floor(w / minSideLength),
    233                         Math.floor(h / minSideLength));
    234 
    235         if (upperBound < lowerBound) {
    236             // return the larger one when there is no overlapping zone.
    237             return lowerBound;
    238         }
    239 
    240         if (maxNumOfPixels < 0 && minSideLength < 0) {
    241             return 1;
    242         } else if (minSideLength < 0) {
    243             return lowerBound;
    244         } else {
    245             return upperBound;
    246         }
    247     }
    248 
    249     public static Bitmap makeBitmap(byte[] jpegData, int maxNumOfPixels) {
    250         try {
    251             BitmapFactory.Options options = new BitmapFactory.Options();
    252             options.inJustDecodeBounds = true;
    253             BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length,
    254                     options);
    255             if (options.mCancel || options.outWidth == -1
    256                     || options.outHeight == -1) {
    257                 return null;
    258             }
    259             options.inSampleSize = computeSampleSize(
    260                     options, -1, maxNumOfPixels);
    261             options.inJustDecodeBounds = false;
    262 
    263             options.inDither = false;
    264             options.inPreferredConfig = Bitmap.Config.ARGB_8888;
    265             return BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length,
    266                     options);
    267         } catch (OutOfMemoryError ex) {
    268             Log.e(TAG, "Got oom exception ", ex);
    269             return null;
    270         }
    271     }
    272 
    273     public static void closeSilently(Closeable c) {
    274         if (c == null) {
    275             return;
    276         }
    277         try {
    278             c.close();
    279         } catch (Throwable t) {
    280             // do nothing
    281         }
    282     }
    283 
    284     public static void Assert(boolean cond) {
    285         if (!cond) {
    286             throw new AssertionError();
    287         }
    288     }
    289 
    290     public static void showErrorAndFinish(final Activity activity, int msgId) {
    291         DialogInterface.OnClickListener buttonListener =
    292                 new DialogInterface.OnClickListener() {
    293                     @Override
    294                     public void onClick(DialogInterface dialog, int which) {
    295                         activity.finish();
    296                     }
    297                 };
    298         TypedValue out = new TypedValue();
    299         activity.getTheme().resolveAttribute(android.R.attr.alertDialogIcon, out, true);
    300         // Some crash reports indicate users leave app prior to this dialog
    301         // appearing, so check to ensure that the activity is not shutting down
    302         // before attempting to attach a dialog to the window manager.
    303         if (!activity.isFinishing()) {
    304             new AlertDialog.Builder(activity)
    305                     .setCancelable(false)
    306                     .setTitle(R.string.camera_error_title)
    307                     .setMessage(msgId)
    308                     .setNeutralButton(R.string.dialog_ok, buttonListener)
    309                     .setIcon(out.resourceId)
    310                     .show();
    311         }
    312     }
    313 
    314     public static <T> T checkNotNull(T object) {
    315         if (object == null) {
    316             throw new NullPointerException();
    317         }
    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         // TODO: what happens if n is negative or already a power of 2?
    327         n -= 1;
    328         n |= n >>> 16;
    329         n |= n >>> 8;
    330         n |= n >>> 4;
    331         n |= n >>> 2;
    332         n |= n >>> 1;
    333         return n + 1;
    334     }
    335 
    336     public static float distance(float x, float y, float sx, float sy) {
    337         float dx = x - sx;
    338         float dy = y - sy;
    339         return (float) Math.sqrt(dx * dx + dy * dy);
    340     }
    341 
    342     /**
    343      * Clamps x to between min and max (inclusive on both ends, x = min --> min,
    344      * x = max --> max).
    345      */
    346     public static int clamp(int x, int min, int max) {
    347         if (x > max) {
    348             return max;
    349         }
    350         if (x < min) {
    351             return min;
    352         }
    353         return x;
    354     }
    355 
    356     /**
    357      * Clamps x to between min and max (inclusive on both ends, x = min --> min,
    358      * x = max --> max).
    359      */
    360     public static float clamp(float x, float min, float max) {
    361         if (x > max) {
    362             return max;
    363         }
    364         if (x < min) {
    365             return min;
    366         }
    367         return x;
    368     }
    369 
    370     /**
    371      * Linear interpolation between a and b by the fraction t. t = 0 --> a, t =
    372      * 1 --> b.
    373      */
    374     public static float lerp(float a, float b, float t) {
    375         return a + t * (b - a);
    376     }
    377 
    378     /**
    379      * Given (nx, ny) \in [0, 1]^2, in the display's portrait coordinate system,
    380      * returns normalized sensor coordinates \in [0, 1]^2 depending on how
    381      * the sensor's orientation \in {0, 90, 180, 270}.
    382      *
    383      * <p>
    384      * Returns null if sensorOrientation is not one of the above.
    385      * </p>
    386      */
    387     public static PointF normalizedSensorCoordsForNormalizedDisplayCoords(
    388         float nx, float ny, int sensorOrientation) {
    389         switch (sensorOrientation) {
    390         case 0:
    391             return new PointF(nx, ny);
    392         case 90:
    393             return new PointF(ny, 1.0f - nx);
    394         case 180:
    395             return new PointF(1.0f - nx, 1.0f - ny);
    396         case 270:
    397             return new PointF(1.0f - ny, nx);
    398         default:
    399             return null;
    400         }
    401     }
    402 
    403     /**
    404      * Given a size, return the largest size with the given aspectRatio that
    405      * maximally fits into the bounding rectangle of the original Size.
    406      *
    407      * @param size the original Size to crop
    408      * @param aspectRatio the target aspect ratio
    409      * @return the largest Size with the given aspect ratio that is smaller than
    410      *         or equal to the original Size.
    411      */
    412     public static Size constrainToAspectRatio(Size size, float aspectRatio) {
    413         float width = size.getWidth();
    414         float height = size.getHeight();
    415 
    416         float currentAspectRatio = width * 1.0f / height;
    417 
    418         if (currentAspectRatio > aspectRatio) {
    419             // chop longer side
    420             if (width > height) {
    421                 width = height * aspectRatio;
    422             } else {
    423                 height = width / aspectRatio;
    424             }
    425         } else if (currentAspectRatio < aspectRatio) {
    426             // chop shorter side
    427             if (width < height) {
    428                 width = height * aspectRatio;
    429             } else {
    430                 height = width / aspectRatio;
    431             }
    432         }
    433 
    434         return new Size((int) width, (int) height);
    435     }
    436 
    437     public static int getDisplayRotation(Context context) {
    438         WindowManager windowManager = (WindowManager) context
    439                 .getSystemService(Context.WINDOW_SERVICE);
    440         int rotation = windowManager.getDefaultDisplay()
    441                 .getRotation();
    442         switch (rotation) {
    443             case Surface.ROTATION_0:
    444                 return 0;
    445             case Surface.ROTATION_90:
    446                 return 90;
    447             case Surface.ROTATION_180:
    448                 return 180;
    449             case Surface.ROTATION_270:
    450                 return 270;
    451         }
    452         return 0;
    453     }
    454 
    455     /**
    456      * Calculate the default orientation of the device based on the width and
    457      * height of the display when rotation = 0 (i.e. natural width and height)
    458      *
    459      * @param context current context
    460      * @return whether the default orientation of the device is portrait
    461      */
    462     public static boolean isDefaultToPortrait(Context context) {
    463         Display currentDisplay = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE))
    464                 .getDefaultDisplay();
    465         Point displaySize = new Point();
    466         currentDisplay.getSize(displaySize);
    467         int orientation = currentDisplay.getRotation();
    468         int naturalWidth, naturalHeight;
    469         if (orientation == Surface.ROTATION_0 || orientation == Surface.ROTATION_180) {
    470             naturalWidth = displaySize.x;
    471             naturalHeight = displaySize.y;
    472         } else {
    473             naturalWidth = displaySize.y;
    474             naturalHeight = displaySize.x;
    475         }
    476         return naturalWidth < naturalHeight;
    477     }
    478 
    479     public static int roundOrientation(int orientation, int orientationHistory) {
    480         boolean changeOrientation = false;
    481         if (orientationHistory == OrientationEventListener.ORIENTATION_UNKNOWN) {
    482             changeOrientation = true;
    483         } else {
    484             int dist = Math.abs(orientation - orientationHistory);
    485             dist = Math.min(dist, 360 - dist);
    486             changeOrientation = (dist >= 45 + ORIENTATION_HYSTERESIS);
    487         }
    488         if (changeOrientation) {
    489             return ((orientation + 45) / 90 * 90) % 360;
    490         }
    491         return orientationHistory;
    492     }
    493 
    494     private static Size getDefaultDisplaySize(Context context) {
    495         WindowManager windowManager = (WindowManager) context
    496                 .getSystemService(Context.WINDOW_SERVICE);
    497         Point res = new Point();
    498         windowManager.getDefaultDisplay().getSize(res);
    499         return new Size(res);
    500     }
    501 
    502     public static com.android.ex.camera2.portability.Size getOptimalPreviewSize(Context context,
    503             List<com.android.ex.camera2.portability.Size> sizes, double targetRatio) {
    504         int optimalPickIndex = getOptimalPreviewSizeIndex(context, Size.convert(sizes),
    505                 targetRatio);
    506         if (optimalPickIndex == -1) {
    507             return null;
    508         } else {
    509             return sizes.get(optimalPickIndex);
    510         }
    511     }
    512 
    513     public static int getOptimalPreviewSizeIndex(Context context,
    514             List<Size> sizes, double targetRatio) {
    515         // Use a very small tolerance because we want an exact match.
    516         final double ASPECT_TOLERANCE = 0.01;
    517         if (sizes == null) {
    518             return -1;
    519         }
    520 
    521         int optimalSizeIndex = -1;
    522         double minDiff = Double.MAX_VALUE;
    523 
    524         // Because of bugs of overlay and layout, we sometimes will try to
    525         // layout the viewfinder in the portrait orientation and thus get the
    526         // wrong size of preview surface. When we change the preview size, the
    527         // new overlay will be created before the old one closed, which causes
    528         // an exception. For now, just get the screen size.
    529         Size defaultDisplaySize = getDefaultDisplaySize(context);
    530         int targetHeight = Math.min(defaultDisplaySize.getWidth(), defaultDisplaySize.getHeight());
    531         // Try to find an size match aspect ratio and size
    532         for (int i = 0; i < sizes.size(); i++) {
    533             Size size = sizes.get(i);
    534             double ratio = (double) size.getWidth() / size.getHeight();
    535             if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) {
    536                 continue;
    537             }
    538 
    539             double heightDiff = Math.abs(size.getHeight() - targetHeight);
    540             if (heightDiff < minDiff) {
    541                 optimalSizeIndex = i;
    542                 minDiff = heightDiff;
    543             } else if (heightDiff == minDiff) {
    544                 // Prefer resolutions smaller-than-display when an equally close
    545                 // larger-than-display resolution is available
    546                 if (size.getHeight() < targetHeight) {
    547                     optimalSizeIndex = i;
    548                     minDiff = heightDiff;
    549                 }
    550             }
    551         }
    552         // Cannot find the one match the aspect ratio. This should not happen.
    553         // Ignore the requirement.
    554         if (optimalSizeIndex == -1) {
    555             Log.w(TAG, "No preview size match the aspect ratio");
    556             minDiff = Double.MAX_VALUE;
    557             for (int i = 0; i < sizes.size(); i++) {
    558                 Size size = sizes.get(i);
    559                 if (Math.abs(size.getHeight() - targetHeight) < minDiff) {
    560                     optimalSizeIndex = i;
    561                     minDiff = Math.abs(size.getHeight() - targetHeight);
    562                 }
    563             }
    564         }
    565         return optimalSizeIndex;
    566     }
    567 
    568     /** Returns the largest picture size which matches the given aspect ratio. */
    569     public static com.android.ex.camera2.portability.Size getOptimalVideoSnapshotPictureSize(
    570             List<com.android.ex.camera2.portability.Size> sizes, double targetRatio) {
    571         // Use a very small tolerance because we want an exact match.
    572         final double ASPECT_TOLERANCE = 0.001;
    573         if (sizes == null) {
    574             return null;
    575         }
    576 
    577         com.android.ex.camera2.portability.Size optimalSize = null;
    578 
    579         // Try to find a size matches aspect ratio and has the largest width
    580         for (com.android.ex.camera2.portability.Size size : sizes) {
    581             double ratio = (double) size.width() / size.height();
    582             if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) {
    583                 continue;
    584             }
    585             if (optimalSize == null || size.width() > optimalSize.width()) {
    586                 optimalSize = size;
    587             }
    588         }
    589 
    590         // Cannot find one that matches the aspect ratio. This should not
    591         // happen. Ignore the requirement.
    592         if (optimalSize == null) {
    593             Log.w(TAG, "No picture size match the aspect ratio");
    594             for (com.android.ex.camera2.portability.Size size : sizes) {
    595                 if (optimalSize == null || size.width() > optimalSize.width()) {
    596                     optimalSize = size;
    597                 }
    598             }
    599         }
    600         return optimalSize;
    601     }
    602 
    603     /**
    604      * Returns whether the device is voice-capable (meaning, it can do MMS).
    605      */
    606     public static boolean isMmsCapable(Context context) {
    607         TelephonyManager telephonyManager = (TelephonyManager)
    608                 context.getSystemService(Context.TELEPHONY_SERVICE);
    609         if (telephonyManager == null) {
    610             return false;
    611         }
    612 
    613         try {
    614             Class<?> partypes[] = new Class[0];
    615             Method sIsVoiceCapable = TelephonyManager.class.getMethod(
    616                     "isVoiceCapable", partypes);
    617 
    618             Object arglist[] = new Object[0];
    619             Object retobj = sIsVoiceCapable.invoke(telephonyManager, arglist);
    620             return (Boolean) retobj;
    621         } catch (java.lang.reflect.InvocationTargetException ite) {
    622             // Failure, must be another device.
    623             // Assume that it is voice capable.
    624         } catch (IllegalAccessException iae) {
    625             // Failure, must be an other device.
    626             // Assume that it is voice capable.
    627         } catch (NoSuchMethodException nsme) {
    628         }
    629         return true;
    630     }
    631 
    632     // This is for test only. Allow the camera to launch the specific camera.
    633     public static int getCameraFacingIntentExtras(Activity currentActivity) {
    634         int cameraId = -1;
    635 
    636         int intentCameraId =
    637                 currentActivity.getIntent().getIntExtra(CameraUtil.EXTRAS_CAMERA_FACING, -1);
    638 
    639         if (isFrontCameraIntent(intentCameraId)) {
    640             // Check if the front camera exist
    641             int frontCameraId = ((CameraActivity) currentActivity).getCameraProvider()
    642                     .getFirstFrontCameraId();
    643             if (frontCameraId != -1) {
    644                 cameraId = frontCameraId;
    645             }
    646         } else if (isBackCameraIntent(intentCameraId)) {
    647             // Check if the back camera exist
    648             int backCameraId = ((CameraActivity) currentActivity).getCameraProvider()
    649                     .getFirstBackCameraId();
    650             if (backCameraId != -1) {
    651                 cameraId = backCameraId;
    652             }
    653         }
    654         return cameraId;
    655     }
    656 
    657     private static boolean isFrontCameraIntent(int intentCameraId) {
    658         return (intentCameraId == android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT);
    659     }
    660 
    661     private static boolean isBackCameraIntent(int intentCameraId) {
    662         return (intentCameraId == android.hardware.Camera.CameraInfo.CAMERA_FACING_BACK);
    663     }
    664 
    665     private static int sLocation[] = new int[2];
    666 
    667     // This method is not thread-safe.
    668     public static boolean pointInView(float x, float y, View v) {
    669         v.getLocationInWindow(sLocation);
    670         return x >= sLocation[0] && x < (sLocation[0] + v.getWidth())
    671                 && y >= sLocation[1] && y < (sLocation[1] + v.getHeight());
    672     }
    673 
    674     public static int[] getRelativeLocation(View reference, View view) {
    675         reference.getLocationInWindow(sLocation);
    676         int referenceX = sLocation[0];
    677         int referenceY = sLocation[1];
    678         view.getLocationInWindow(sLocation);
    679         sLocation[0] -= referenceX;
    680         sLocation[1] -= referenceY;
    681         return sLocation;
    682     }
    683 
    684     public static boolean isUriValid(Uri uri, ContentResolver resolver) {
    685         if (uri == null) {
    686             return false;
    687         }
    688 
    689         try {
    690             ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "r");
    691             if (pfd == null) {
    692                 Log.e(TAG, "Fail to open URI. URI=" + uri);
    693                 return false;
    694             }
    695             pfd.close();
    696         } catch (IOException ex) {
    697             return false;
    698         }
    699         return true;
    700     }
    701 
    702     public static void dumpRect(RectF rect, String msg) {
    703         Log.v(TAG, msg + "=(" + rect.left + "," + rect.top
    704                 + "," + rect.right + "," + rect.bottom + ")");
    705     }
    706 
    707     public static void rectFToRect(RectF rectF, Rect rect) {
    708         rect.left = Math.round(rectF.left);
    709         rect.top = Math.round(rectF.top);
    710         rect.right = Math.round(rectF.right);
    711         rect.bottom = Math.round(rectF.bottom);
    712     }
    713 
    714     public static Rect rectFToRect(RectF rectF) {
    715         Rect rect = new Rect();
    716         rectFToRect(rectF, rect);
    717         return rect;
    718     }
    719 
    720     public static RectF rectToRectF(Rect r) {
    721         return new RectF(r.left, r.top, r.right, r.bottom);
    722     }
    723 
    724     public static void prepareMatrix(Matrix matrix, boolean mirror, int displayOrientation,
    725             int viewWidth, int viewHeight) {
    726         // Need mirror for front camera.
    727         matrix.setScale(mirror ? -1 : 1, 1);
    728         // This is the value for android.hardware.Camera.setDisplayOrientation.
    729         matrix.postRotate(displayOrientation);
    730         // Camera driver coordinates range from (-1000, -1000) to (1000, 1000).
    731         // UI coordinates range from (0, 0) to (width, height).
    732         matrix.postScale(viewWidth / 2000f, viewHeight / 2000f);
    733         matrix.postTranslate(viewWidth / 2f, viewHeight / 2f);
    734     }
    735 
    736     public static void prepareMatrix(Matrix matrix, boolean mirror, int displayOrientation,
    737             Rect previewRect) {
    738         // Need mirror for front camera.
    739         matrix.setScale(mirror ? -1 : 1, 1);
    740         // This is the value for android.hardware.Camera.setDisplayOrientation.
    741         matrix.postRotate(displayOrientation);
    742 
    743         // Camera driver coordinates range from (-1000, -1000) to (1000, 1000).
    744         // We need to map camera driver coordinates to preview rect coordinates
    745         Matrix mapping = new Matrix();
    746         mapping.setRectToRect(new RectF(-1000, -1000, 1000, 1000), rectToRectF(previewRect),
    747                 Matrix.ScaleToFit.FILL);
    748         matrix.setConcat(mapping, matrix);
    749     }
    750 
    751     public static String createJpegName(long dateTaken) {
    752         synchronized (sImageFileNamer) {
    753             return sImageFileNamer.generateName(dateTaken);
    754         }
    755     }
    756 
    757     public static void broadcastNewPicture(Context context, Uri uri) {
    758         context.sendBroadcast(new Intent(ACTION_NEW_PICTURE, uri));
    759         // Keep compatibility
    760         context.sendBroadcast(new Intent("com.android.camera.NEW_PICTURE", uri));
    761     }
    762 
    763     public static void fadeIn(View view, float startAlpha, float endAlpha, long duration) {
    764         if (view.getVisibility() == View.VISIBLE) {
    765             return;
    766         }
    767 
    768         view.setVisibility(View.VISIBLE);
    769         Animation animation = new AlphaAnimation(startAlpha, endAlpha);
    770         animation.setDuration(duration);
    771         view.startAnimation(animation);
    772     }
    773 
    774     /**
    775      * Down-samples a jpeg byte array.
    776      *
    777      * @param data a byte array of jpeg data
    778      * @param downSampleFactor down-sample factor
    779      * @return decoded and down-sampled bitmap
    780      */
    781     public static Bitmap downSample(final byte[] data, int downSampleFactor) {
    782         final BitmapFactory.Options opts = new BitmapFactory.Options();
    783         // Downsample the image
    784         opts.inSampleSize = downSampleFactor;
    785         return BitmapFactory.decodeByteArray(data, 0, data.length, opts);
    786     }
    787 
    788     public static void setGpsParameters(CameraSettings settings, Location loc) {
    789         // Clear previous GPS location from the parameters.
    790         settings.clearGpsData();
    791 
    792         boolean hasLatLon = false;
    793         double lat;
    794         double lon;
    795         // Set GPS location.
    796         if (loc != null) {
    797             lat = loc.getLatitude();
    798             lon = loc.getLongitude();
    799             hasLatLon = (lat != 0.0d) || (lon != 0.0d);
    800         }
    801 
    802         if (!hasLatLon) {
    803             // We always encode GpsTimeStamp even if the GPS location is not
    804             // available.
    805             settings.setGpsData(
    806                     new CameraSettings.GpsData(0f, 0f, 0f, System.currentTimeMillis() / 1000, null)
    807                     );
    808         } else {
    809             Log.d(TAG, "Set gps location");
    810             // for NETWORK_PROVIDER location provider, we may have
    811             // no altitude information, but the driver needs it, so
    812             // we fake one.
    813             // Location.getTime() is UTC in milliseconds.
    814             // gps-timestamp is UTC in seconds.
    815             long utcTimeSeconds = loc.getTime() / 1000;
    816             settings.setGpsData(new CameraSettings.GpsData(loc.getLatitude(), loc.getLongitude(),
    817                     (loc.hasAltitude() ? loc.getAltitude() : 0),
    818                     (utcTimeSeconds != 0 ? utcTimeSeconds : System.currentTimeMillis()),
    819                     loc.getProvider().toUpperCase()));
    820         }
    821     }
    822 
    823     /**
    824      * For still image capture, we need to get the right fps range such that the
    825      * camera can slow down the framerate to allow for less-noisy/dark
    826      * viewfinder output in dark conditions.
    827      *
    828      * @param capabilities Camera's capabilities.
    829      * @return null if no appropiate fps range can't be found. Otherwise, return
    830      *         the right range.
    831      */
    832     public static int[] getPhotoPreviewFpsRange(CameraCapabilities capabilities) {
    833         return getPhotoPreviewFpsRange(capabilities.getSupportedPreviewFpsRange());
    834     }
    835 
    836     public static int[] getPhotoPreviewFpsRange(List<int[]> frameRates) {
    837         if (frameRates.size() == 0) {
    838             Log.e(TAG, "No suppoted frame rates returned!");
    839             return null;
    840         }
    841 
    842         // Find the lowest min rate in supported ranges who can cover 30fps.
    843         int lowestMinRate = MAX_PREVIEW_FPS_TIMES_1000;
    844         for (int[] rate : frameRates) {
    845             int minFps = rate[0];
    846             int maxFps = rate[1];
    847             if (maxFps >= PREFERRED_PREVIEW_FPS_TIMES_1000 &&
    848                     minFps <= PREFERRED_PREVIEW_FPS_TIMES_1000 &&
    849                     minFps < lowestMinRate) {
    850                 lowestMinRate = minFps;
    851             }
    852         }
    853 
    854         // Find all the modes with the lowest min rate found above, the pick the
    855         // one with highest max rate.
    856         int resultIndex = -1;
    857         int highestMaxRate = 0;
    858         for (int i = 0; i < frameRates.size(); i++) {
    859             int[] rate = frameRates.get(i);
    860             int minFps = rate[0];
    861             int maxFps = rate[1];
    862             if (minFps == lowestMinRate && highestMaxRate < maxFps) {
    863                 highestMaxRate = maxFps;
    864                 resultIndex = i;
    865             }
    866         }
    867 
    868         if (resultIndex >= 0) {
    869             return frameRates.get(resultIndex);
    870         }
    871         Log.e(TAG, "Can't find an appropiate frame rate range!");
    872         return null;
    873     }
    874 
    875     public static int[] getMaxPreviewFpsRange(List<int[]> frameRates) {
    876         if (frameRates != null && frameRates.size() > 0) {
    877             // The list is sorted. Return the last element.
    878             return frameRates.get(frameRates.size() - 1);
    879         }
    880         return new int[0];
    881     }
    882 
    883     public static void throwIfCameraDisabled(Context context) throws CameraDisabledException {
    884         // Check if device policy has disabled the camera.
    885         DevicePolicyManager dpm =
    886                 (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
    887         if (dpm.getCameraDisabled(null)) {
    888             throw new CameraDisabledException();
    889         }
    890     }
    891 
    892     /**
    893      * Generates a 1d Gaussian mask of the input array size, and store the mask
    894      * in the input array.
    895      *
    896      * @param mask empty array of size n, where n will be used as the size of
    897      *            the Gaussian mask, and the array will be populated with the
    898      *            values of the mask.
    899      */
    900     private static void getGaussianMask(float[] mask) {
    901         int len = mask.length;
    902         int mid = len / 2;
    903         float sigma = len;
    904         float sum = 0;
    905         for (int i = 0; i <= mid; i++) {
    906             float ex = FloatMath.exp(-(i - mid) * (i - mid) / (mid * mid))
    907                     / (2 * sigma * sigma);
    908             int symmetricIndex = len - 1 - i;
    909             mask[i] = ex;
    910             mask[symmetricIndex] = ex;
    911             sum += mask[i];
    912             if (i != symmetricIndex) {
    913                 sum += mask[symmetricIndex];
    914             }
    915         }
    916 
    917         for (int i = 0; i < mask.length; i++) {
    918             mask[i] /= sum;
    919         }
    920 
    921     }
    922 
    923     /**
    924      * Add two pixels together where the second pixel will be applied with a
    925      * weight.
    926      *
    927      * @param pixel pixel color value of weight 1
    928      * @param newPixel second pixel color value where the weight will be applied
    929      * @param weight a float weight that will be applied to the second pixel
    930      *            color
    931      * @return the weighted addition of the two pixels
    932      */
    933     public static int addPixel(int pixel, int newPixel, float weight) {
    934         // TODO: scale weight to [0, 1024] to avoid casting to float and back to
    935         // int.
    936         int r = ((pixel & 0x00ff0000) + (int) ((newPixel & 0x00ff0000) * weight)) & 0x00ff0000;
    937         int g = ((pixel & 0x0000ff00) + (int) ((newPixel & 0x0000ff00) * weight)) & 0x0000ff00;
    938         int b = ((pixel & 0x000000ff) + (int) ((newPixel & 0x000000ff) * weight)) & 0x000000ff;
    939         return 0xff000000 | r | g | b;
    940     }
    941 
    942     /**
    943      * Apply blur to the input image represented in an array of colors and put
    944      * the output image, in the form of an array of colors, into the output
    945      * array.
    946      *
    947      * @param src source array of colors
    948      * @param out output array of colors after the blur
    949      * @param w width of the image
    950      * @param h height of the image
    951      * @param size size of the Gaussian blur mask
    952      */
    953     public static void blur(int[] src, int[] out, int w, int h, int size) {
    954         float[] k = new float[size];
    955         int off = size / 2;
    956 
    957         getGaussianMask(k);
    958 
    959         int[] tmp = new int[src.length];
    960 
    961         // Apply the 1d Gaussian mask horizontally to the image and put the
    962         // intermediat results in a temporary array.
    963         int rowPointer = 0;
    964         for (int y = 0; y < h; y++) {
    965             for (int x = 0; x < w; x++) {
    966                 int sum = 0;
    967                 for (int i = 0; i < k.length; i++) {
    968                     int dx = x + i - off;
    969                     dx = clamp(dx, 0, w - 1);
    970                     sum = addPixel(sum, src[rowPointer + dx], k[i]);
    971                 }
    972                 tmp[x + rowPointer] = sum;
    973             }
    974             rowPointer += w;
    975         }
    976 
    977         // Apply the 1d Gaussian mask vertically to the intermediate array, and
    978         // the final results will be stored in the output array.
    979         for (int x = 0; x < w; x++) {
    980             rowPointer = 0;
    981             for (int y = 0; y < h; y++) {
    982                 int sum = 0;
    983                 for (int i = 0; i < k.length; i++) {
    984                     int dy = y + i - off;
    985                     dy = clamp(dy, 0, h - 1);
    986                     sum = addPixel(sum, tmp[dy * w + x], k[i]);
    987                 }
    988                 out[x + rowPointer] = sum;
    989                 rowPointer += w;
    990             }
    991         }
    992     }
    993 
    994     /**
    995      * Calculates a new dimension to fill the bound with the original aspect
    996      * ratio preserved.
    997      *
    998      * @param imageWidth The original width.
    999      * @param imageHeight The original height.
   1000      * @param imageRotation The clockwise rotation in degrees of the image which
   1001      *            the original dimension comes from.
   1002      * @param boundWidth The width of the bound.
   1003      * @param boundHeight The height of the bound.
   1004      * @returns The final width/height stored in Point.x/Point.y to fill the
   1005      *          bounds and preserve image aspect ratio.
   1006      */
   1007     public static Point resizeToFill(int imageWidth, int imageHeight, int imageRotation,
   1008             int boundWidth, int boundHeight) {
   1009         if (imageRotation % 180 != 0) {
   1010             // Swap width and height.
   1011             int savedWidth = imageWidth;
   1012             imageWidth = imageHeight;
   1013             imageHeight = savedWidth;
   1014         }
   1015         if (imageWidth == ImageData.SIZE_FULL
   1016                 || imageHeight == ImageData.SIZE_FULL) {
   1017             imageWidth = boundWidth;
   1018             imageHeight = boundHeight;
   1019         }
   1020 
   1021         Point p = new Point();
   1022         p.x = boundWidth;
   1023         p.y = boundHeight;
   1024 
   1025         if (imageWidth * boundHeight > boundWidth * imageHeight) {
   1026             p.y = imageHeight * p.x / imageWidth;
   1027         } else {
   1028             p.x = imageWidth * p.y / imageHeight;
   1029         }
   1030 
   1031         return p;
   1032     }
   1033 
   1034     private static class ImageFileNamer {
   1035         private final SimpleDateFormat mFormat;
   1036 
   1037         // The date (in milliseconds) used to generate the last name.
   1038         private long mLastDate;
   1039 
   1040         // Number of names generated for the same second.
   1041         private int mSameSecondCount;
   1042 
   1043         public ImageFileNamer(String format) {
   1044             mFormat = new SimpleDateFormat(format);
   1045         }
   1046 
   1047         public String generateName(long dateTaken) {
   1048             Date date = new Date(dateTaken);
   1049             String result = mFormat.format(date);
   1050 
   1051             // If the last name was generated for the same second,
   1052             // we append _1, _2, etc to the name.
   1053             if (dateTaken / 1000 == mLastDate / 1000) {
   1054                 mSameSecondCount++;
   1055                 result += "_" + mSameSecondCount;
   1056             } else {
   1057                 mLastDate = dateTaken;
   1058                 mSameSecondCount = 0;
   1059             }
   1060 
   1061             return result;
   1062         }
   1063     }
   1064 
   1065     public static void playVideo(Activity activity, Uri uri, String title) {
   1066         try {
   1067             CameraActivity cameraActivity = (CameraActivity)activity;
   1068             boolean isSecureCamera = cameraActivity.isSecureCamera();
   1069             if (!isSecureCamera) {
   1070                 Intent intent = IntentHelper.getVideoPlayerIntent(uri)
   1071                         .putExtra(Intent.EXTRA_TITLE, title)
   1072                         .putExtra(KEY_TREAT_UP_AS_BACK, true);
   1073                 cameraActivity.launchActivityByIntent(intent);
   1074             } else {
   1075                 // In order not to send out any intent to be intercepted and
   1076                 // show the lock screen immediately, we just let the secure
   1077                 // camera activity finish.
   1078                 activity.finish();
   1079             }
   1080         } catch (ActivityNotFoundException e) {
   1081             Toast.makeText(activity, activity.getString(R.string.video_err),
   1082                     Toast.LENGTH_SHORT).show();
   1083         }
   1084     }
   1085 
   1086     /**
   1087      * Starts GMM with the given location shown. If this fails, and GMM could
   1088      * not be found, we use a geo intent as a fallback.
   1089      *
   1090      * @param activity the activity to use for launching the Maps intent.
   1091      * @param latLong a 2-element array containing {latitude/longitude}.
   1092      */
   1093     public static void showOnMap(Activity activity, double[] latLong) {
   1094         try {
   1095             // We don't use "geo:latitude,longitude" because it only centers
   1096             // the MapView to the specified location, but we need a marker
   1097             // for further operations (routing to/from).
   1098             // The q=(lat, lng) syntax is suggested by geo-team.
   1099             String uri = String.format(Locale.ENGLISH, "http://maps.google.com/maps?f=q&q=(%f,%f)",
   1100                     latLong[0], latLong[1]);
   1101             ComponentName compName = new ComponentName(MAPS_PACKAGE_NAME,
   1102                     MAPS_CLASS_NAME);
   1103             Intent mapsIntent = new Intent(Intent.ACTION_VIEW,
   1104                     Uri.parse(uri)).setComponent(compName);
   1105             mapsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
   1106             activity.startActivity(mapsIntent);
   1107         } catch (ActivityNotFoundException e) {
   1108             // Use the "geo intent" if no GMM is installed
   1109             Log.e(TAG, "GMM activity not found!", e);
   1110             String url = String.format(Locale.ENGLISH, "geo:%f,%f", latLong[0], latLong[1]);
   1111             Intent mapsIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
   1112             activity.startActivity(mapsIntent);
   1113         }
   1114     }
   1115 
   1116     /**
   1117      * Dumps the stack trace.
   1118      *
   1119      * @param level How many levels of the stack are dumped. 0 means all.
   1120      * @return A {@link java.lang.String} of all the output with newline between
   1121      *         each.
   1122      */
   1123     public static String dumpStackTrace(int level) {
   1124         StackTraceElement[] elems = Thread.currentThread().getStackTrace();
   1125         // Ignore the first 3 elements.
   1126         level = (level == 0 ? elems.length : Math.min(level + 3, elems.length));
   1127         String ret = new String();
   1128         for (int i = 3; i < level; i++) {
   1129             ret = ret + "\t" + elems[i].toString() + '\n';
   1130         }
   1131         return ret;
   1132     }
   1133 
   1134     /**
   1135      * Gets the theme color of a specific mode.
   1136      *
   1137      * @param modeIndex index of the mode
   1138      * @param context current context
   1139      * @return theme color of the mode if input index is valid, otherwise 0
   1140      */
   1141     public static int getCameraThemeColorId(int modeIndex, Context context) {
   1142 
   1143         // Find the theme color using id from the color array
   1144         TypedArray colorRes = context.getResources()
   1145                 .obtainTypedArray(R.array.camera_mode_theme_color);
   1146         if (modeIndex >= colorRes.length() || modeIndex < 0) {
   1147             // Mode index not found
   1148             Log.e(TAG, "Invalid mode index: " + modeIndex);
   1149             return 0;
   1150         }
   1151         return colorRes.getResourceId(modeIndex, 0);
   1152     }
   1153 
   1154     /**
   1155      * Gets the mode icon resource id of a specific mode.
   1156      *
   1157      * @param modeIndex index of the mode
   1158      * @param context current context
   1159      * @return icon resource id if the index is valid, otherwise 0
   1160      */
   1161     public static int getCameraModeIconResId(int modeIndex, Context context) {
   1162         // Find the camera mode icon using id
   1163         TypedArray cameraModesIcons = context.getResources()
   1164                 .obtainTypedArray(R.array.camera_mode_icon);
   1165         if (modeIndex >= cameraModesIcons.length() || modeIndex < 0) {
   1166             // Mode index not found
   1167             Log.e(TAG, "Invalid mode index: " + modeIndex);
   1168             return 0;
   1169         }
   1170         return cameraModesIcons.getResourceId(modeIndex, 0);
   1171     }
   1172 
   1173     /**
   1174      * Gets the mode text of a specific mode.
   1175      *
   1176      * @param modeIndex index of the mode
   1177      * @param context current context
   1178      * @return mode text if the index is valid, otherwise a new empty string
   1179      */
   1180     public static String getCameraModeText(int modeIndex, Context context) {
   1181         // Find the camera mode icon using id
   1182         String[] cameraModesText = context.getResources()
   1183                 .getStringArray(R.array.camera_mode_text);
   1184         if (modeIndex < 0 || modeIndex >= cameraModesText.length) {
   1185             Log.e(TAG, "Invalid mode index: " + modeIndex);
   1186             return new String();
   1187         }
   1188         return cameraModesText[modeIndex];
   1189     }
   1190 
   1191     /**
   1192      * Gets the mode content description of a specific mode.
   1193      *
   1194      * @param modeIndex index of the mode
   1195      * @param context current context
   1196      * @return mode content description if the index is valid, otherwise a new
   1197      *         empty string
   1198      */
   1199     public static String getCameraModeContentDescription(int modeIndex, Context context) {
   1200         String[] cameraModesDesc = context.getResources()
   1201                 .getStringArray(R.array.camera_mode_content_description);
   1202         if (modeIndex < 0 || modeIndex >= cameraModesDesc.length) {
   1203             Log.e(TAG, "Invalid mode index: " + modeIndex);
   1204             return new String();
   1205         }
   1206         return cameraModesDesc[modeIndex];
   1207     }
   1208 
   1209     /**
   1210      * Gets the shutter icon res id for a specific mode.
   1211      *
   1212      * @param modeIndex index of the mode
   1213      * @param context current context
   1214      * @return mode shutter icon id if the index is valid, otherwise 0.
   1215      */
   1216     public static int getCameraShutterIconId(int modeIndex, Context context) {
   1217         // Find the camera mode icon using id
   1218         TypedArray shutterIcons = context.getResources()
   1219                 .obtainTypedArray(R.array.camera_mode_shutter_icon);
   1220         if (modeIndex < 0 || modeIndex >= shutterIcons.length()) {
   1221             Log.e(TAG, "Invalid mode index: " + modeIndex);
   1222             throw new IllegalStateException("Invalid mode index: " + modeIndex);
   1223         }
   1224         return shutterIcons.getResourceId(modeIndex, 0);
   1225     }
   1226 
   1227     /**
   1228      * Gets the parent mode that hosts a specific mode in nav drawer.
   1229      *
   1230      * @param modeIndex index of the mode
   1231      * @param context current context
   1232      * @return mode id if the index is valid, otherwise 0
   1233      */
   1234     public static int getCameraModeParentModeId(int modeIndex, Context context) {
   1235         // Find the camera mode icon using id
   1236         int[] cameraModeParent = context.getResources()
   1237                 .getIntArray(R.array.camera_mode_nested_in_nav_drawer);
   1238         if (modeIndex < 0 || modeIndex >= cameraModeParent.length) {
   1239             Log.e(TAG, "Invalid mode index: " + modeIndex);
   1240             return 0;
   1241         }
   1242         return cameraModeParent[modeIndex];
   1243     }
   1244 
   1245     /**
   1246      * Gets the mode cover icon resource id of a specific mode.
   1247      *
   1248      * @param modeIndex index of the mode
   1249      * @param context current context
   1250      * @return icon resource id if the index is valid, otherwise 0
   1251      */
   1252     public static int getCameraModeCoverIconResId(int modeIndex, Context context) {
   1253         // Find the camera mode icon using id
   1254         TypedArray cameraModesIcons = context.getResources()
   1255                 .obtainTypedArray(R.array.camera_mode_cover_icon);
   1256         if (modeIndex >= cameraModesIcons.length() || modeIndex < 0) {
   1257             // Mode index not found
   1258             Log.e(TAG, "Invalid mode index: " + modeIndex);
   1259             return 0;
   1260         }
   1261         return cameraModesIcons.getResourceId(modeIndex, 0);
   1262     }
   1263 
   1264     /**
   1265      * Gets the number of cores available in this device, across all processors.
   1266      * Requires: Ability to peruse the filesystem at "/sys/devices/system/cpu"
   1267      * <p>
   1268      * Source: http://stackoverflow.com/questions/7962155/
   1269      *
   1270      * @return The number of cores, or 1 if failed to get result
   1271      */
   1272     public static int getNumCpuCores() {
   1273         // Private Class to display only CPU devices in the directory listing
   1274         class CpuFilter implements java.io.FileFilter {
   1275             @Override
   1276             public boolean accept(java.io.File pathname) {
   1277                 // Check if filename is "cpu", followed by a single digit number
   1278                 if (java.util.regex.Pattern.matches("cpu[0-9]+", pathname.getName())) {
   1279                     return true;
   1280                 }
   1281                 return false;
   1282             }
   1283         }
   1284 
   1285         try {
   1286             // Get directory containing CPU info
   1287             java.io.File dir = new java.io.File("/sys/devices/system/cpu/");
   1288             // Filter to only list the devices we care about
   1289             java.io.File[] files = dir.listFiles(new CpuFilter());
   1290             // Return the number of cores (virtual CPU devices)
   1291             return files.length;
   1292         } catch (Exception e) {
   1293             // Default to return 1 core
   1294             Log.e(TAG, "Failed to count number of cores, defaulting to 1", e);
   1295             return 1;
   1296         }
   1297     }
   1298 
   1299     /**
   1300      * Given the device orientation and Camera2 characteristics, this returns
   1301      * the required JPEG rotation for this camera.
   1302      *
   1303      * @param deviceOrientationDegrees the device orientation in degrees.
   1304      * @return The JPEG orientation in degrees.
   1305      */
   1306     public static int getJpegRotation(int deviceOrientationDegrees,
   1307             CameraCharacteristics characteristics) {
   1308         if (deviceOrientationDegrees == OrientationEventListener.ORIENTATION_UNKNOWN) {
   1309             return 0;
   1310         }
   1311         int facing = characteristics.get(CameraCharacteristics.LENS_FACING);
   1312         int sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
   1313         if (facing == CameraMetadata.LENS_FACING_FRONT) {
   1314             return (sensorOrientation + deviceOrientationDegrees) % 360;
   1315         } else {
   1316             return (sensorOrientation - deviceOrientationDegrees + 360) % 360;
   1317         }
   1318     }
   1319 }
   1320