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