Home | History | Annotate | Download | only in camera
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.camera;
     18 
     19 import android.annotation.TargetApi;
     20 import android.app.Activity;
     21 import android.app.AlertDialog;
     22 import android.content.ContentProviderClient;
     23 import android.content.ContentResolver;
     24 import android.content.Context;
     25 import android.content.DialogInterface;
     26 import android.content.Intent;
     27 import android.content.SharedPreferences.Editor;
     28 import android.content.res.Configuration;
     29 import android.graphics.Bitmap;
     30 import android.graphics.SurfaceTexture;
     31 import android.hardware.Camera.CameraInfo;
     32 import android.hardware.Camera.Parameters;
     33 import android.hardware.Camera.PictureCallback;
     34 import android.hardware.Camera.Size;
     35 import android.hardware.Sensor;
     36 import android.hardware.SensorEvent;
     37 import android.hardware.SensorEventListener;
     38 import android.hardware.SensorManager;
     39 import android.location.Location;
     40 import android.media.CameraProfile;
     41 import android.net.Uri;
     42 import android.os.Bundle;
     43 import android.os.ConditionVariable;
     44 import android.os.Handler;
     45 import android.os.Looper;
     46 import android.os.Message;
     47 import android.os.MessageQueue;
     48 import android.os.SystemClock;
     49 import android.provider.MediaStore;
     50 import android.util.Log;
     51 import android.view.KeyEvent;
     52 import android.view.MotionEvent;
     53 import android.view.OrientationEventListener;
     54 import android.view.SurfaceHolder;
     55 import android.view.View;
     56 import android.view.WindowManager;
     57 
     58 import com.android.camera.CameraManager.CameraProxy;
     59 import com.android.camera.ui.CountDownView.OnCountDownFinishedListener;
     60 import com.android.camera.ui.PopupManager;
     61 import com.android.camera.ui.RotateTextToast;
     62 import com.android.gallery3d.R;
     63 import com.android.gallery3d.common.ApiHelper;
     64 import com.android.gallery3d.exif.ExifInterface;
     65 import com.android.gallery3d.exif.ExifTag;
     66 import com.android.gallery3d.exif.Rational;
     67 import com.android.gallery3d.filtershow.FilterShowActivity;
     68 import com.android.gallery3d.filtershow.crop.CropExtras;
     69 import com.android.gallery3d.util.UsageStatistics;
     70 
     71 import java.io.File;
     72 import java.io.FileNotFoundException;
     73 import java.io.FileOutputStream;
     74 import java.io.IOException;
     75 import java.io.OutputStream;
     76 import java.util.ArrayList;
     77 import java.util.Collections;
     78 import java.util.Formatter;
     79 import java.util.List;
     80 
     81 public class PhotoModule
     82     implements CameraModule,
     83     PhotoController,
     84     FocusOverlayManager.Listener,
     85     CameraPreference.OnPreferenceChangedListener,
     86     ShutterButton.OnShutterButtonListener,
     87     MediaSaveService.Listener,
     88     OnCountDownFinishedListener,
     89     SensorEventListener {
     90 
     91     private static final String TAG = "CAM_PhotoModule";
     92 
     93     // We number the request code from 1000 to avoid collision with Gallery.
     94     private static final int REQUEST_CROP = 1000;
     95 
     96     private static final int SETUP_PREVIEW = 1;
     97     private static final int FIRST_TIME_INIT = 2;
     98     private static final int CLEAR_SCREEN_DELAY = 3;
     99     private static final int SET_CAMERA_PARAMETERS_WHEN_IDLE = 4;
    100     private static final int CHECK_DISPLAY_ROTATION = 5;
    101     private static final int SHOW_TAP_TO_FOCUS_TOAST = 6;
    102     private static final int SWITCH_CAMERA = 7;
    103     private static final int SWITCH_CAMERA_START_ANIMATION = 8;
    104     private static final int CAMERA_OPEN_DONE = 9;
    105     private static final int START_PREVIEW_DONE = 10;
    106     private static final int OPEN_CAMERA_FAIL = 11;
    107     private static final int CAMERA_DISABLED = 12;
    108     private static final int CAPTURE_ANIMATION_DONE = 13;
    109 
    110     // The subset of parameters we need to update in setCameraParameters().
    111     private static final int UPDATE_PARAM_INITIALIZE = 1;
    112     private static final int UPDATE_PARAM_ZOOM = 2;
    113     private static final int UPDATE_PARAM_PREFERENCE = 4;
    114     private static final int UPDATE_PARAM_ALL = -1;
    115 
    116     // This is the timeout to keep the camera in onPause for the first time
    117     // after screen on if the activity is started from secure lock screen.
    118     private static final int KEEP_CAMERA_TIMEOUT = 1000; // ms
    119 
    120     // copied from Camera hierarchy
    121     private CameraActivity mActivity;
    122     private CameraProxy mCameraDevice;
    123     private int mCameraId;
    124     private Parameters mParameters;
    125     private boolean mPaused;
    126 
    127     private PhotoUI mUI;
    128 
    129     // these are only used by Camera
    130 
    131     // The activity is going to switch to the specified camera id. This is
    132     // needed because texture copy is done in GL thread. -1 means camera is not
    133     // switching.
    134     protected int mPendingSwitchCameraId = -1;
    135     private boolean mOpenCameraFail;
    136     private boolean mCameraDisabled;
    137 
    138     // When setCameraParametersWhenIdle() is called, we accumulate the subsets
    139     // needed to be updated in mUpdateSet.
    140     private int mUpdateSet;
    141 
    142     private static final int SCREEN_DELAY = 2 * 60 * 1000;
    143 
    144     private int mZoomValue;  // The current zoom value.
    145 
    146     private Parameters mInitialParams;
    147     private boolean mFocusAreaSupported;
    148     private boolean mMeteringAreaSupported;
    149     private boolean mAeLockSupported;
    150     private boolean mAwbLockSupported;
    151     private boolean mContinousFocusSupported;
    152 
    153     // The degrees of the device rotated clockwise from its natural orientation.
    154     private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
    155     private ComboPreferences mPreferences;
    156 
    157     private static final String sTempCropFilename = "crop-temp";
    158 
    159     private ContentProviderClient mMediaProviderClient;
    160     private boolean mFaceDetectionStarted = false;
    161 
    162     // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
    163     private String mCropValue;
    164     private Uri mSaveUri;
    165 
    166     // We use a queue to generated names of the images to be used later
    167     // when the image is ready to be saved.
    168     private NamedImages mNamedImages;
    169 
    170     private Runnable mDoSnapRunnable = new Runnable() {
    171         @Override
    172         public void run() {
    173             onShutterButtonClick();
    174         }
    175     };
    176 
    177     private Runnable mFlashRunnable = new Runnable() {
    178         @Override
    179         public void run() {
    180             animateFlash();
    181         }
    182     };
    183 
    184     private final StringBuilder mBuilder = new StringBuilder();
    185     private final Formatter mFormatter = new Formatter(mBuilder);
    186     private final Object[] mFormatterArgs = new Object[1];
    187 
    188     /**
    189      * An unpublished intent flag requesting to return as soon as capturing
    190      * is completed.
    191      *
    192      * TODO: consider publishing by moving into MediaStore.
    193      */
    194     private static final String EXTRA_QUICK_CAPTURE =
    195             "android.intent.extra.quickCapture";
    196 
    197     // The display rotation in degrees. This is only valid when mCameraState is
    198     // not PREVIEW_STOPPED.
    199     private int mDisplayRotation;
    200     // The value for android.hardware.Camera.setDisplayOrientation.
    201     private int mCameraDisplayOrientation;
    202     // The value for UI components like indicators.
    203     private int mDisplayOrientation;
    204     // The value for android.hardware.Camera.Parameters.setRotation.
    205     private int mJpegRotation;
    206     private boolean mFirstTimeInitialized;
    207     private boolean mIsImageCaptureIntent;
    208 
    209     private int mCameraState = PREVIEW_STOPPED;
    210     private boolean mSnapshotOnIdle = false;
    211 
    212     private ContentResolver mContentResolver;
    213 
    214     private LocationManager mLocationManager;
    215 
    216     private final PostViewPictureCallback mPostViewPictureCallback =
    217             new PostViewPictureCallback();
    218     private final RawPictureCallback mRawPictureCallback =
    219             new RawPictureCallback();
    220     private final AutoFocusCallback mAutoFocusCallback =
    221             new AutoFocusCallback();
    222     private final Object mAutoFocusMoveCallback =
    223             ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
    224             ? new AutoFocusMoveCallback()
    225             : null;
    226 
    227     private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
    228 
    229     private long mFocusStartTime;
    230     private long mShutterCallbackTime;
    231     private long mPostViewPictureCallbackTime;
    232     private long mRawPictureCallbackTime;
    233     private long mJpegPictureCallbackTime;
    234     private long mOnResumeTime;
    235     private byte[] mJpegImageData;
    236 
    237     // These latency time are for the CameraLatency test.
    238     public long mAutoFocusTime;
    239     public long mShutterLag;
    240     public long mShutterToPictureDisplayedTime;
    241     public long mPictureDisplayedToJpegCallbackTime;
    242     public long mJpegCallbackFinishTime;
    243     public long mCaptureStartTime;
    244 
    245     // This handles everything about focus.
    246     private FocusOverlayManager mFocusManager;
    247 
    248     private String mSceneMode;
    249 
    250     private final Handler mHandler = new MainHandler();
    251     private PreferenceGroup mPreferenceGroup;
    252 
    253     private boolean mQuickCapture;
    254 
    255     CameraStartUpThread mCameraStartUpThread;
    256     ConditionVariable mStartPreviewPrerequisiteReady = new ConditionVariable();
    257 
    258     private SensorManager mSensorManager;
    259     private float[] mGData = new float[3];
    260     private float[] mMData = new float[3];
    261     private float[] mR = new float[16];
    262     private int mHeading = -1;
    263 
    264     private MediaSaveService.OnMediaSavedListener mOnMediaSavedListener =
    265             new MediaSaveService.OnMediaSavedListener() {
    266                 @Override
    267                 public void onMediaSaved(Uri uri) {
    268                     if (uri != null) {
    269                         mActivity.addSecureAlbumItemIfNeeded(false, uri);
    270                         Util.broadcastNewPicture(mActivity, uri);
    271                     }
    272                 }
    273             };
    274 
    275     // The purpose is not to block the main thread in onCreate and onResume.
    276     private class CameraStartUpThread extends Thread {
    277         private volatile boolean mCancelled;
    278 
    279         public void cancel() {
    280             mCancelled = true;
    281             interrupt();
    282         }
    283 
    284         public boolean isCanceled() {
    285             return mCancelled;
    286         }
    287 
    288         @Override
    289         public void run() {
    290             try {
    291                 // We need to check whether the activity is paused before long
    292                 // operations to ensure that onPause() can be done ASAP.
    293                 if (mCancelled) return;
    294                 mCameraDevice = Util.openCamera(mActivity, mCameraId);
    295                 mParameters = mCameraDevice.getParameters();
    296                 // Wait until all the initialization needed by startPreview are
    297                 // done.
    298                 mStartPreviewPrerequisiteReady.block();
    299 
    300                 initializeCapabilities();
    301                 if (mFocusManager == null) initializeFocusManager();
    302                 if (mCancelled) return;
    303                 setCameraParameters(UPDATE_PARAM_ALL);
    304                 mHandler.sendEmptyMessage(CAMERA_OPEN_DONE);
    305                 if (mCancelled) return;
    306                 startPreview();
    307                 mHandler.sendEmptyMessage(START_PREVIEW_DONE);
    308                 mOnResumeTime = SystemClock.uptimeMillis();
    309                 mHandler.sendEmptyMessage(CHECK_DISPLAY_ROTATION);
    310             } catch (CameraHardwareException e) {
    311                 mHandler.sendEmptyMessage(OPEN_CAMERA_FAIL);
    312             } catch (CameraDisabledException e) {
    313                 mHandler.sendEmptyMessage(CAMERA_DISABLED);
    314             }
    315         }
    316     }
    317 
    318     /**
    319      * This Handler is used to post message back onto the main thread of the
    320      * application
    321      */
    322     private class MainHandler extends Handler {
    323         @Override
    324         public void handleMessage(Message msg) {
    325             switch (msg.what) {
    326                 case SETUP_PREVIEW: {
    327                     setupPreview();
    328                     break;
    329                 }
    330 
    331                 case CLEAR_SCREEN_DELAY: {
    332                     mActivity.getWindow().clearFlags(
    333                             WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    334                     break;
    335                 }
    336 
    337                 case FIRST_TIME_INIT: {
    338                     initializeFirstTime();
    339                     break;
    340                 }
    341 
    342                 case SET_CAMERA_PARAMETERS_WHEN_IDLE: {
    343                     setCameraParametersWhenIdle(0);
    344                     break;
    345                 }
    346 
    347                 case CHECK_DISPLAY_ROTATION: {
    348                     // Set the display orientation if display rotation has changed.
    349                     // Sometimes this happens when the device is held upside
    350                     // down and camera app is opened. Rotation animation will
    351                     // take some time and the rotation value we have got may be
    352                     // wrong. Framework does not have a callback for this now.
    353                     if (Util.getDisplayRotation(mActivity) != mDisplayRotation) {
    354                         setDisplayOrientation();
    355                     }
    356                     if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
    357                         mHandler.sendEmptyMessageDelayed(CHECK_DISPLAY_ROTATION, 100);
    358                     }
    359                     break;
    360                 }
    361 
    362                 case SHOW_TAP_TO_FOCUS_TOAST: {
    363                     showTapToFocusToast();
    364                     break;
    365                 }
    366 
    367                 case SWITCH_CAMERA: {
    368                     switchCamera();
    369                     break;
    370                 }
    371 
    372                 case SWITCH_CAMERA_START_ANIMATION: {
    373                     ((CameraScreenNail) mActivity.mCameraScreenNail).animateSwitchCamera();
    374                     break;
    375                 }
    376 
    377                 case CAMERA_OPEN_DONE: {
    378                     onCameraOpened();
    379                     break;
    380                 }
    381 
    382                 case START_PREVIEW_DONE: {
    383                     onPreviewStarted();
    384                     break;
    385                 }
    386 
    387                 case OPEN_CAMERA_FAIL: {
    388                     mCameraStartUpThread = null;
    389                     mOpenCameraFail = true;
    390                     Util.showErrorAndFinish(mActivity,
    391                             R.string.cannot_connect_camera);
    392                     break;
    393                 }
    394 
    395                 case CAMERA_DISABLED: {
    396                     mCameraStartUpThread = null;
    397                     mCameraDisabled = true;
    398                     Util.showErrorAndFinish(mActivity,
    399                             R.string.camera_disabled);
    400                     break;
    401                 }
    402                 case CAPTURE_ANIMATION_DONE: {
    403                     mUI.enablePreviewThumb(false);
    404                     break;
    405                 }
    406             }
    407         }
    408     }
    409 
    410     @Override
    411     public void init(CameraActivity activity, View parent, boolean reuseNail) {
    412         mActivity = activity;
    413         mUI = new PhotoUI(activity, this, parent);
    414         mPreferences = new ComboPreferences(mActivity);
    415         CameraSettings.upgradeGlobalPreferences(mPreferences.getGlobal());
    416         mCameraId = getPreferredCameraId(mPreferences);
    417 
    418         mContentResolver = mActivity.getContentResolver();
    419 
    420         // To reduce startup time, open the camera and start the preview in
    421         // another thread.
    422         mCameraStartUpThread = new CameraStartUpThread();
    423         mCameraStartUpThread.start();
    424 
    425 
    426         // Surface texture is from camera screen nail and startPreview needs it.
    427         // This must be done before startPreview.
    428         mIsImageCaptureIntent = isImageCaptureIntent();
    429         if (reuseNail) {
    430             mActivity.reuseCameraScreenNail(!mIsImageCaptureIntent);
    431         } else {
    432             mActivity.createCameraScreenNail(!mIsImageCaptureIntent);
    433         }
    434 
    435         mPreferences.setLocalId(mActivity, mCameraId);
    436         CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
    437         // we need to reset exposure for the preview
    438         resetExposureCompensation();
    439         // Starting the preview needs preferences, camera screen nail, and
    440         // focus area indicator.
    441         mStartPreviewPrerequisiteReady.open();
    442 
    443         initializeControlByIntent();
    444         mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
    445         mLocationManager = new LocationManager(mActivity, mUI);
    446         mSensorManager = (SensorManager)(mActivity.getSystemService(Context.SENSOR_SERVICE));
    447 
    448     }
    449 
    450     private void initializeControlByIntent() {
    451         mUI.initializeControlByIntent();
    452         if (mIsImageCaptureIntent) {
    453             setupCaptureParams();
    454         }
    455     }
    456 
    457     private void onPreviewStarted() {
    458         mCameraStartUpThread = null;
    459         setCameraState(IDLE);
    460         if (!ApiHelper.HAS_SURFACE_TEXTURE) {
    461             // This may happen if surfaceCreated has arrived.
    462             mCameraDevice.setPreviewDisplayAsync(mUI.getSurfaceHolder());
    463         }
    464         startFaceDetection();
    465         locationFirstRun();
    466     }
    467 
    468     // Prompt the user to pick to record location for the very first run of
    469     // camera only
    470     private void locationFirstRun() {
    471         if (RecordLocationPreference.isSet(mPreferences)) {
    472             return;
    473         }
    474         if (mActivity.isSecureCamera()) return;
    475         // Check if the back camera exists
    476         int backCameraId = CameraHolder.instance().getBackCameraId();
    477         if (backCameraId == -1) {
    478             // If there is no back camera, do not show the prompt.
    479             return;
    480         }
    481 
    482         new AlertDialog.Builder(mActivity)
    483             .setTitle(R.string.remember_location_title)
    484             .setMessage(R.string.remember_location_prompt)
    485             .setPositiveButton(R.string.remember_location_yes, new DialogInterface.OnClickListener() {
    486                 @Override
    487                 public void onClick(DialogInterface dialog, int arg1) {
    488                     setLocationPreference(RecordLocationPreference.VALUE_ON);
    489                 }
    490             })
    491             .setNegativeButton(R.string.remember_location_no, new DialogInterface.OnClickListener() {
    492                 @Override
    493                 public void onClick(DialogInterface dialog, int arg1) {
    494                     dialog.cancel();
    495                 }
    496             })
    497             .setOnCancelListener(new DialogInterface.OnCancelListener() {
    498                 @Override
    499                 public void onCancel(DialogInterface dialog) {
    500                     setLocationPreference(RecordLocationPreference.VALUE_OFF);
    501                 }
    502             })
    503             .show();
    504     }
    505 
    506     private void setLocationPreference(String value) {
    507         mPreferences.edit()
    508             .putString(CameraSettings.KEY_RECORD_LOCATION, value)
    509             .apply();
    510         // TODO: Fix this to use the actual onSharedPreferencesChanged listener
    511         // instead of invoking manually
    512         onSharedPreferenceChanged();
    513     }
    514 
    515     private void onCameraOpened() {
    516         View root = mUI.getRootView();
    517         // These depend on camera parameters.
    518 
    519         int width = root.getWidth();
    520         int height = root.getHeight();
    521         mFocusManager.setPreviewSize(width, height);
    522         // Full-screen screennail
    523         if (Util.getDisplayRotation(mActivity) % 180 == 0) {
    524             ((CameraScreenNail) mActivity.mCameraScreenNail).setPreviewFrameLayoutSize(width, height);
    525         } else {
    526             ((CameraScreenNail) mActivity.mCameraScreenNail).setPreviewFrameLayoutSize(height, width);
    527         }
    528         // Set touch focus listener.
    529         mActivity.setSingleTapUpListener(root);
    530         openCameraCommon();
    531         onFullScreenChanged(mActivity.isInCameraApp());
    532     }
    533 
    534     private void switchCamera() {
    535         if (mPaused) return;
    536 
    537         Log.v(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
    538         mCameraId = mPendingSwitchCameraId;
    539         mPendingSwitchCameraId = -1;
    540         setCameraId(mCameraId);
    541 
    542         // from onPause
    543         closeCamera();
    544         mUI.collapseCameraControls();
    545         mUI.clearFaces();
    546         if (mFocusManager != null) mFocusManager.removeMessages();
    547 
    548         // Restart the camera and initialize the UI. From onCreate.
    549         mPreferences.setLocalId(mActivity, mCameraId);
    550         CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
    551         try {
    552             mCameraDevice = Util.openCamera(mActivity, mCameraId);
    553             mParameters = mCameraDevice.getParameters();
    554         } catch (CameraHardwareException e) {
    555             Util.showErrorAndFinish(mActivity, R.string.cannot_connect_camera);
    556             return;
    557         } catch (CameraDisabledException e) {
    558             Util.showErrorAndFinish(mActivity, R.string.camera_disabled);
    559             return;
    560         }
    561         initializeCapabilities();
    562         CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
    563         boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
    564         mFocusManager.setMirror(mirror);
    565         mFocusManager.setParameters(mInitialParams);
    566         setupPreview();
    567 
    568         openCameraCommon();
    569 
    570         if (ApiHelper.HAS_SURFACE_TEXTURE) {
    571             // Start switch camera animation. Post a message because
    572             // onFrameAvailable from the old camera may already exist.
    573             mHandler.sendEmptyMessage(SWITCH_CAMERA_START_ANIMATION);
    574         }
    575     }
    576 
    577     protected void setCameraId(int cameraId) {
    578         ListPreference pref = mPreferenceGroup.findPreference(CameraSettings.KEY_CAMERA_ID);
    579         pref.setValue("" + cameraId);
    580     }
    581 
    582     // either open a new camera or switch cameras
    583     private void openCameraCommon() {
    584         loadCameraPreferences();
    585 
    586         mUI.onCameraOpened(mPreferenceGroup, mPreferences, mParameters, this);
    587         updateSceneMode();
    588         showTapToFocusToastIfNeeded();
    589 
    590 
    591     }
    592 
    593     public void onScreenSizeChanged(int width, int height, int previewWidth, int previewHeight) {
    594         Log.d(TAG, "Preview size changed.");
    595         if (mFocusManager != null) mFocusManager.setPreviewSize(width, height);
    596         ((CameraScreenNail) mActivity.mCameraScreenNail).setPreviewFrameLayoutSize(
    597                 previewWidth, previewHeight);
    598         mActivity.notifyScreenNailChanged();
    599     }
    600 
    601     private void resetExposureCompensation() {
    602         String value = mPreferences.getString(CameraSettings.KEY_EXPOSURE,
    603                 CameraSettings.EXPOSURE_DEFAULT_VALUE);
    604         if (!CameraSettings.EXPOSURE_DEFAULT_VALUE.equals(value)) {
    605             Editor editor = mPreferences.edit();
    606             editor.putString(CameraSettings.KEY_EXPOSURE, "0");
    607             editor.apply();
    608         }
    609     }
    610 
    611     private void keepMediaProviderInstance() {
    612         // We want to keep a reference to MediaProvider in camera's lifecycle.
    613         // TODO: Utilize mMediaProviderClient instance to replace
    614         // ContentResolver calls.
    615         if (mMediaProviderClient == null) {
    616             mMediaProviderClient = mContentResolver
    617                     .acquireContentProviderClient(MediaStore.AUTHORITY);
    618         }
    619     }
    620 
    621     // Snapshots can only be taken after this is called. It should be called
    622     // once only. We could have done these things in onCreate() but we want to
    623     // make preview screen appear as soon as possible.
    624     private void initializeFirstTime() {
    625         if (mFirstTimeInitialized) return;
    626 
    627         // Initialize location service.
    628         boolean recordLocation = RecordLocationPreference.get(
    629                 mPreferences, mContentResolver);
    630         mLocationManager.recordLocation(recordLocation);
    631 
    632         keepMediaProviderInstance();
    633 
    634         mUI.initializeFirstTime();
    635         MediaSaveService s = mActivity.getMediaSaveService();
    636         // We set the listener only when both service and shutterbutton
    637         // are initialized.
    638         if (s != null) {
    639             s.setListener(this);
    640         }
    641 
    642         mNamedImages = new NamedImages();
    643 
    644         mFirstTimeInitialized = true;
    645         addIdleHandler();
    646 
    647         mActivity.updateStorageSpaceAndHint();
    648     }
    649 
    650     // If the activity is paused and resumed, this method will be called in
    651     // onResume.
    652     private void initializeSecondTime() {
    653         // Start location update if needed.
    654         boolean recordLocation = RecordLocationPreference.get(
    655                 mPreferences, mContentResolver);
    656         mLocationManager.recordLocation(recordLocation);
    657         MediaSaveService s = mActivity.getMediaSaveService();
    658         if (s != null) {
    659             s.setListener(this);
    660         }
    661         mNamedImages = new NamedImages();
    662         mUI.initializeSecondTime(mParameters);
    663         keepMediaProviderInstance();
    664     }
    665 
    666     @Override
    667     public void onSurfaceCreated(SurfaceHolder holder) {
    668         // Do not access the camera if camera start up thread is not finished.
    669         if (mCameraDevice == null || mCameraStartUpThread != null)
    670             return;
    671 
    672         mCameraDevice.setPreviewDisplayAsync(holder);
    673         // This happens when onConfigurationChanged arrives, surface has been
    674         // destroyed, and there is no onFullScreenChanged.
    675         if (mCameraState == PREVIEW_STOPPED) {
    676             setupPreview();
    677         }
    678     }
    679 
    680     private void showTapToFocusToastIfNeeded() {
    681         // Show the tap to focus toast if this is the first start.
    682         if (mFocusAreaSupported &&
    683                 mPreferences.getBoolean(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, true)) {
    684             // Delay the toast for one second to wait for orientation.
    685             mHandler.sendEmptyMessageDelayed(SHOW_TAP_TO_FOCUS_TOAST, 1000);
    686         }
    687     }
    688 
    689     private void addIdleHandler() {
    690         MessageQueue queue = Looper.myQueue();
    691         queue.addIdleHandler(new MessageQueue.IdleHandler() {
    692             @Override
    693             public boolean queueIdle() {
    694                 Storage.ensureOSXCompatible();
    695                 return false;
    696             }
    697         });
    698     }
    699 
    700     @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
    701     @Override
    702     public void startFaceDetection() {
    703         if (!ApiHelper.HAS_FACE_DETECTION) return;
    704         if (mFaceDetectionStarted) return;
    705         if (mParameters.getMaxNumDetectedFaces() > 0) {
    706             mFaceDetectionStarted = true;
    707             CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
    708             mUI.onStartFaceDetection(mDisplayOrientation,
    709                     (info.facing == CameraInfo.CAMERA_FACING_FRONT));
    710             mCameraDevice.setFaceDetectionListener(mUI);
    711             mCameraDevice.startFaceDetection();
    712         }
    713     }
    714 
    715     @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
    716     @Override
    717     public void stopFaceDetection() {
    718         if (!ApiHelper.HAS_FACE_DETECTION) return;
    719         if (!mFaceDetectionStarted) return;
    720         if (mParameters.getMaxNumDetectedFaces() > 0) {
    721             mFaceDetectionStarted = false;
    722             mCameraDevice.setFaceDetectionListener(null);
    723             mCameraDevice.stopFaceDetection();
    724             mUI.clearFaces();
    725         }
    726     }
    727 
    728     @Override
    729     public boolean dispatchTouchEvent(MotionEvent m) {
    730         if (mCameraState == SWITCHING_CAMERA) return true;
    731         return mUI.dispatchTouchEvent(m);
    732     }
    733 
    734     private final class ShutterCallback
    735             implements android.hardware.Camera.ShutterCallback {
    736 
    737         private boolean mAnimateFlash;
    738 
    739         public ShutterCallback(boolean animateFlash) {
    740             mAnimateFlash = animateFlash;
    741         }
    742 
    743         @Override
    744         public void onShutter() {
    745             mShutterCallbackTime = System.currentTimeMillis();
    746             mShutterLag = mShutterCallbackTime - mCaptureStartTime;
    747             Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
    748             if (mAnimateFlash) {
    749                 mActivity.runOnUiThread(mFlashRunnable);
    750             }
    751         }
    752     }
    753 
    754     private final class PostViewPictureCallback implements PictureCallback {
    755         @Override
    756         public void onPictureTaken(
    757                 byte [] data, android.hardware.Camera camera) {
    758             mPostViewPictureCallbackTime = System.currentTimeMillis();
    759             Log.v(TAG, "mShutterToPostViewCallbackTime = "
    760                     + (mPostViewPictureCallbackTime - mShutterCallbackTime)
    761                     + "ms");
    762         }
    763     }
    764 
    765     private final class RawPictureCallback implements PictureCallback {
    766         @Override
    767         public void onPictureTaken(
    768                 byte [] rawData, android.hardware.Camera camera) {
    769             mRawPictureCallbackTime = System.currentTimeMillis();
    770             Log.v(TAG, "mShutterToRawCallbackTime = "
    771                     + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
    772         }
    773     }
    774 
    775     private final class JpegPictureCallback implements PictureCallback {
    776         Location mLocation;
    777 
    778         public JpegPictureCallback(Location loc) {
    779             mLocation = loc;
    780         }
    781 
    782         @Override
    783         public void onPictureTaken(
    784                 final byte [] jpegData, final android.hardware.Camera camera) {
    785             if (mPaused) {
    786                 return;
    787             }
    788             if (mSceneMode == Util.SCENE_MODE_HDR) {
    789                 mActivity.showSwitcher();
    790                 mActivity.setSwipingEnabled(true);
    791             }
    792 
    793             mJpegPictureCallbackTime = System.currentTimeMillis();
    794             // If postview callback has arrived, the captured image is displayed
    795             // in postview callback. If not, the captured image is displayed in
    796             // raw picture callback.
    797             if (mPostViewPictureCallbackTime != 0) {
    798                 mShutterToPictureDisplayedTime =
    799                         mPostViewPictureCallbackTime - mShutterCallbackTime;
    800                 mPictureDisplayedToJpegCallbackTime =
    801                         mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
    802             } else {
    803                 mShutterToPictureDisplayedTime =
    804                         mRawPictureCallbackTime - mShutterCallbackTime;
    805                 mPictureDisplayedToJpegCallbackTime =
    806                         mJpegPictureCallbackTime - mRawPictureCallbackTime;
    807             }
    808             Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
    809                     + mPictureDisplayedToJpegCallbackTime + "ms");
    810 
    811             // Only animate when in full screen capture mode
    812             // i.e. If monkey/a user swipes to the gallery during picture taking,
    813             // don't show animation
    814             if (ApiHelper.HAS_SURFACE_TEXTURE && !mIsImageCaptureIntent
    815                     && mActivity.mShowCameraAppView) {
    816                 // Finish capture animation
    817                 mHandler.removeMessages(CAPTURE_ANIMATION_DONE);
    818                 ((CameraScreenNail) mActivity.mCameraScreenNail).animateSlide();
    819                 mHandler.sendEmptyMessageDelayed(CAPTURE_ANIMATION_DONE,
    820                         CaptureAnimManager.getAnimationDuration());
    821             }
    822             mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
    823             if (!mIsImageCaptureIntent) {
    824                 if (ApiHelper.CAN_START_PREVIEW_IN_JPEG_CALLBACK) {
    825                     setupPreview();
    826                 } else {
    827                     // Camera HAL of some devices have a bug. Starting preview
    828                     // immediately after taking a picture will fail. Wait some
    829                     // time before starting the preview.
    830                     mHandler.sendEmptyMessageDelayed(SETUP_PREVIEW, 300);
    831                 }
    832             }
    833 
    834             if (!mIsImageCaptureIntent) {
    835                 // Calculate the width and the height of the jpeg.
    836                 Size s = mParameters.getPictureSize();
    837                 ExifInterface exif = Exif.getExif(jpegData);
    838                 int orientation = Exif.getOrientation(exif);
    839                 int width, height;
    840                 if ((mJpegRotation + orientation) % 180 == 0) {
    841                     width = s.width;
    842                     height = s.height;
    843                 } else {
    844                     width = s.height;
    845                     height = s.width;
    846                 }
    847                 String title = mNamedImages.getTitle();
    848                 long date = mNamedImages.getDate();
    849                 if (title == null) {
    850                     Log.e(TAG, "Unbalanced name/data pair");
    851                 } else {
    852                     if (date == -1) date = mCaptureStartTime;
    853                     if (mHeading >= 0) {
    854                         // heading direction has been updated by the sensor.
    855                         ExifTag directionRefTag = exif.buildTag(
    856                                 ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
    857                                 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
    858                         ExifTag directionTag = exif.buildTag(
    859                                 ExifInterface.TAG_GPS_IMG_DIRECTION,
    860                                 new Rational(mHeading, 1));
    861                         exif.setTag(directionRefTag);
    862                         exif.setTag(directionTag);
    863                     }
    864                     mActivity.getMediaSaveService().addImage(
    865                             jpegData, title, date, mLocation, width, height,
    866                             orientation, exif, mOnMediaSavedListener, mContentResolver);
    867                 }
    868             } else {
    869                 mJpegImageData = jpegData;
    870                 if (!mQuickCapture) {
    871                     mUI.showPostCaptureAlert();
    872                 } else {
    873                     onCaptureDone();
    874                 }
    875             }
    876 
    877             // Check this in advance of each shot so we don't add to shutter
    878             // latency. It's true that someone else could write to the SD card in
    879             // the mean time and fill it, but that could have happened between the
    880             // shutter press and saving the JPEG too.
    881             mActivity.updateStorageSpaceAndHint();
    882 
    883             long now = System.currentTimeMillis();
    884             mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
    885             Log.v(TAG, "mJpegCallbackFinishTime = "
    886                     + mJpegCallbackFinishTime + "ms");
    887             mJpegPictureCallbackTime = 0;
    888         }
    889     }
    890 
    891     private final class AutoFocusCallback
    892             implements android.hardware.Camera.AutoFocusCallback {
    893         @Override
    894         public void onAutoFocus(
    895                 boolean focused, android.hardware.Camera camera) {
    896             if (mPaused) return;
    897 
    898             mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
    899             Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms");
    900             setCameraState(IDLE);
    901             mFocusManager.onAutoFocus(focused, mUI.isShutterPressed());
    902         }
    903     }
    904 
    905     @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
    906     private final class AutoFocusMoveCallback
    907             implements android.hardware.Camera.AutoFocusMoveCallback {
    908         @Override
    909         public void onAutoFocusMoving(
    910             boolean moving, android.hardware.Camera camera) {
    911                 mFocusManager.onAutoFocusMoving(moving);
    912         }
    913     }
    914 
    915     private static class NamedImages {
    916         private ArrayList<NamedEntity> mQueue;
    917         private boolean mStop;
    918         private NamedEntity mNamedEntity;
    919 
    920         public NamedImages() {
    921             mQueue = new ArrayList<NamedEntity>();
    922         }
    923 
    924         public void nameNewImage(ContentResolver resolver, long date) {
    925             NamedEntity r = new NamedEntity();
    926             r.title = Util.createJpegName(date);
    927             r.date = date;
    928             mQueue.add(r);
    929         }
    930 
    931         public String getTitle() {
    932             if (mQueue.isEmpty()) {
    933                 mNamedEntity = null;
    934                 return null;
    935             }
    936             mNamedEntity = mQueue.get(0);
    937             mQueue.remove(0);
    938 
    939             return mNamedEntity.title;
    940         }
    941 
    942         // Must be called after getTitle().
    943         public long getDate() {
    944             if (mNamedEntity == null) return -1;
    945             return mNamedEntity.date;
    946         }
    947 
    948         private static class NamedEntity {
    949             String title;
    950             long date;
    951         }
    952     }
    953 
    954     private void setCameraState(int state) {
    955         mCameraState = state;
    956         switch (state) {
    957         case PhotoController.PREVIEW_STOPPED:
    958         case PhotoController.SNAPSHOT_IN_PROGRESS:
    959         case PhotoController.FOCUSING:
    960         case PhotoController.SWITCHING_CAMERA:
    961             mUI.enableGestures(false);
    962             break;
    963         case PhotoController.IDLE:
    964             if (mActivity.isInCameraApp()) {
    965                 mUI.enableGestures(true);
    966             }
    967             break;
    968         }
    969     }
    970 
    971     private void animateFlash() {
    972         // Only animate when in full screen capture mode
    973         // i.e. If monkey/a user swipes to the gallery during picture taking,
    974         // don't show animation
    975         if (ApiHelper.HAS_SURFACE_TEXTURE && !mIsImageCaptureIntent
    976                 && mActivity.mShowCameraAppView) {
    977             // Start capture animation.
    978             ((CameraScreenNail) mActivity.mCameraScreenNail).animateFlash(mDisplayRotation);
    979             mUI.enablePreviewThumb(true);
    980             mHandler.sendEmptyMessageDelayed(CAPTURE_ANIMATION_DONE,
    981                     CaptureAnimManager.getAnimationDuration());
    982         }
    983     }
    984 
    985     @Override
    986     public boolean capture() {
    987         // If we are already in the middle of taking a snapshot or the image save request
    988         // is full then ignore.
    989         if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
    990                 || mCameraState == SWITCHING_CAMERA
    991                 || mActivity.getMediaSaveService().isQueueFull()) {
    992             return false;
    993         }
    994         mCaptureStartTime = System.currentTimeMillis();
    995         mPostViewPictureCallbackTime = 0;
    996         mJpegImageData = null;
    997 
    998         final boolean animateBefore = (mSceneMode == Util.SCENE_MODE_HDR);
    999 
   1000         if (animateBefore) {
   1001             animateFlash();
   1002         }
   1003 
   1004         // Set rotation and gps data.
   1005         int orientation;
   1006         // We need to be consistent with the framework orientation (i.e. the
   1007         // orientation of the UI.) when the auto-rotate screen setting is on.
   1008         if (mActivity.isAutoRotateScreen()) {
   1009             orientation = (360 - mDisplayRotation) % 360;
   1010         } else {
   1011             orientation = mOrientation;
   1012         }
   1013         mJpegRotation = Util.getJpegRotation(mCameraId, orientation);
   1014         mParameters.setRotation(mJpegRotation);
   1015         Location loc = mLocationManager.getCurrentLocation();
   1016         Util.setGpsParameters(mParameters, loc);
   1017         mCameraDevice.setParameters(mParameters);
   1018 
   1019         mCameraDevice.takePicture2(new ShutterCallback(!animateBefore),
   1020                 mRawPictureCallback, mPostViewPictureCallback,
   1021                 new JpegPictureCallback(loc), mCameraState,
   1022                 mFocusManager.getFocusState());
   1023 
   1024         mNamedImages.nameNewImage(mContentResolver, mCaptureStartTime);
   1025 
   1026         mFaceDetectionStarted = false;
   1027         setCameraState(SNAPSHOT_IN_PROGRESS);
   1028         UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
   1029                 UsageStatistics.ACTION_CAPTURE_DONE, "Photo");
   1030         return true;
   1031     }
   1032 
   1033     @Override
   1034     public void setFocusParameters() {
   1035         setCameraParameters(UPDATE_PARAM_PREFERENCE);
   1036     }
   1037 
   1038     private int getPreferredCameraId(ComboPreferences preferences) {
   1039         int intentCameraId = Util.getCameraFacingIntentExtras(mActivity);
   1040         if (intentCameraId != -1) {
   1041             // Testing purpose. Launch a specific camera through the intent
   1042             // extras.
   1043             return intentCameraId;
   1044         } else {
   1045             return CameraSettings.readPreferredCameraId(preferences);
   1046         }
   1047     }
   1048 
   1049     @Override
   1050     public void onFullScreenChanged(boolean full) {
   1051         mUI.onFullScreenChanged(full);
   1052         if (ApiHelper.HAS_SURFACE_TEXTURE) {
   1053             if (mActivity.mCameraScreenNail != null) {
   1054                 ((CameraScreenNail) mActivity.mCameraScreenNail).setFullScreen(full);
   1055             }
   1056             return;
   1057         }
   1058     }
   1059 
   1060     private void updateSceneMode() {
   1061         // If scene mode is set, we cannot set flash mode, white balance, and
   1062         // focus mode, instead, we read it from driver
   1063         if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
   1064             overrideCameraSettings(mParameters.getFlashMode(),
   1065                     mParameters.getWhiteBalance(), mParameters.getFocusMode());
   1066         } else {
   1067             overrideCameraSettings(null, null, null);
   1068         }
   1069     }
   1070 
   1071     private void overrideCameraSettings(final String flashMode,
   1072             final String whiteBalance, final String focusMode) {
   1073         mUI.overrideSettings(
   1074                 CameraSettings.KEY_FLASH_MODE, flashMode,
   1075                 CameraSettings.KEY_WHITE_BALANCE, whiteBalance,
   1076                 CameraSettings.KEY_FOCUS_MODE, focusMode);
   1077     }
   1078 
   1079     private void loadCameraPreferences() {
   1080         CameraSettings settings = new CameraSettings(mActivity, mInitialParams,
   1081                 mCameraId, CameraHolder.instance().getCameraInfo());
   1082         mPreferenceGroup = settings.getPreferenceGroup(R.xml.camera_preferences);
   1083     }
   1084 
   1085     @Override
   1086     public void onOrientationChanged(int orientation) {
   1087         // We keep the last known orientation. So if the user first orient
   1088         // the camera then point the camera to floor or sky, we still have
   1089         // the correct orientation.
   1090         if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) return;
   1091         mOrientation = Util.roundOrientation(orientation, mOrientation);
   1092 
   1093         // Show the toast after getting the first orientation changed.
   1094         if (mHandler.hasMessages(SHOW_TAP_TO_FOCUS_TOAST)) {
   1095             mHandler.removeMessages(SHOW_TAP_TO_FOCUS_TOAST);
   1096             showTapToFocusToast();
   1097         }
   1098     }
   1099 
   1100     @Override
   1101     public void onStop() {
   1102         if (mMediaProviderClient != null) {
   1103             mMediaProviderClient.release();
   1104             mMediaProviderClient = null;
   1105         }
   1106     }
   1107 
   1108     @Override
   1109     public void onCaptureCancelled() {
   1110         mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
   1111         mActivity.finish();
   1112     }
   1113 
   1114     @Override
   1115     public void onCaptureRetake() {
   1116         if (mPaused)
   1117             return;
   1118         mUI.hidePostCaptureAlert();
   1119         setupPreview();
   1120     }
   1121 
   1122     @Override
   1123     public void onCaptureDone() {
   1124         if (mPaused) {
   1125             return;
   1126         }
   1127 
   1128         byte[] data = mJpegImageData;
   1129 
   1130         if (mCropValue == null) {
   1131             // First handle the no crop case -- just return the value.  If the
   1132             // caller specifies a "save uri" then write the data to its
   1133             // stream. Otherwise, pass back a scaled down version of the bitmap
   1134             // directly in the extras.
   1135             if (mSaveUri != null) {
   1136                 OutputStream outputStream = null;
   1137                 try {
   1138                     outputStream = mContentResolver.openOutputStream(mSaveUri);
   1139                     outputStream.write(data);
   1140                     outputStream.close();
   1141 
   1142                     mActivity.setResultEx(Activity.RESULT_OK);
   1143                     mActivity.finish();
   1144                 } catch (IOException ex) {
   1145                     // ignore exception
   1146                 } finally {
   1147                     Util.closeSilently(outputStream);
   1148                 }
   1149             } else {
   1150                 ExifInterface exif = Exif.getExif(data);
   1151                 int orientation = Exif.getOrientation(exif);
   1152                 Bitmap bitmap = Util.makeBitmap(data, 50 * 1024);
   1153                 bitmap = Util.rotate(bitmap, orientation);
   1154                 mActivity.setResultEx(Activity.RESULT_OK,
   1155                         new Intent("inline-data").putExtra("data", bitmap));
   1156                 mActivity.finish();
   1157             }
   1158         } else {
   1159             // Save the image to a temp file and invoke the cropper
   1160             Uri tempUri = null;
   1161             FileOutputStream tempStream = null;
   1162             try {
   1163                 File path = mActivity.getFileStreamPath(sTempCropFilename);
   1164                 path.delete();
   1165                 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
   1166                 tempStream.write(data);
   1167                 tempStream.close();
   1168                 tempUri = Uri.fromFile(path);
   1169             } catch (FileNotFoundException ex) {
   1170                 mActivity.setResultEx(Activity.RESULT_CANCELED);
   1171                 mActivity.finish();
   1172                 return;
   1173             } catch (IOException ex) {
   1174                 mActivity.setResultEx(Activity.RESULT_CANCELED);
   1175                 mActivity.finish();
   1176                 return;
   1177             } finally {
   1178                 Util.closeSilently(tempStream);
   1179             }
   1180 
   1181             Bundle newExtras = new Bundle();
   1182             if (mCropValue.equals("circle")) {
   1183                 newExtras.putString("circleCrop", "true");
   1184             }
   1185             if (mSaveUri != null) {
   1186                 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
   1187             } else {
   1188                 newExtras.putBoolean(CropExtras.KEY_RETURN_DATA, true);
   1189             }
   1190             if (mActivity.isSecureCamera()) {
   1191                 newExtras.putBoolean(CropExtras.KEY_SHOW_WHEN_LOCKED, true);
   1192             }
   1193 
   1194             Intent cropIntent = new Intent(FilterShowActivity.CROP_ACTION);
   1195 
   1196             cropIntent.setData(tempUri);
   1197             cropIntent.putExtras(newExtras);
   1198 
   1199             mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
   1200         }
   1201     }
   1202 
   1203     @Override
   1204     public void onShutterButtonFocus(boolean pressed) {
   1205         if (mPaused || mUI.collapseCameraControls()
   1206                 || (mCameraState == SNAPSHOT_IN_PROGRESS)
   1207                 || (mCameraState == PREVIEW_STOPPED)) return;
   1208 
   1209         // Do not do focus if there is not enough storage.
   1210         if (pressed && !canTakePicture()) return;
   1211 
   1212         if (pressed) {
   1213             mFocusManager.onShutterDown();
   1214         } else {
   1215             // for countdown mode, we need to postpone the shutter release
   1216             // i.e. lock the focus during countdown.
   1217             if (!mUI.isCountingDown()) {
   1218                 mFocusManager.onShutterUp();
   1219             }
   1220         }
   1221     }
   1222 
   1223     @Override
   1224     public void onShutterButtonClick() {
   1225         if (mPaused || mUI.collapseCameraControls()
   1226                 || (mCameraState == SWITCHING_CAMERA)
   1227                 || (mCameraState == PREVIEW_STOPPED)) return;
   1228 
   1229         // Do not take the picture if there is not enough storage.
   1230         if (mActivity.getStorageSpace() <= Storage.LOW_STORAGE_THRESHOLD) {
   1231             Log.i(TAG, "Not enough space or storage not ready. remaining="
   1232                     + mActivity.getStorageSpace());
   1233             return;
   1234         }
   1235         Log.v(TAG, "onShutterButtonClick: mCameraState=" + mCameraState);
   1236 
   1237         if (mSceneMode == Util.SCENE_MODE_HDR) {
   1238             mActivity.hideSwitcher();
   1239             mActivity.setSwipingEnabled(false);
   1240         }
   1241         // If the user wants to do a snapshot while the previous one is still
   1242         // in progress, remember the fact and do it after we finish the previous
   1243         // one and re-start the preview. Snapshot in progress also includes the
   1244         // state that autofocus is focusing and a picture will be taken when
   1245         // focus callback arrives.
   1246         if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)
   1247                 && !mIsImageCaptureIntent) {
   1248             mSnapshotOnIdle = true;
   1249             return;
   1250         }
   1251 
   1252         String timer = mPreferences.getString(
   1253                 CameraSettings.KEY_TIMER,
   1254                 mActivity.getString(R.string.pref_camera_timer_default));
   1255         boolean playSound = mPreferences.getString(CameraSettings.KEY_TIMER_SOUND_EFFECTS,
   1256                 mActivity.getString(R.string.pref_camera_timer_sound_default))
   1257                 .equals(mActivity.getString(R.string.setting_on_value));
   1258 
   1259         int seconds = Integer.parseInt(timer);
   1260         // When shutter button is pressed, check whether the previous countdown is
   1261         // finished. If not, cancel the previous countdown and start a new one.
   1262         if (mUI.isCountingDown()) {
   1263             mUI.cancelCountDown();
   1264         }
   1265         if (seconds > 0) {
   1266             mUI.startCountDown(seconds, playSound);
   1267         } else {
   1268            mSnapshotOnIdle = false;
   1269            mFocusManager.doSnap();
   1270         }
   1271     }
   1272 
   1273     @Override
   1274     public void installIntentFilter() {
   1275     }
   1276 
   1277     @Override
   1278     public boolean updateStorageHintOnResume() {
   1279         return mFirstTimeInitialized;
   1280     }
   1281 
   1282     @Override
   1283     public void updateCameraAppView() {
   1284     }
   1285 
   1286     @Override
   1287     public void onResumeBeforeSuper() {
   1288         mPaused = false;
   1289     }
   1290 
   1291     @Override
   1292     public void onResumeAfterSuper() {
   1293         if (mOpenCameraFail || mCameraDisabled) return;
   1294 
   1295         mJpegPictureCallbackTime = 0;
   1296         mZoomValue = 0;
   1297 
   1298         // Start the preview if it is not started.
   1299         if (mCameraState == PREVIEW_STOPPED && mCameraStartUpThread == null) {
   1300             resetExposureCompensation();
   1301             mCameraStartUpThread = new CameraStartUpThread();
   1302             mCameraStartUpThread.start();
   1303         }
   1304 
   1305         // If first time initialization is not finished, put it in the
   1306         // message queue.
   1307         if (!mFirstTimeInitialized) {
   1308             mHandler.sendEmptyMessage(FIRST_TIME_INIT);
   1309         } else {
   1310             initializeSecondTime();
   1311         }
   1312         keepScreenOnAwhile();
   1313 
   1314         // Dismiss open menu if exists.
   1315         PopupManager.getInstance(mActivity).notifyShowPopup(null);
   1316         UsageStatistics.onContentViewChanged(
   1317                 UsageStatistics.COMPONENT_CAMERA, "PhotoModule");
   1318 
   1319         Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
   1320         if (gsensor != null) {
   1321             mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL);
   1322         }
   1323 
   1324         Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
   1325         if (msensor != null) {
   1326             mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL);
   1327         }
   1328     }
   1329 
   1330     void waitCameraStartUpThread() {
   1331         try {
   1332             if (mCameraStartUpThread != null) {
   1333                 mCameraStartUpThread.cancel();
   1334                 mCameraStartUpThread.join();
   1335                 mCameraStartUpThread = null;
   1336                 setCameraState(IDLE);
   1337             }
   1338         } catch (InterruptedException e) {
   1339             // ignore
   1340         }
   1341     }
   1342 
   1343     @Override
   1344     public void onPauseBeforeSuper() {
   1345         mPaused = true;
   1346         Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
   1347         if (gsensor != null) {
   1348             mSensorManager.unregisterListener(this, gsensor);
   1349         }
   1350 
   1351         Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
   1352         if (msensor != null) {
   1353             mSensorManager.unregisterListener(this, msensor);
   1354         }
   1355     }
   1356 
   1357     @Override
   1358     public void onPauseAfterSuper() {
   1359         // Wait the camera start up thread to finish.
   1360         waitCameraStartUpThread();
   1361 
   1362         // When camera is started from secure lock screen for the first time
   1363         // after screen on, the activity gets onCreate->onResume->onPause->onResume.
   1364         // To reduce the latency, keep the camera for a short time so it does
   1365         // not need to be opened again.
   1366         if (mCameraDevice != null && mActivity.isSecureCamera()
   1367                 && ActivityBase.isFirstStartAfterScreenOn()) {
   1368             ActivityBase.resetFirstStartAfterScreenOn();
   1369             CameraHolder.instance().keep(KEEP_CAMERA_TIMEOUT);
   1370         }
   1371         // Reset the focus first. Camera CTS does not guarantee that
   1372         // cancelAutoFocus is allowed after preview stops.
   1373         if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
   1374             mCameraDevice.cancelAutoFocus();
   1375         }
   1376         stopPreview();
   1377         // Release surface texture.
   1378         ((CameraScreenNail) mActivity.mCameraScreenNail).releaseSurfaceTexture();
   1379 
   1380         mNamedImages = null;
   1381 
   1382         if (mLocationManager != null) mLocationManager.recordLocation(false);
   1383 
   1384         // If we are in an image capture intent and has taken
   1385         // a picture, we just clear it in onPause.
   1386         mJpegImageData = null;
   1387 
   1388         // Remove the messages in the event queue.
   1389         mHandler.removeMessages(SETUP_PREVIEW);
   1390         mHandler.removeMessages(FIRST_TIME_INIT);
   1391         mHandler.removeMessages(CHECK_DISPLAY_ROTATION);
   1392         mHandler.removeMessages(SWITCH_CAMERA);
   1393         mHandler.removeMessages(SWITCH_CAMERA_START_ANIMATION);
   1394         mHandler.removeMessages(CAMERA_OPEN_DONE);
   1395         mHandler.removeMessages(START_PREVIEW_DONE);
   1396         mHandler.removeMessages(OPEN_CAMERA_FAIL);
   1397         mHandler.removeMessages(CAMERA_DISABLED);
   1398 
   1399         closeCamera();
   1400 
   1401         resetScreenOn();
   1402         mUI.onPause();
   1403 
   1404         mPendingSwitchCameraId = -1;
   1405         if (mFocusManager != null) mFocusManager.removeMessages();
   1406         MediaSaveService s = mActivity.getMediaSaveService();
   1407         if (s != null) {
   1408             s.setListener(null);
   1409         }
   1410     }
   1411 
   1412     /**
   1413      * The focus manager is the first UI related element to get initialized,
   1414      * and it requires the RenderOverlay, so initialize it here
   1415      */
   1416     private void initializeFocusManager() {
   1417         // Create FocusManager object. startPreview needs it.
   1418         // if mFocusManager not null, reuse it
   1419         // otherwise create a new instance
   1420         if (mFocusManager != null) {
   1421             mFocusManager.removeMessages();
   1422         } else {
   1423             CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
   1424             boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
   1425             String[] defaultFocusModes = mActivity.getResources().getStringArray(
   1426                     R.array.pref_camera_focusmode_default_array);
   1427             mFocusManager = new FocusOverlayManager(mPreferences, defaultFocusModes,
   1428                     mInitialParams, this, mirror,
   1429                     mActivity.getMainLooper(), mUI);
   1430         }
   1431     }
   1432 
   1433     @Override
   1434     public void onConfigurationChanged(Configuration newConfig) {
   1435         Log.v(TAG, "onConfigurationChanged");
   1436         setDisplayOrientation();
   1437     }
   1438 
   1439     @Override
   1440     public void onActivityResult(
   1441             int requestCode, int resultCode, Intent data) {
   1442         switch (requestCode) {
   1443             case REQUEST_CROP: {
   1444                 Intent intent = new Intent();
   1445                 if (data != null) {
   1446                     Bundle extras = data.getExtras();
   1447                     if (extras != null) {
   1448                         intent.putExtras(extras);
   1449                     }
   1450                 }
   1451                 mActivity.setResultEx(resultCode, intent);
   1452                 mActivity.finish();
   1453 
   1454                 File path = mActivity.getFileStreamPath(sTempCropFilename);
   1455                 path.delete();
   1456 
   1457                 break;
   1458             }
   1459         }
   1460     }
   1461 
   1462     private boolean canTakePicture() {
   1463         return isCameraIdle() && (mActivity.getStorageSpace() > Storage.LOW_STORAGE_THRESHOLD);
   1464     }
   1465 
   1466     @Override
   1467     public void autoFocus() {
   1468         mFocusStartTime = System.currentTimeMillis();
   1469         mCameraDevice.autoFocus(mAutoFocusCallback);
   1470         setCameraState(FOCUSING);
   1471     }
   1472 
   1473     @Override
   1474     public void cancelAutoFocus() {
   1475         mCameraDevice.cancelAutoFocus();
   1476         setCameraState(IDLE);
   1477         setCameraParameters(UPDATE_PARAM_PREFERENCE);
   1478     }
   1479 
   1480     // Preview area is touched. Handle touch focus.
   1481     @Override
   1482     public void onSingleTapUp(View view, int x, int y) {
   1483         if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
   1484                 || mCameraState == SNAPSHOT_IN_PROGRESS
   1485                 || mCameraState == SWITCHING_CAMERA
   1486                 || mCameraState == PREVIEW_STOPPED) {
   1487             return;
   1488         }
   1489 
   1490         // Do not trigger touch focus if popup window is opened.
   1491         if (mUI.removeTopLevelPopup()) return;
   1492 
   1493         // Check if metering area or focus area is supported.
   1494         if (!mFocusAreaSupported && !mMeteringAreaSupported) return;
   1495         mFocusManager.onSingleTapUp(x, y);
   1496     }
   1497 
   1498     @Override
   1499     public boolean onBackPressed() {
   1500         return mUI.onBackPressed();
   1501     }
   1502 
   1503     @Override
   1504     public boolean onKeyDown(int keyCode, KeyEvent event) {
   1505         switch (keyCode) {
   1506         case KeyEvent.KEYCODE_VOLUME_UP:
   1507         case KeyEvent.KEYCODE_VOLUME_DOWN:
   1508         case KeyEvent.KEYCODE_FOCUS:
   1509             if (mActivity.isInCameraApp() && mFirstTimeInitialized) {
   1510                 if (event.getRepeatCount() == 0) {
   1511                     onShutterButtonFocus(true);
   1512                 }
   1513                 return true;
   1514             }
   1515             return false;
   1516         case KeyEvent.KEYCODE_CAMERA:
   1517             if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
   1518                 onShutterButtonClick();
   1519             }
   1520             return true;
   1521         case KeyEvent.KEYCODE_DPAD_CENTER:
   1522             // If we get a dpad center event without any focused view, move
   1523             // the focus to the shutter button and press it.
   1524             if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
   1525                 // Start auto-focus immediately to reduce shutter lag. After
   1526                 // the shutter button gets the focus, onShutterButtonFocus()
   1527                 // will be called again but it is fine.
   1528                 if (mUI.removeTopLevelPopup()) return true;
   1529                 onShutterButtonFocus(true);
   1530                 mUI.pressShutterButton();
   1531             }
   1532             return true;
   1533         }
   1534         return false;
   1535     }
   1536 
   1537     @Override
   1538     public boolean onKeyUp(int keyCode, KeyEvent event) {
   1539         switch (keyCode) {
   1540         case KeyEvent.KEYCODE_VOLUME_UP:
   1541         case KeyEvent.KEYCODE_VOLUME_DOWN:
   1542             if (mActivity.isInCameraApp() && mFirstTimeInitialized) {
   1543                 onShutterButtonClick();
   1544                 return true;
   1545             }
   1546             return false;
   1547         case KeyEvent.KEYCODE_FOCUS:
   1548             if (mFirstTimeInitialized) {
   1549                 onShutterButtonFocus(false);
   1550             }
   1551             return true;
   1552         }
   1553         return false;
   1554     }
   1555 
   1556     @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
   1557     private void closeCamera() {
   1558         if (mCameraDevice != null) {
   1559             mCameraDevice.setZoomChangeListener(null);
   1560             if(ApiHelper.HAS_FACE_DETECTION) {
   1561                 mCameraDevice.setFaceDetectionListener(null);
   1562             }
   1563             mCameraDevice.setErrorCallback(null);
   1564             CameraHolder.instance().release();
   1565             mFaceDetectionStarted = false;
   1566             mCameraDevice = null;
   1567             setCameraState(PREVIEW_STOPPED);
   1568             mFocusManager.onCameraReleased();
   1569         }
   1570     }
   1571 
   1572     private void setDisplayOrientation() {
   1573         mDisplayRotation = Util.getDisplayRotation(mActivity);
   1574         mDisplayOrientation = Util.getDisplayOrientation(mDisplayRotation, mCameraId);
   1575         mCameraDisplayOrientation = Util.getDisplayOrientation(0, mCameraId);
   1576         mUI.setDisplayOrientation(mDisplayOrientation);
   1577         if (mFocusManager != null) {
   1578             mFocusManager.setDisplayOrientation(mDisplayOrientation);
   1579         }
   1580         // GLRoot also uses the DisplayRotation, and needs to be told to layout to update
   1581         mActivity.getGLRoot().requestLayoutContentPane();
   1582     }
   1583 
   1584     // Only called by UI thread.
   1585     private void setupPreview() {
   1586         mFocusManager.resetTouchFocus();
   1587         startPreview();
   1588         setCameraState(IDLE);
   1589         startFaceDetection();
   1590     }
   1591 
   1592     // This can be called by UI Thread or CameraStartUpThread. So this should
   1593     // not modify the views.
   1594     private void startPreview() {
   1595         mCameraDevice.setErrorCallback(mErrorCallback);
   1596 
   1597         // ICS camera frameworks has a bug. Face detection state is not cleared
   1598         // after taking a picture. Stop the preview to work around it. The bug
   1599         // was fixed in JB.
   1600         if (mCameraState != PREVIEW_STOPPED) stopPreview();
   1601 
   1602         setDisplayOrientation();
   1603 
   1604         if (!mSnapshotOnIdle) {
   1605             // If the focus mode is continuous autofocus, call cancelAutoFocus to
   1606             // resume it because it may have been paused by autoFocus call.
   1607             if (Util.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusManager.getFocusMode())) {
   1608                 mCameraDevice.cancelAutoFocus();
   1609             }
   1610             mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
   1611         }
   1612         setCameraParameters(UPDATE_PARAM_ALL);
   1613 
   1614         if (ApiHelper.HAS_SURFACE_TEXTURE) {
   1615             CameraScreenNail screenNail = (CameraScreenNail) mActivity.mCameraScreenNail;
   1616             if (mUI.getSurfaceTexture() == null) {
   1617                 Size size = mParameters.getPreviewSize();
   1618                 if (mCameraDisplayOrientation % 180 == 0) {
   1619                     screenNail.setSize(size.width, size.height);
   1620                 } else {
   1621                     screenNail.setSize(size.height, size.width);
   1622                 }
   1623                 screenNail.enableAspectRatioClamping();
   1624                 mActivity.notifyScreenNailChanged();
   1625                 screenNail.acquireSurfaceTexture();
   1626                 CameraStartUpThread t = mCameraStartUpThread;
   1627                 if (t != null && t.isCanceled()) {
   1628                     return; // Exiting, so no need to get the surface texture.
   1629                 }
   1630                 mUI.setSurfaceTexture(screenNail.getSurfaceTexture());
   1631             } else {
   1632                 updatePreviewSize(screenNail);
   1633             }
   1634             mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);
   1635             Object st = mUI.getSurfaceTexture();
   1636             if (st != null) {
   1637                 mCameraDevice.setPreviewTextureAsync((SurfaceTexture) st);
   1638             }
   1639         } else {
   1640             mCameraDevice.setDisplayOrientation(mDisplayOrientation);
   1641             mCameraDevice.setPreviewDisplayAsync(mUI.getSurfaceHolder());
   1642         }
   1643 
   1644         Log.v(TAG, "startPreview");
   1645         mCameraDevice.startPreviewAsync();
   1646 
   1647         mFocusManager.onPreviewStarted();
   1648 
   1649         if (mSnapshotOnIdle) {
   1650             mHandler.post(mDoSnapRunnable);
   1651         }
   1652     }
   1653 
   1654     private void updatePreviewSize(CameraScreenNail snail) {
   1655         Size size = mParameters.getPreviewSize();
   1656         int w = size.width;
   1657         int h = size.height;
   1658         if (mCameraDisplayOrientation % 180 != 0) {
   1659             w = size.height;
   1660             h = size.width;
   1661         }
   1662         if (snail.getWidth() != w || snail.getHeight() != h) {
   1663             snail.setSize(w, h);
   1664         }
   1665         snail.enableAspectRatioClamping();
   1666         mActivity.notifyScreenNailChanged();
   1667     }
   1668 
   1669     @Override
   1670     public void stopPreview() {
   1671         if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
   1672             Log.v(TAG, "stopPreview");
   1673             mCameraDevice.stopPreview();
   1674             mFaceDetectionStarted = false;
   1675         }
   1676         setCameraState(PREVIEW_STOPPED);
   1677         if (mFocusManager != null) mFocusManager.onPreviewStopped();
   1678     }
   1679 
   1680     @SuppressWarnings("deprecation")
   1681     private void updateCameraParametersInitialize() {
   1682         // Reset preview frame rate to the maximum because it may be lowered by
   1683         // video camera application.
   1684         List<Integer> frameRates = mParameters.getSupportedPreviewFrameRates();
   1685         if (frameRates != null) {
   1686             Integer max = Collections.max(frameRates);
   1687             mParameters.setPreviewFrameRate(max);
   1688         }
   1689 
   1690         mParameters.set(Util.RECORDING_HINT, Util.FALSE);
   1691 
   1692         // Disable video stabilization. Convenience methods not available in API
   1693         // level <= 14
   1694         String vstabSupported = mParameters.get("video-stabilization-supported");
   1695         if ("true".equals(vstabSupported)) {
   1696             mParameters.set("video-stabilization", "false");
   1697         }
   1698     }
   1699 
   1700     private void updateCameraParametersZoom() {
   1701         // Set zoom.
   1702         if (mParameters.isZoomSupported()) {
   1703             mParameters.setZoom(mZoomValue);
   1704         }
   1705     }
   1706 
   1707     @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
   1708     private void setAutoExposureLockIfSupported() {
   1709         if (mAeLockSupported) {
   1710             mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock());
   1711         }
   1712     }
   1713 
   1714     @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
   1715     private void setAutoWhiteBalanceLockIfSupported() {
   1716         if (mAwbLockSupported) {
   1717             mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
   1718         }
   1719     }
   1720 
   1721     @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
   1722     private void setFocusAreasIfSupported() {
   1723         if (mFocusAreaSupported) {
   1724             mParameters.setFocusAreas(mFocusManager.getFocusAreas());
   1725         }
   1726     }
   1727 
   1728     @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
   1729     private void setMeteringAreasIfSupported() {
   1730         if (mMeteringAreaSupported) {
   1731             // Use the same area for focus and metering.
   1732             mParameters.setMeteringAreas(mFocusManager.getMeteringAreas());
   1733         }
   1734     }
   1735 
   1736     private void updateCameraParametersPreference() {
   1737         setAutoExposureLockIfSupported();
   1738         setAutoWhiteBalanceLockIfSupported();
   1739         setFocusAreasIfSupported();
   1740         setMeteringAreasIfSupported();
   1741 
   1742         // Set picture size.
   1743         String pictureSize = mPreferences.getString(
   1744                 CameraSettings.KEY_PICTURE_SIZE, null);
   1745         if (pictureSize == null) {
   1746             CameraSettings.initialCameraPictureSize(mActivity, mParameters);
   1747         } else {
   1748             List<Size> supported = mParameters.getSupportedPictureSizes();
   1749             CameraSettings.setCameraPictureSize(
   1750                     pictureSize, supported, mParameters);
   1751         }
   1752         Size size = mParameters.getPictureSize();
   1753 
   1754         // Set a preview size that is closest to the viewfinder height and has
   1755         // the right aspect ratio.
   1756         List<Size> sizes = mParameters.getSupportedPreviewSizes();
   1757         Size optimalSize = Util.getOptimalPreviewSize(mActivity, sizes,
   1758                 (double) size.width / size.height);
   1759         Size original = mParameters.getPreviewSize();
   1760         if (!original.equals(optimalSize)) {
   1761             mParameters.setPreviewSize(optimalSize.width, optimalSize.height);
   1762             // Zoom related settings will be changed for different preview
   1763             // sizes, so set and read the parameters to get latest values
   1764             if (mHandler.getLooper() == Looper.myLooper()) {
   1765                 // On UI thread only, not when camera starts up
   1766                 setupPreview();
   1767             } else {
   1768                 mCameraDevice.setParameters(mParameters);
   1769             }
   1770             mParameters = mCameraDevice.getParameters();
   1771         }
   1772         Log.v(TAG, "Preview size is " + optimalSize.width + "x" + optimalSize.height);
   1773 
   1774         // Since changing scene mode may change supported values, set scene mode
   1775         // first. HDR is a scene mode. To promote it in UI, it is stored in a
   1776         // separate preference.
   1777         String hdr = mPreferences.getString(CameraSettings.KEY_CAMERA_HDR,
   1778                 mActivity.getString(R.string.pref_camera_hdr_default));
   1779         if (mActivity.getString(R.string.setting_on_value).equals(hdr)) {
   1780             mSceneMode = Util.SCENE_MODE_HDR;
   1781         } else {
   1782             mSceneMode = mPreferences.getString(
   1783                 CameraSettings.KEY_SCENE_MODE,
   1784                 mActivity.getString(R.string.pref_camera_scenemode_default));
   1785         }
   1786         if (Util.isSupported(mSceneMode, mParameters.getSupportedSceneModes())) {
   1787             if (!mParameters.getSceneMode().equals(mSceneMode)) {
   1788                 mParameters.setSceneMode(mSceneMode);
   1789 
   1790                 // Setting scene mode will change the settings of flash mode,
   1791                 // white balance, and focus mode. Here we read back the
   1792                 // parameters, so we can know those settings.
   1793                 mCameraDevice.setParameters(mParameters);
   1794                 mParameters = mCameraDevice.getParameters();
   1795             }
   1796         } else {
   1797             mSceneMode = mParameters.getSceneMode();
   1798             if (mSceneMode == null) {
   1799                 mSceneMode = Parameters.SCENE_MODE_AUTO;
   1800             }
   1801         }
   1802 
   1803         // Set JPEG quality.
   1804         int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
   1805                 CameraProfile.QUALITY_HIGH);
   1806         mParameters.setJpegQuality(jpegQuality);
   1807 
   1808         // For the following settings, we need to check if the settings are
   1809         // still supported by latest driver, if not, ignore the settings.
   1810 
   1811         // Set exposure compensation
   1812         int value = CameraSettings.readExposure(mPreferences);
   1813         int max = mParameters.getMaxExposureCompensation();
   1814         int min = mParameters.getMinExposureCompensation();
   1815         if (value >= min && value <= max) {
   1816             mParameters.setExposureCompensation(value);
   1817         } else {
   1818             Log.w(TAG, "invalid exposure range: " + value);
   1819         }
   1820 
   1821         if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
   1822             // Set flash mode.
   1823             String flashMode = mPreferences.getString(
   1824                     CameraSettings.KEY_FLASH_MODE,
   1825                     mActivity.getString(R.string.pref_camera_flashmode_default));
   1826             List<String> supportedFlash = mParameters.getSupportedFlashModes();
   1827             if (Util.isSupported(flashMode, supportedFlash)) {
   1828                 mParameters.setFlashMode(flashMode);
   1829             } else {
   1830                 flashMode = mParameters.getFlashMode();
   1831                 if (flashMode == null) {
   1832                     flashMode = mActivity.getString(
   1833                             R.string.pref_camera_flashmode_no_flash);
   1834                 }
   1835             }
   1836 
   1837             // Set white balance parameter.
   1838             String whiteBalance = mPreferences.getString(
   1839                     CameraSettings.KEY_WHITE_BALANCE,
   1840                     mActivity.getString(R.string.pref_camera_whitebalance_default));
   1841             if (Util.isSupported(whiteBalance,
   1842                     mParameters.getSupportedWhiteBalance())) {
   1843                 mParameters.setWhiteBalance(whiteBalance);
   1844             } else {
   1845                 whiteBalance = mParameters.getWhiteBalance();
   1846                 if (whiteBalance == null) {
   1847                     whiteBalance = Parameters.WHITE_BALANCE_AUTO;
   1848                 }
   1849             }
   1850 
   1851             // Set focus mode.
   1852             mFocusManager.overrideFocusMode(null);
   1853             mParameters.setFocusMode(mFocusManager.getFocusMode());
   1854         } else {
   1855             mFocusManager.overrideFocusMode(mParameters.getFocusMode());
   1856         }
   1857 
   1858         if (mContinousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
   1859             updateAutoFocusMoveCallback();
   1860         }
   1861     }
   1862 
   1863     @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
   1864     private void updateAutoFocusMoveCallback() {
   1865         if (mParameters.getFocusMode().equals(Util.FOCUS_MODE_CONTINUOUS_PICTURE)) {
   1866             mCameraDevice.setAutoFocusMoveCallback(
   1867                 (AutoFocusMoveCallback) mAutoFocusMoveCallback);
   1868         } else {
   1869             mCameraDevice.setAutoFocusMoveCallback(null);
   1870         }
   1871     }
   1872 
   1873     // We separate the parameters into several subsets, so we can update only
   1874     // the subsets actually need updating. The PREFERENCE set needs extra
   1875     // locking because the preference can be changed from GLThread as well.
   1876     private void setCameraParameters(int updateSet) {
   1877         if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
   1878             updateCameraParametersInitialize();
   1879         }
   1880 
   1881         if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
   1882             updateCameraParametersZoom();
   1883         }
   1884 
   1885         if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
   1886             updateCameraParametersPreference();
   1887         }
   1888 
   1889         mCameraDevice.setParameters(mParameters);
   1890     }
   1891 
   1892     // If the Camera is idle, update the parameters immediately, otherwise
   1893     // accumulate them in mUpdateSet and update later.
   1894     private void setCameraParametersWhenIdle(int additionalUpdateSet) {
   1895         mUpdateSet |= additionalUpdateSet;
   1896         if (mCameraDevice == null) {
   1897             // We will update all the parameters when we open the device, so
   1898             // we don't need to do anything now.
   1899             mUpdateSet = 0;
   1900             return;
   1901         } else if (isCameraIdle()) {
   1902             setCameraParameters(mUpdateSet);
   1903             updateSceneMode();
   1904             mUpdateSet = 0;
   1905         } else {
   1906             if (!mHandler.hasMessages(SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
   1907                 mHandler.sendEmptyMessageDelayed(
   1908                         SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
   1909             }
   1910         }
   1911     }
   1912 
   1913     public boolean isCameraIdle() {
   1914         return (mCameraState == IDLE) ||
   1915                 (mCameraState == PREVIEW_STOPPED) ||
   1916                 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
   1917                         && (mCameraState != SWITCHING_CAMERA));
   1918     }
   1919 
   1920     public boolean isImageCaptureIntent() {
   1921         String action = mActivity.getIntent().getAction();
   1922         return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
   1923                 || ActivityBase.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
   1924     }
   1925 
   1926     private void setupCaptureParams() {
   1927         Bundle myExtras = mActivity.getIntent().getExtras();
   1928         if (myExtras != null) {
   1929             mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
   1930             mCropValue = myExtras.getString("crop");
   1931         }
   1932     }
   1933 
   1934     @Override
   1935     public void onSharedPreferenceChanged() {
   1936         // ignore the events after "onPause()"
   1937         if (mPaused) return;
   1938 
   1939         boolean recordLocation = RecordLocationPreference.get(
   1940                 mPreferences, mContentResolver);
   1941         mLocationManager.recordLocation(recordLocation);
   1942 
   1943         setCameraParametersWhenIdle(UPDATE_PARAM_PREFERENCE);
   1944         mUI.updateOnScreenIndicators(mParameters, mPreferenceGroup, mPreferences);
   1945     }
   1946 
   1947     @Override
   1948     public void onCameraPickerClicked(int cameraId) {
   1949         if (mPaused || mPendingSwitchCameraId != -1) return;
   1950 
   1951         mPendingSwitchCameraId = cameraId;
   1952         if (ApiHelper.HAS_SURFACE_TEXTURE) {
   1953             Log.v(TAG, "Start to copy texture. cameraId=" + cameraId);
   1954             // We need to keep a preview frame for the animation before
   1955             // releasing the camera. This will trigger onPreviewTextureCopied.
   1956             ((CameraScreenNail) mActivity.mCameraScreenNail).copyTexture();
   1957             // Disable all camera controls.
   1958             setCameraState(SWITCHING_CAMERA);
   1959         } else {
   1960             switchCamera();
   1961         }
   1962     }
   1963 
   1964     // Preview texture has been copied. Now camera can be released and the
   1965     // animation can be started.
   1966     @Override
   1967     public void onPreviewTextureCopied() {
   1968         mHandler.sendEmptyMessage(SWITCH_CAMERA);
   1969     }
   1970 
   1971     @Override
   1972     public void onCaptureTextureCopied() {
   1973     }
   1974 
   1975     @Override
   1976     public void onUserInteraction() {
   1977         if (!mActivity.isFinishing()) keepScreenOnAwhile();
   1978     }
   1979 
   1980     private void resetScreenOn() {
   1981         mHandler.removeMessages(CLEAR_SCREEN_DELAY);
   1982         mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
   1983     }
   1984 
   1985     private void keepScreenOnAwhile() {
   1986         mHandler.removeMessages(CLEAR_SCREEN_DELAY);
   1987         mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
   1988         mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY);
   1989     }
   1990 
   1991     // TODO: Delete this function after old camera code is removed
   1992     @Override
   1993     public void onRestorePreferencesClicked() {
   1994     }
   1995 
   1996     @Override
   1997     public void onOverriddenPreferencesClicked() {
   1998         if (mPaused) return;
   1999         mUI.showPreferencesToast();
   2000     }
   2001 
   2002     private void showTapToFocusToast() {
   2003         // TODO: Use a toast?
   2004         new RotateTextToast(mActivity, R.string.tap_to_focus, 0).show();
   2005         // Clear the preference.
   2006         Editor editor = mPreferences.edit();
   2007         editor.putBoolean(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, false);
   2008         editor.apply();
   2009     }
   2010 
   2011     private void initializeCapabilities() {
   2012         mInitialParams = mCameraDevice.getParameters();
   2013         mFocusAreaSupported = Util.isFocusAreaSupported(mInitialParams);
   2014         mMeteringAreaSupported = Util.isMeteringAreaSupported(mInitialParams);
   2015         mAeLockSupported = Util.isAutoExposureLockSupported(mInitialParams);
   2016         mAwbLockSupported = Util.isAutoWhiteBalanceLockSupported(mInitialParams);
   2017         mContinousFocusSupported = mInitialParams.getSupportedFocusModes().contains(
   2018                 Util.FOCUS_MODE_CONTINUOUS_PICTURE);
   2019     }
   2020 
   2021     @Override
   2022     public void onCountDownFinished() {
   2023         mSnapshotOnIdle = false;
   2024         mFocusManager.doSnap();
   2025         mFocusManager.onShutterUp();
   2026     }
   2027 
   2028     @Override
   2029     public boolean needsSwitcher() {
   2030         return !mIsImageCaptureIntent;
   2031     }
   2032 
   2033     @Override
   2034     public boolean needsPieMenu() {
   2035         return true;
   2036     }
   2037 
   2038     @Override
   2039     public void onShowSwitcherPopup() {
   2040         mUI.onShowSwitcherPopup();
   2041     }
   2042 
   2043     @Override
   2044     public int onZoomChanged(int index) {
   2045         // Not useful to change zoom value when the activity is paused.
   2046         if (mPaused) return index;
   2047         mZoomValue = index;
   2048         if (mParameters == null || mCameraDevice == null) return index;
   2049         // Set zoom parameters asynchronously
   2050         mParameters.setZoom(mZoomValue);
   2051         mCameraDevice.setParameters(mParameters);
   2052         Parameters p = mCameraDevice.getParameters();
   2053         if (p != null) return p.getZoom();
   2054         return index;
   2055     }
   2056 
   2057     @Override
   2058     public int getCameraState() {
   2059         return mCameraState;
   2060     }
   2061 
   2062     @Override
   2063     public void onQueueStatus(boolean full) {
   2064         mUI.enableShutter(!full);
   2065     }
   2066 
   2067     @Override
   2068     public void onMediaSaveServiceConnected(MediaSaveService s) {
   2069         // We set the listener only when both service and shutterbutton
   2070         // are initialized.
   2071         if (mFirstTimeInitialized) {
   2072             s.setListener(this);
   2073         }
   2074     }
   2075 
   2076     @Override
   2077     public void onAccuracyChanged(Sensor sensor, int accuracy) {
   2078     }
   2079 
   2080     @Override
   2081     public void onSensorChanged(SensorEvent event) {
   2082         int type = event.sensor.getType();
   2083         float[] data;
   2084         if (type == Sensor.TYPE_ACCELEROMETER) {
   2085             data = mGData;
   2086         } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
   2087             data = mMData;
   2088         } else {
   2089             // we should not be here.
   2090             return;
   2091         }
   2092         for (int i = 0; i < 3 ; i++) {
   2093             data[i] = event.values[i];
   2094         }
   2095         float[] orientation = new float[3];
   2096         SensorManager.getRotationMatrix(mR, null, mGData, mMData);
   2097         SensorManager.getOrientation(mR, orientation);
   2098         mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
   2099         if (mHeading < 0) {
   2100             mHeading += 360;
   2101         }
   2102     }
   2103 }
   2104