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.content.ContentResolver;
     22 import android.content.Intent;
     23 import android.graphics.Bitmap;
     24 import android.graphics.BitmapFactory;
     25 import android.graphics.SurfaceTexture;
     26 import android.location.Location;
     27 import android.media.CameraProfile;
     28 import android.net.Uri;
     29 import android.os.AsyncTask;
     30 import android.os.Build;
     31 import android.os.Bundle;
     32 import android.os.Handler;
     33 import android.os.Looper;
     34 import android.os.Message;
     35 import android.os.MessageQueue;
     36 import android.os.SystemClock;
     37 import android.provider.MediaStore;
     38 import android.view.KeyEvent;
     39 import android.view.View;
     40 
     41 import com.android.camera.PhotoModule.NamedImages.NamedEntity;
     42 import com.android.camera.app.AppController;
     43 import com.android.camera.app.CameraAppUI;
     44 import com.android.camera.app.CameraProvider;
     45 import com.android.camera.app.MediaSaver;
     46 import com.android.camera.app.MemoryManager;
     47 import com.android.camera.app.MemoryManager.MemoryListener;
     48 import com.android.camera.app.MotionManager;
     49 import com.android.camera.debug.Log;
     50 import com.android.camera.exif.ExifInterface;
     51 import com.android.camera.exif.ExifTag;
     52 import com.android.camera.exif.Rational;
     53 import com.android.camera.hardware.HardwareSpec;
     54 import com.android.camera.hardware.HardwareSpecImpl;
     55 import com.android.camera.hardware.HeadingSensor;
     56 import com.android.camera.module.ModuleController;
     57 import com.android.camera.one.OneCamera;
     58 import com.android.camera.one.OneCameraAccessException;
     59 import com.android.camera.one.OneCameraException;
     60 import com.android.camera.one.OneCameraManager;
     61 import com.android.camera.one.OneCameraModule;
     62 import com.android.camera.remote.RemoteCameraModule;
     63 import com.android.camera.settings.CameraPictureSizesCacher;
     64 import com.android.camera.settings.Keys;
     65 import com.android.camera.settings.ResolutionUtil;
     66 import com.android.camera.settings.SettingsManager;
     67 import com.android.camera.stats.SessionStatsCollector;
     68 import com.android.camera.stats.UsageStatistics;
     69 import com.android.camera.ui.CountDownView;
     70 import com.android.camera.ui.TouchCoordinate;
     71 import com.android.camera.util.AndroidServices;
     72 import com.android.camera.util.ApiHelper;
     73 import com.android.camera.util.CameraUtil;
     74 import com.android.camera.util.GcamHelper;
     75 import com.android.camera.util.GservicesHelper;
     76 import com.android.camera.util.Size;
     77 import com.android.camera2.R;
     78 import com.android.ex.camera2.portability.CameraAgent;
     79 import com.android.ex.camera2.portability.CameraAgent.CameraAFCallback;
     80 import com.android.ex.camera2.portability.CameraAgent.CameraAFMoveCallback;
     81 import com.android.ex.camera2.portability.CameraAgent.CameraPictureCallback;
     82 import com.android.ex.camera2.portability.CameraAgent.CameraProxy;
     83 import com.android.ex.camera2.portability.CameraAgent.CameraShutterCallback;
     84 import com.android.ex.camera2.portability.CameraCapabilities;
     85 import com.android.ex.camera2.portability.CameraDeviceInfo.Characteristics;
     86 import com.android.ex.camera2.portability.CameraSettings;
     87 import com.google.common.logging.eventprotos;
     88 
     89 import java.io.ByteArrayOutputStream;
     90 import java.io.File;
     91 import java.io.FileNotFoundException;
     92 import java.io.FileOutputStream;
     93 import java.io.IOException;
     94 import java.io.OutputStream;
     95 import java.lang.ref.WeakReference;
     96 import java.util.ArrayList;
     97 import java.util.List;
     98 import java.util.Vector;
     99 
    100 
    101 public class PhotoModule
    102         extends CameraModule
    103         implements PhotoController,
    104         ModuleController,
    105         MemoryListener,
    106         FocusOverlayManager.Listener,
    107         SettingsManager.OnSettingChangedListener,
    108         RemoteCameraModule,
    109         CountDownView.OnCountDownStatusListener {
    110 
    111     private static final Log.Tag TAG = new Log.Tag("PhotoModule");
    112 
    113     // We number the request code from 1000 to avoid collision with Gallery.
    114     private static final int REQUEST_CROP = 1000;
    115 
    116     // Messages defined for the UI thread handler.
    117     private static final int MSG_FIRST_TIME_INIT = 1;
    118     private static final int MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE = 2;
    119 
    120     // The subset of parameters we need to update in setCameraParameters().
    121     private static final int UPDATE_PARAM_INITIALIZE = 1;
    122     private static final int UPDATE_PARAM_ZOOM = 2;
    123     private static final int UPDATE_PARAM_PREFERENCE = 4;
    124     private static final int UPDATE_PARAM_ALL = -1;
    125 
    126     private static final String DEBUG_IMAGE_PREFIX = "DEBUG_";
    127 
    128     private CameraActivity mActivity;
    129     private CameraProxy mCameraDevice;
    130     private int mCameraId;
    131     private CameraCapabilities mCameraCapabilities;
    132     private CameraSettings mCameraSettings;
    133     private HardwareSpec mHardwareSpec;
    134     private boolean mPaused;
    135 
    136     private PhotoUI mUI;
    137 
    138     // The activity is going to switch to the specified camera id. This is
    139     // needed because texture copy is done in GL thread. -1 means camera is not
    140     // switching.
    141     protected int mPendingSwitchCameraId = -1;
    142 
    143     // When setCameraParametersWhenIdle() is called, we accumulate the subsets
    144     // needed to be updated in mUpdateSet.
    145     private int mUpdateSet;
    146 
    147     private float mZoomValue; // The current zoom ratio.
    148     private int mTimerDuration;
    149     /** Set when a volume button is clicked to take photo */
    150     private boolean mVolumeButtonClickedFlag = false;
    151 
    152     private boolean mFocusAreaSupported;
    153     private boolean mMeteringAreaSupported;
    154     private boolean mAeLockSupported;
    155     private boolean mAwbLockSupported;
    156     private boolean mContinuousFocusSupported;
    157 
    158     private static final String sTempCropFilename = "crop-temp";
    159 
    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     private Uri mDebugUri;
    167 
    168     // We use a queue to generated names of the images to be used later
    169     // when the image is ready to be saved.
    170     private NamedImages mNamedImages;
    171 
    172     private final Runnable mDoSnapRunnable = new Runnable() {
    173         @Override
    174         public void run() {
    175             onShutterButtonClick();
    176         }
    177     };
    178 
    179     /**
    180      * An unpublished intent flag requesting to return as soon as capturing is
    181      * completed. TODO: consider publishing by moving into MediaStore.
    182      */
    183     private static final String EXTRA_QUICK_CAPTURE =
    184             "android.intent.extra.quickCapture";
    185 
    186     // The display rotation in degrees. This is only valid when mCameraState is
    187     // not PREVIEW_STOPPED.
    188     private int mDisplayRotation;
    189     // The value for UI components like indicators.
    190     private int mDisplayOrientation;
    191     // The value for cameradevice.CameraSettings.setPhotoRotationDegrees.
    192     private int mJpegRotation;
    193     // Indicates whether we are using front camera
    194     private boolean mMirror;
    195     private boolean mFirstTimeInitialized;
    196     private boolean mIsImageCaptureIntent;
    197 
    198     private int mCameraState = PREVIEW_STOPPED;
    199     private boolean mSnapshotOnIdle = false;
    200 
    201     private ContentResolver mContentResolver;
    202 
    203     private AppController mAppController;
    204     private OneCameraManager mOneCameraManager;
    205 
    206     private final PostViewPictureCallback mPostViewPictureCallback =
    207             new PostViewPictureCallback();
    208     private final RawPictureCallback mRawPictureCallback =
    209             new RawPictureCallback();
    210     private final AutoFocusCallback mAutoFocusCallback =
    211             new AutoFocusCallback();
    212     private final Object mAutoFocusMoveCallback =
    213             ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
    214                     ? new AutoFocusMoveCallback()
    215                     : null;
    216 
    217     private long mFocusStartTime;
    218     private long mShutterCallbackTime;
    219     private long mPostViewPictureCallbackTime;
    220     private long mRawPictureCallbackTime;
    221     private long mJpegPictureCallbackTime;
    222     private long mOnResumeTime;
    223     private byte[] mJpegImageData;
    224     /** Touch coordinate for shutter button press. */
    225     private TouchCoordinate mShutterTouchCoordinate;
    226 
    227 
    228     // These latency time are for the CameraLatency test.
    229     public long mAutoFocusTime;
    230     public long mShutterLag;
    231     public long mShutterToPictureDisplayedTime;
    232     public long mPictureDisplayedToJpegCallbackTime;
    233     public long mJpegCallbackFinishTime;
    234     public long mCaptureStartTime;
    235 
    236     // This handles everything about focus.
    237     private FocusOverlayManager mFocusManager;
    238 
    239     private final int mGcamModeIndex;
    240     private SoundPlayer mCountdownSoundPlayer;
    241 
    242     private CameraCapabilities.SceneMode mSceneMode;
    243 
    244     private final Handler mHandler = new MainHandler(this);
    245 
    246     private boolean mQuickCapture;
    247 
    248     /** Used to detect motion. We use this to release focus lock early. */
    249     private MotionManager mMotionManager;
    250 
    251     private HeadingSensor mHeadingSensor;
    252 
    253     /** True if all the parameters needed to start preview is ready. */
    254     private boolean mCameraPreviewParamsReady = false;
    255 
    256     private final MediaSaver.OnMediaSavedListener mOnMediaSavedListener =
    257             new MediaSaver.OnMediaSavedListener() {
    258 
    259                 @Override
    260                 public void onMediaSaved(Uri uri) {
    261                     if (uri != null) {
    262                         mActivity.notifyNewMedia(uri);
    263                     } else {
    264                         onError();
    265                     }
    266                 }
    267             };
    268 
    269     /**
    270      * Displays error dialog and allows use to enter feedback. Does not shut
    271      * down the app.
    272      */
    273     private void onError() {
    274         mAppController.getFatalErrorHandler().onMediaStorageFailure();
    275     }
    276 
    277     private boolean mShouldResizeTo16x9 = false;
    278 
    279     /**
    280      * We keep the flash setting before entering scene modes (HDR)
    281      * and restore it after HDR is off.
    282      */
    283     private String mFlashModeBeforeSceneMode;
    284 
    285     private void checkDisplayRotation() {
    286         // Need to just be a no-op for the quick resume-pause scenario.
    287         if (mPaused) {
    288             return;
    289         }
    290         // Set the display orientation if display rotation has changed.
    291         // Sometimes this happens when the device is held upside
    292         // down and camera app is opened. Rotation animation will
    293         // take some time and the rotation value we have got may be
    294         // wrong. Framework does not have a callback for this now.
    295         if (CameraUtil.getDisplayRotation() != mDisplayRotation) {
    296             setDisplayOrientation();
    297         }
    298         if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
    299             mHandler.postDelayed(new Runnable() {
    300                 @Override
    301                 public void run() {
    302                     checkDisplayRotation();
    303                 }
    304             }, 100);
    305         }
    306     }
    307 
    308     /**
    309      * This Handler is used to post message back onto the main thread of the
    310      * application
    311      */
    312     private static class MainHandler extends Handler {
    313         private final WeakReference<PhotoModule> mModule;
    314 
    315         public MainHandler(PhotoModule module) {
    316             super(Looper.getMainLooper());
    317             mModule = new WeakReference<PhotoModule>(module);
    318         }
    319 
    320         @Override
    321         public void handleMessage(Message msg) {
    322             PhotoModule module = mModule.get();
    323             if (module == null) {
    324                 return;
    325             }
    326             switch (msg.what) {
    327                 case MSG_FIRST_TIME_INIT: {
    328                     module.initializeFirstTime();
    329                     break;
    330                 }
    331 
    332                 case MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE: {
    333                     module.setCameraParametersWhenIdle(0);
    334                     break;
    335                 }
    336             }
    337         }
    338     }
    339 
    340     private void switchToGcamCapture() {
    341         if (mActivity != null && mGcamModeIndex != 0) {
    342             SettingsManager settingsManager = mActivity.getSettingsManager();
    343             settingsManager.set(SettingsManager.SCOPE_GLOBAL,
    344                                 Keys.KEY_CAMERA_HDR_PLUS, true);
    345 
    346             // Disable the HDR+ button to prevent callbacks from being
    347             // queued before the correct callback is attached to the button
    348             // in the new module.  The new module will set the enabled/disabled
    349             // of this button when the module's preferred camera becomes available.
    350             ButtonManager buttonManager = mActivity.getButtonManager();
    351 
    352             buttonManager.disableButtonClick(ButtonManager.BUTTON_HDR_PLUS);
    353 
    354             mAppController.getCameraAppUI().freezeScreenUntilPreviewReady();
    355 
    356             // Do not post this to avoid this module switch getting interleaved with
    357             // other button callbacks.
    358             mActivity.onModeSelected(mGcamModeIndex);
    359 
    360             buttonManager.enableButtonClick(ButtonManager.BUTTON_HDR_PLUS);
    361         }
    362     }
    363 
    364     /**
    365      * Constructs a new photo module.
    366      */
    367     public PhotoModule(AppController app) {
    368         super(app);
    369         mGcamModeIndex = app.getAndroidContext().getResources()
    370                 .getInteger(R.integer.camera_mode_gcam);
    371     }
    372 
    373     @Override
    374     public String getPeekAccessibilityString() {
    375         return mAppController.getAndroidContext()
    376             .getResources().getString(R.string.photo_accessibility_peek);
    377     }
    378 
    379     @Override
    380     public void init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent) {
    381         mActivity = activity;
    382         // TODO: Need to look at the controller interface to see if we can get
    383         // rid of passing in the activity directly.
    384         mAppController = mActivity;
    385 
    386         mUI = new PhotoUI(mActivity, this, mActivity.getModuleLayoutRoot());
    387         mActivity.setPreviewStatusListener(mUI);
    388 
    389         SettingsManager settingsManager = mActivity.getSettingsManager();
    390         // TODO: Move this to SettingsManager as a part of upgrade procedure.
    391         // Aspect Ratio selection dialog is only shown for Nexus 4, 5 and 6.
    392         if (mAppController.getCameraAppUI().shouldShowAspectRatioDialog()) {
    393             // Switch to back camera to set aspect ratio.
    394             settingsManager.setToDefault(mAppController.getModuleScope(), Keys.KEY_CAMERA_ID);
    395         }
    396         mCameraId = settingsManager.getInteger(mAppController.getModuleScope(),
    397                                                Keys.KEY_CAMERA_ID);
    398 
    399         mContentResolver = mActivity.getContentResolver();
    400 
    401         // Surface texture is from camera screen nail and startPreview needs it.
    402         // This must be done before startPreview.
    403         mIsImageCaptureIntent = isImageCaptureIntent();
    404         mUI.setCountdownFinishedListener(this);
    405 
    406         mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
    407         mHeadingSensor = new HeadingSensor(AndroidServices.instance().provideSensorManager());
    408         mCountdownSoundPlayer = new SoundPlayer(mAppController.getAndroidContext());
    409 
    410         try {
    411             mOneCameraManager = OneCameraModule.provideOneCameraManager();
    412         } catch (OneCameraException e) {
    413             Log.e(TAG, "Hardware manager failed to open.");
    414         }
    415 
    416         // TODO: Make this a part of app controller API.
    417         View cancelButton = mActivity.findViewById(R.id.shutter_cancel_button);
    418         cancelButton.setOnClickListener(new View.OnClickListener() {
    419             @Override
    420             public void onClick(View view) {
    421                 cancelCountDown();
    422             }
    423         });
    424     }
    425 
    426     private void cancelCountDown() {
    427         if (mUI.isCountingDown()) {
    428             // Cancel on-going countdown.
    429             mUI.cancelCountDown();
    430         }
    431         mAppController.getCameraAppUI().transitionToCapture();
    432         mAppController.getCameraAppUI().showModeOptions();
    433         mAppController.setShutterEnabled(true);
    434     }
    435 
    436     @Override
    437     public boolean isUsingBottomBar() {
    438         return true;
    439     }
    440 
    441     private void initializeControlByIntent() {
    442         if (mIsImageCaptureIntent) {
    443             mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
    444             setupCaptureParams();
    445         }
    446     }
    447 
    448     private void onPreviewStarted() {
    449         mAppController.onPreviewStarted();
    450         mAppController.setShutterEnabled(true);
    451         setCameraState(IDLE);
    452         startFaceDetection();
    453     }
    454 
    455     @Override
    456     public void onPreviewUIReady() {
    457         Log.i(TAG, "onPreviewUIReady");
    458         startPreview();
    459     }
    460 
    461     @Override
    462     public void onPreviewUIDestroyed() {
    463         if (mCameraDevice == null) {
    464             return;
    465         }
    466         mCameraDevice.setPreviewTexture(null);
    467         stopPreview();
    468     }
    469 
    470     @Override
    471     public void startPreCaptureAnimation() {
    472         mAppController.startFlashAnimation(false);
    473     }
    474 
    475     private void onCameraOpened() {
    476         openCameraCommon();
    477         initializeControlByIntent();
    478     }
    479 
    480     private void switchCamera() {
    481         if (mPaused) {
    482             return;
    483         }
    484         cancelCountDown();
    485 
    486         mAppController.freezeScreenUntilPreviewReady();
    487         SettingsManager settingsManager = mActivity.getSettingsManager();
    488 
    489         Log.i(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
    490         closeCamera();
    491         mCameraId = mPendingSwitchCameraId;
    492 
    493         settingsManager.set(mAppController.getModuleScope(), Keys.KEY_CAMERA_ID, mCameraId);
    494         requestCameraOpen();
    495         mUI.clearFaces();
    496         if (mFocusManager != null) {
    497             mFocusManager.removeMessages();
    498         }
    499 
    500         mMirror = isCameraFrontFacing();
    501         mFocusManager.setMirror(mMirror);
    502         // Start switch camera animation. Post a message because
    503         // onFrameAvailable from the old camera may already exist.
    504     }
    505 
    506     /**
    507      * Uses the {@link CameraProvider} to open the currently-selected camera
    508      * device, using {@link GservicesHelper} to choose between API-1 and API-2.
    509      */
    510     private void requestCameraOpen() {
    511         Log.v(TAG, "requestCameraOpen");
    512         mActivity.getCameraProvider().requestCamera(mCameraId,
    513                         GservicesHelper.useCamera2ApiThroughPortabilityLayer(mActivity
    514                                 .getContentResolver()));
    515     }
    516 
    517     private final ButtonManager.ButtonCallback mCameraCallback =
    518             new ButtonManager.ButtonCallback() {
    519                 @Override
    520                 public void onStateChanged(int state) {
    521                     // At the time this callback is fired, the camera id
    522                     // has be set to the desired camera.
    523 
    524                     if (mPaused || mAppController.getCameraProvider().waitingForCamera()) {
    525                         return;
    526                     }
    527                     // If switching to back camera, and HDR+ is still on,
    528                     // switch back to gcam, otherwise handle callback normally.
    529                     SettingsManager settingsManager = mActivity.getSettingsManager();
    530                     if (Keys.isCameraBackFacing(settingsManager,
    531                                                 mAppController.getModuleScope())) {
    532                         if (Keys.requestsReturnToHdrPlus(settingsManager,
    533                                                          mAppController.getModuleScope())) {
    534                             switchToGcamCapture();
    535                             return;
    536                         }
    537                     }
    538 
    539                     ButtonManager buttonManager = mActivity.getButtonManager();
    540                     buttonManager.disableCameraButtonAndBlock();
    541 
    542                     mPendingSwitchCameraId = state;
    543 
    544                     Log.d(TAG, "Start to switch camera. cameraId=" + state);
    545                     // We need to keep a preview frame for the animation before
    546                     // releasing the camera. This will trigger
    547                     // onPreviewTextureCopied.
    548                     // TODO: Need to animate the camera switch
    549                     switchCamera();
    550                 }
    551             };
    552 
    553     private final ButtonManager.ButtonCallback mHdrPlusCallback =
    554             new ButtonManager.ButtonCallback() {
    555                 @Override
    556                 public void onStateChanged(int state) {
    557                     SettingsManager settingsManager = mActivity.getSettingsManager();
    558                     if (GcamHelper.hasGcamAsSeparateModule(
    559                             mAppController.getCameraFeatureConfig())) {
    560                         // Set the camera setting to default backfacing.
    561                         settingsManager.setToDefault(mAppController.getModuleScope(),
    562                                                      Keys.KEY_CAMERA_ID);
    563                         switchToGcamCapture();
    564                     } else {
    565                         if (Keys.isHdrOn(settingsManager)) {
    566                             settingsManager.set(mAppController.getCameraScope(), Keys.KEY_SCENE_MODE,
    567                                     mCameraCapabilities.getStringifier().stringify(
    568                                             CameraCapabilities.SceneMode.HDR));
    569                         } else {
    570                             settingsManager.set(mAppController.getCameraScope(), Keys.KEY_SCENE_MODE,
    571                                     mCameraCapabilities.getStringifier().stringify(
    572                                             CameraCapabilities.SceneMode.AUTO));
    573                         }
    574                         updateParametersSceneMode();
    575                         if (mCameraDevice != null) {
    576                             mCameraDevice.applySettings(mCameraSettings);
    577                         }
    578                         updateSceneMode();
    579                     }
    580                 }
    581             };
    582 
    583     private final View.OnClickListener mCancelCallback = new View.OnClickListener() {
    584         @Override
    585         public void onClick(View v) {
    586             onCaptureCancelled();
    587         }
    588     };
    589 
    590     private final View.OnClickListener mDoneCallback = new View.OnClickListener() {
    591         @Override
    592         public void onClick(View v) {
    593             onCaptureDone();
    594         }
    595     };
    596 
    597     private final View.OnClickListener mRetakeCallback = new View.OnClickListener() {
    598         @Override
    599         public void onClick(View v) {
    600             mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
    601             onCaptureRetake();
    602         }
    603     };
    604 
    605     @Override
    606     public void hardResetSettings(SettingsManager settingsManager) {
    607         // PhotoModule should hard reset HDR+ to off,
    608         // and HDR to off if HDR+ is supported.
    609         settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR_PLUS, false);
    610         if (GcamHelper.hasGcamAsSeparateModule(mAppController.getCameraFeatureConfig())) {
    611             settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR, false);
    612         }
    613     }
    614 
    615     @Override
    616     public HardwareSpec getHardwareSpec() {
    617         if (mHardwareSpec == null) {
    618             mHardwareSpec = (mCameraSettings != null ?
    619                     new HardwareSpecImpl(getCameraProvider(), mCameraCapabilities,
    620                             mAppController.getCameraFeatureConfig(), isCameraFrontFacing()) : null);
    621         }
    622         return mHardwareSpec;
    623     }
    624 
    625     @Override
    626     public CameraAppUI.BottomBarUISpec getBottomBarSpec() {
    627         CameraAppUI.BottomBarUISpec bottomBarSpec = new CameraAppUI.BottomBarUISpec();
    628 
    629         bottomBarSpec.enableCamera = true;
    630         bottomBarSpec.cameraCallback = mCameraCallback;
    631         bottomBarSpec.enableFlash = !mAppController.getSettingsManager()
    632             .getBoolean(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR);
    633         bottomBarSpec.enableHdr = true;
    634         bottomBarSpec.hdrCallback = mHdrPlusCallback;
    635         bottomBarSpec.enableGridLines = true;
    636         if (mCameraCapabilities != null) {
    637             bottomBarSpec.enableExposureCompensation = true;
    638             bottomBarSpec.exposureCompensationSetCallback =
    639                 new CameraAppUI.BottomBarUISpec.ExposureCompensationSetCallback() {
    640                 @Override
    641                 public void setExposure(int value) {
    642                     setExposureCompensation(value);
    643                 }
    644             };
    645             bottomBarSpec.minExposureCompensation =
    646                 mCameraCapabilities.getMinExposureCompensation();
    647             bottomBarSpec.maxExposureCompensation =
    648                 mCameraCapabilities.getMaxExposureCompensation();
    649             bottomBarSpec.exposureCompensationStep =
    650                 mCameraCapabilities.getExposureCompensationStep();
    651         }
    652 
    653         bottomBarSpec.enableSelfTimer = true;
    654         bottomBarSpec.showSelfTimer = true;
    655 
    656         if (isImageCaptureIntent()) {
    657             bottomBarSpec.showCancel = true;
    658             bottomBarSpec.cancelCallback = mCancelCallback;
    659             bottomBarSpec.showDone = true;
    660             bottomBarSpec.doneCallback = mDoneCallback;
    661             bottomBarSpec.showRetake = true;
    662             bottomBarSpec.retakeCallback = mRetakeCallback;
    663         }
    664 
    665         return bottomBarSpec;
    666     }
    667 
    668     // either open a new camera or switch cameras
    669     private void openCameraCommon() {
    670         mUI.onCameraOpened(mCameraCapabilities, mCameraSettings);
    671         if (mIsImageCaptureIntent) {
    672             // Set hdr plus to default: off.
    673             SettingsManager settingsManager = mActivity.getSettingsManager();
    674             settingsManager.setToDefault(SettingsManager.SCOPE_GLOBAL,
    675                                          Keys.KEY_CAMERA_HDR_PLUS);
    676         }
    677         updateSceneMode();
    678     }
    679 
    680     @Override
    681     public void updatePreviewAspectRatio(float aspectRatio) {
    682         mAppController.updatePreviewAspectRatio(aspectRatio);
    683     }
    684 
    685     private void resetExposureCompensation() {
    686         SettingsManager settingsManager = mActivity.getSettingsManager();
    687         if (settingsManager == null) {
    688             Log.e(TAG, "Settings manager is null!");
    689             return;
    690         }
    691         settingsManager.setToDefault(mAppController.getCameraScope(),
    692                                      Keys.KEY_EXPOSURE);
    693     }
    694 
    695     // Snapshots can only be taken after this is called. It should be called
    696     // once only. We could have done these things in onCreate() but we want to
    697     // make preview screen appear as soon as possible.
    698     private void initializeFirstTime() {
    699         if (mFirstTimeInitialized || mPaused) {
    700             return;
    701         }
    702 
    703         mUI.initializeFirstTime();
    704 
    705         // We set the listener only when both service and shutterbutton
    706         // are initialized.
    707         getServices().getMemoryManager().addListener(this);
    708 
    709         mNamedImages = new NamedImages();
    710 
    711         mFirstTimeInitialized = true;
    712         addIdleHandler();
    713 
    714         mActivity.updateStorageSpaceAndHint(null);
    715     }
    716 
    717     // If the activity is paused and resumed, this method will be called in
    718     // onResume.
    719     private void initializeSecondTime() {
    720         getServices().getMemoryManager().addListener(this);
    721         mNamedImages = new NamedImages();
    722         mUI.initializeSecondTime(mCameraCapabilities, mCameraSettings);
    723     }
    724 
    725     private void addIdleHandler() {
    726         MessageQueue queue = Looper.myQueue();
    727         queue.addIdleHandler(new MessageQueue.IdleHandler() {
    728             @Override
    729             public boolean queueIdle() {
    730                 Storage.ensureOSXCompatible();
    731                 return false;
    732             }
    733         });
    734     }
    735 
    736     @Override
    737     public void startFaceDetection() {
    738         if (mFaceDetectionStarted || mCameraDevice == null) {
    739             return;
    740         }
    741         if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
    742             mFaceDetectionStarted = true;
    743             mUI.onStartFaceDetection(mDisplayOrientation, isCameraFrontFacing());
    744             mCameraDevice.setFaceDetectionCallback(mHandler, mUI);
    745             mCameraDevice.startFaceDetection();
    746             SessionStatsCollector.instance().faceScanActive(true);
    747         }
    748     }
    749 
    750     @Override
    751     public void stopFaceDetection() {
    752         if (!mFaceDetectionStarted || mCameraDevice == null) {
    753             return;
    754         }
    755         if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
    756             mFaceDetectionStarted = false;
    757             mCameraDevice.setFaceDetectionCallback(null, null);
    758             mCameraDevice.stopFaceDetection();
    759             mUI.clearFaces();
    760             SessionStatsCollector.instance().faceScanActive(false);
    761         }
    762     }
    763 
    764     private final class ShutterCallback
    765             implements CameraShutterCallback {
    766 
    767         private final boolean mNeedsAnimation;
    768 
    769         public ShutterCallback(boolean needsAnimation) {
    770             mNeedsAnimation = needsAnimation;
    771         }
    772 
    773         @Override
    774         public void onShutter(CameraProxy camera) {
    775             mShutterCallbackTime = System.currentTimeMillis();
    776             mShutterLag = mShutterCallbackTime - mCaptureStartTime;
    777             Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
    778             if (mNeedsAnimation) {
    779                 mActivity.runOnUiThread(new Runnable() {
    780                     @Override
    781                     public void run() {
    782                         animateAfterShutter();
    783                     }
    784                 });
    785             }
    786         }
    787     }
    788 
    789     private final class PostViewPictureCallback
    790             implements CameraPictureCallback {
    791         @Override
    792         public void onPictureTaken(byte[] data, CameraProxy camera) {
    793             mPostViewPictureCallbackTime = System.currentTimeMillis();
    794             Log.v(TAG, "mShutterToPostViewCallbackTime = "
    795                     + (mPostViewPictureCallbackTime - mShutterCallbackTime)
    796                     + "ms");
    797         }
    798     }
    799 
    800     private final class RawPictureCallback
    801             implements CameraPictureCallback {
    802         @Override
    803         public void onPictureTaken(byte[] rawData, CameraProxy camera) {
    804             mRawPictureCallbackTime = System.currentTimeMillis();
    805             Log.v(TAG, "mShutterToRawCallbackTime = "
    806                     + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
    807         }
    808     }
    809 
    810     private static class ResizeBundle {
    811         byte[] jpegData;
    812         float targetAspectRatio;
    813         ExifInterface exif;
    814     }
    815 
    816     /**
    817      * @return Cropped image if the target aspect ratio is larger than the jpeg
    818      *         aspect ratio on the long axis. The original jpeg otherwise.
    819      */
    820     private ResizeBundle cropJpegDataToAspectRatio(ResizeBundle dataBundle) {
    821 
    822         final byte[] jpegData = dataBundle.jpegData;
    823         final ExifInterface exif = dataBundle.exif;
    824         float targetAspectRatio = dataBundle.targetAspectRatio;
    825 
    826         Bitmap original = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length);
    827         int originalWidth = original.getWidth();
    828         int originalHeight = original.getHeight();
    829         int newWidth;
    830         int newHeight;
    831 
    832         if (originalWidth > originalHeight) {
    833             newHeight = (int) (originalWidth / targetAspectRatio);
    834             newWidth = originalWidth;
    835         } else {
    836             newWidth = (int) (originalHeight / targetAspectRatio);
    837             newHeight = originalHeight;
    838         }
    839         int xOffset = (originalWidth - newWidth)/2;
    840         int yOffset = (originalHeight - newHeight)/2;
    841 
    842         if (xOffset < 0 || yOffset < 0) {
    843             return dataBundle;
    844         }
    845 
    846         Bitmap resized = Bitmap.createBitmap(original,xOffset,yOffset,newWidth, newHeight);
    847         exif.setTagValue(ExifInterface.TAG_PIXEL_X_DIMENSION, new Integer(newWidth));
    848         exif.setTagValue(ExifInterface.TAG_PIXEL_Y_DIMENSION, new Integer(newHeight));
    849 
    850         ByteArrayOutputStream stream = new ByteArrayOutputStream();
    851 
    852         resized.compress(Bitmap.CompressFormat.JPEG, 90, stream);
    853         dataBundle.jpegData = stream.toByteArray();
    854         return dataBundle;
    855     }
    856 
    857     private final class JpegPictureCallback
    858             implements CameraPictureCallback {
    859         Location mLocation;
    860 
    861         public JpegPictureCallback(Location loc) {
    862             mLocation = loc;
    863         }
    864 
    865         @Override
    866         public void onPictureTaken(final byte[] originalJpegData, final CameraProxy camera) {
    867             Log.i(TAG, "onPictureTaken");
    868             mAppController.setShutterEnabled(true);
    869             if (mPaused) {
    870                 return;
    871             }
    872             if (mIsImageCaptureIntent) {
    873                 stopPreview();
    874             }
    875             if (mSceneMode == CameraCapabilities.SceneMode.HDR) {
    876                 mUI.setSwipingEnabled(true);
    877             }
    878 
    879             mJpegPictureCallbackTime = System.currentTimeMillis();
    880             // If postview callback has arrived, the captured image is displayed
    881             // in postview callback. If not, the captured image is displayed in
    882             // raw picture callback.
    883             if (mPostViewPictureCallbackTime != 0) {
    884                 mShutterToPictureDisplayedTime =
    885                         mPostViewPictureCallbackTime - mShutterCallbackTime;
    886                 mPictureDisplayedToJpegCallbackTime =
    887                         mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
    888             } else {
    889                 mShutterToPictureDisplayedTime =
    890                         mRawPictureCallbackTime - mShutterCallbackTime;
    891                 mPictureDisplayedToJpegCallbackTime =
    892                         mJpegPictureCallbackTime - mRawPictureCallbackTime;
    893             }
    894             Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
    895                     + mPictureDisplayedToJpegCallbackTime + "ms");
    896 
    897             if (!mIsImageCaptureIntent) {
    898                 setupPreview();
    899             }
    900 
    901             long now = System.currentTimeMillis();
    902             mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
    903             Log.v(TAG, "mJpegCallbackFinishTime = " + mJpegCallbackFinishTime + "ms");
    904             mJpegPictureCallbackTime = 0;
    905 
    906             final ExifInterface exif = Exif.getExif(originalJpegData);
    907             final NamedEntity name = mNamedImages.getNextNameEntity();
    908             if (mShouldResizeTo16x9) {
    909                 final ResizeBundle dataBundle = new ResizeBundle();
    910                 dataBundle.jpegData = originalJpegData;
    911                 dataBundle.targetAspectRatio = ResolutionUtil.NEXUS_5_LARGE_16_BY_9_ASPECT_RATIO;
    912                 dataBundle.exif = exif;
    913                 new AsyncTask<ResizeBundle, Void, ResizeBundle>() {
    914 
    915                     @Override
    916                     protected ResizeBundle doInBackground(ResizeBundle... resizeBundles) {
    917                         return cropJpegDataToAspectRatio(resizeBundles[0]);
    918                     }
    919 
    920                     @Override
    921                     protected void onPostExecute(ResizeBundle result) {
    922                         saveFinalPhoto(result.jpegData, name, result.exif, camera);
    923                     }
    924                 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, dataBundle);
    925 
    926             } else {
    927                 saveFinalPhoto(originalJpegData, name, exif, camera);
    928             }
    929         }
    930 
    931         void saveFinalPhoto(final byte[] jpegData, NamedEntity name, final ExifInterface exif,
    932                 CameraProxy camera) {
    933             int orientation = Exif.getOrientation(exif);
    934 
    935             float zoomValue = 1.0f;
    936             if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
    937                 zoomValue = mCameraSettings.getCurrentZoomRatio();
    938             }
    939             boolean hdrOn = CameraCapabilities.SceneMode.HDR == mSceneMode;
    940             String flashSetting =
    941                     mActivity.getSettingsManager().getString(mAppController.getCameraScope(),
    942                                                              Keys.KEY_FLASH_MODE);
    943             boolean gridLinesOn = Keys.areGridLinesOn(mActivity.getSettingsManager());
    944             UsageStatistics.instance().photoCaptureDoneEvent(
    945                     eventprotos.NavigationChange.Mode.PHOTO_CAPTURE,
    946                     name.title + ".jpg", exif,
    947                     isCameraFrontFacing(), hdrOn, zoomValue, flashSetting, gridLinesOn,
    948                     (float) mTimerDuration, null, mShutterTouchCoordinate, mVolumeButtonClickedFlag,
    949                     null, null, null);
    950             mShutterTouchCoordinate = null;
    951             mVolumeButtonClickedFlag = false;
    952 
    953             if (!mIsImageCaptureIntent) {
    954                 // Calculate the width and the height of the jpeg.
    955                 Integer exifWidth = exif.getTagIntValue(ExifInterface.TAG_PIXEL_X_DIMENSION);
    956                 Integer exifHeight = exif.getTagIntValue(ExifInterface.TAG_PIXEL_Y_DIMENSION);
    957                 int width, height;
    958                 if (mShouldResizeTo16x9 && exifWidth != null && exifHeight != null) {
    959                     width = exifWidth;
    960                     height = exifHeight;
    961                 } else {
    962                     Size s = new Size(mCameraSettings.getCurrentPhotoSize());
    963                     if ((mJpegRotation + orientation) % 180 == 0) {
    964                         width = s.width();
    965                         height = s.height();
    966                     } else {
    967                         width = s.height();
    968                         height = s.width();
    969                     }
    970                 }
    971                 String title = (name == null) ? null : name.title;
    972                 long date = (name == null) ? -1 : name.date;
    973 
    974                 // Handle debug mode outputs
    975                 if (mDebugUri != null) {
    976                     // If using a debug uri, save jpeg there.
    977                     saveToDebugUri(jpegData);
    978 
    979                     // Adjust the title of the debug image shown in mediastore.
    980                     if (title != null) {
    981                         title = DEBUG_IMAGE_PREFIX + title;
    982                     }
    983                 }
    984 
    985                 if (title == null) {
    986                     Log.e(TAG, "Unbalanced name/data pair");
    987                 } else {
    988                     if (date == -1) {
    989                         date = mCaptureStartTime;
    990                     }
    991                     int heading = mHeadingSensor.getCurrentHeading();
    992                     if (heading != HeadingSensor.INVALID_HEADING) {
    993                         // heading direction has been updated by the sensor.
    994                         ExifTag directionRefTag = exif.buildTag(
    995                                 ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
    996                                 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
    997                         ExifTag directionTag = exif.buildTag(
    998                                 ExifInterface.TAG_GPS_IMG_DIRECTION,
    999                                 new Rational(heading, 1));
   1000                         exif.setTag(directionRefTag);
   1001                         exif.setTag(directionTag);
   1002                     }
   1003                     getServices().getMediaSaver().addImage(
   1004                             jpegData, title, date, mLocation, width, height,
   1005                             orientation, exif, mOnMediaSavedListener);
   1006                 }
   1007                 // Animate capture with real jpeg data instead of a preview
   1008                 // frame.
   1009                 mUI.animateCapture(jpegData, orientation, mMirror);
   1010             } else {
   1011                 mJpegImageData = jpegData;
   1012                 if (!mQuickCapture) {
   1013                     Log.v(TAG, "showing UI");
   1014                     mUI.showCapturedImageForReview(jpegData, orientation, mMirror);
   1015                 } else {
   1016                     onCaptureDone();
   1017                 }
   1018             }
   1019 
   1020             // Send the taken photo to remote shutter listeners, if any are
   1021             // registered.
   1022             getServices().getRemoteShutterListener().onPictureTaken(jpegData);
   1023 
   1024             // Check this in advance of each shot so we don't add to shutter
   1025             // latency. It's true that someone else could write to the SD card
   1026             // in the mean time and fill it, but that could have happened
   1027             // between the shutter press and saving the JPEG too.
   1028             mActivity.updateStorageSpaceAndHint(null);
   1029         }
   1030     }
   1031 
   1032     private final class AutoFocusCallback implements CameraAFCallback {
   1033         @Override
   1034         public void onAutoFocus(boolean focused, CameraProxy camera) {
   1035             SessionStatsCollector.instance().autofocusResult(focused);
   1036             if (mPaused) {
   1037                 return;
   1038             }
   1039 
   1040             mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
   1041             Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms   focused = "+focused);
   1042             setCameraState(IDLE);
   1043             mFocusManager.onAutoFocus(focused, false);
   1044         }
   1045     }
   1046 
   1047     private final class AutoFocusMoveCallback
   1048             implements CameraAFMoveCallback {
   1049         @Override
   1050         public void onAutoFocusMoving(
   1051                 boolean moving, CameraProxy camera) {
   1052             mFocusManager.onAutoFocusMoving(moving);
   1053             SessionStatsCollector.instance().autofocusMoving(moving);
   1054         }
   1055     }
   1056 
   1057     /**
   1058      * This class is just a thread-safe queue for name,date holder objects.
   1059      */
   1060     public static class NamedImages {
   1061         private final Vector<NamedEntity> mQueue;
   1062 
   1063         public NamedImages() {
   1064             mQueue = new Vector<NamedEntity>();
   1065         }
   1066 
   1067         public void nameNewImage(long date) {
   1068             NamedEntity r = new NamedEntity();
   1069             r.title = CameraUtil.instance().createJpegName(date);
   1070             r.date = date;
   1071             mQueue.add(r);
   1072         }
   1073 
   1074         public NamedEntity getNextNameEntity() {
   1075             synchronized (mQueue) {
   1076                 if (!mQueue.isEmpty()) {
   1077                     return mQueue.remove(0);
   1078                 }
   1079             }
   1080             return null;
   1081         }
   1082 
   1083         public static class NamedEntity {
   1084             public String title;
   1085             public long date;
   1086         }
   1087     }
   1088 
   1089     private void setCameraState(int state) {
   1090         mCameraState = state;
   1091         switch (state) {
   1092             case PREVIEW_STOPPED:
   1093             case SNAPSHOT_IN_PROGRESS:
   1094             case SWITCHING_CAMERA:
   1095                 // TODO: Tell app UI to disable swipe
   1096                 break;
   1097             case PhotoController.IDLE:
   1098                 // TODO: Tell app UI to enable swipe
   1099                 break;
   1100         }
   1101     }
   1102 
   1103     private void animateAfterShutter() {
   1104         // Only animate when in full screen capture mode
   1105         // i.e. If monkey/a user swipes to the gallery during picture taking,
   1106         // don't show animation
   1107         if (!mIsImageCaptureIntent) {
   1108             mUI.animateFlash();
   1109         }
   1110     }
   1111 
   1112     @Override
   1113     public boolean capture() {
   1114         Log.i(TAG, "capture");
   1115         // If we are already in the middle of taking a snapshot or the image
   1116         // save request is full then ignore.
   1117         if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
   1118                 || mCameraState == SWITCHING_CAMERA) {
   1119             return false;
   1120         }
   1121         setCameraState(SNAPSHOT_IN_PROGRESS);
   1122 
   1123         mCaptureStartTime = System.currentTimeMillis();
   1124 
   1125         mPostViewPictureCallbackTime = 0;
   1126         mJpegImageData = null;
   1127 
   1128         final boolean animateBefore = (mSceneMode == CameraCapabilities.SceneMode.HDR);
   1129 
   1130         if (animateBefore) {
   1131             animateAfterShutter();
   1132         }
   1133 
   1134         Location loc = mActivity.getLocationManager().getCurrentLocation();
   1135         CameraUtil.setGpsParameters(mCameraSettings, loc);
   1136         mCameraDevice.applySettings(mCameraSettings);
   1137 
   1138         // Set JPEG orientation. Even if screen UI is locked in portrait, camera orientation should
   1139         // still match device orientation (e.g., users should always get landscape photos while
   1140         // capturing by putting device in landscape.)
   1141         Characteristics info = mActivity.getCameraProvider().getCharacteristics(mCameraId);
   1142         int sensorOrientation = info.getSensorOrientation();
   1143         int deviceOrientation =
   1144                 mAppController.getOrientationManager().getDeviceOrientation().getDegrees();
   1145         boolean isFrontCamera = info.isFacingFront();
   1146         mJpegRotation =
   1147                 CameraUtil.getImageRotation(sensorOrientation, deviceOrientation, isFrontCamera);
   1148         mCameraDevice.setJpegOrientation(mJpegRotation);
   1149 
   1150         mCameraDevice.takePicture(mHandler,
   1151                 new ShutterCallback(!animateBefore),
   1152                 mRawPictureCallback, mPostViewPictureCallback,
   1153                 new JpegPictureCallback(loc));
   1154 
   1155         mNamedImages.nameNewImage(mCaptureStartTime);
   1156 
   1157         mFaceDetectionStarted = false;
   1158         return true;
   1159     }
   1160 
   1161     @Override
   1162     public void setFocusParameters() {
   1163         setCameraParameters(UPDATE_PARAM_PREFERENCE);
   1164     }
   1165 
   1166     private void updateSceneMode() {
   1167         // If scene mode is set, we cannot set flash mode, white balance, and
   1168         // focus mode, instead, we read it from driver. Some devices don't have
   1169         // any scene modes, so we must check both NO_SCENE_MODE in addition to
   1170         // AUTO to check where there is no actual scene mode set.
   1171         if (!(CameraCapabilities.SceneMode.AUTO == mSceneMode ||
   1172                 CameraCapabilities.SceneMode.NO_SCENE_MODE == mSceneMode)) {
   1173             overrideCameraSettings(mCameraSettings.getCurrentFlashMode(),
   1174                     mCameraSettings.getCurrentFocusMode());
   1175         }
   1176     }
   1177 
   1178     private void overrideCameraSettings(CameraCapabilities.FlashMode flashMode,
   1179             CameraCapabilities.FocusMode focusMode) {
   1180         CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
   1181         SettingsManager settingsManager = mActivity.getSettingsManager();
   1182         if ((flashMode != null) && (!CameraCapabilities.FlashMode.NO_FLASH.equals(flashMode))) {
   1183             String flashModeString = stringifier.stringify(flashMode);
   1184             Log.v(TAG, "override flash setting to: " + flashModeString);
   1185             settingsManager.set(mAppController.getCameraScope(), Keys.KEY_FLASH_MODE,
   1186                     flashModeString);
   1187         } else {
   1188             Log.v(TAG, "skip setting flash mode on override due to NO_FLASH");
   1189         }
   1190         if (focusMode != null) {
   1191             String focusModeString = stringifier.stringify(focusMode);
   1192             Log.v(TAG, "override focus setting to: " + focusModeString);
   1193             settingsManager.set(mAppController.getCameraScope(), Keys.KEY_FOCUS_MODE,
   1194                     focusModeString);
   1195         }
   1196     }
   1197 
   1198     @Override
   1199     public void onCameraAvailable(CameraProxy cameraProxy) {
   1200         Log.i(TAG, "onCameraAvailable");
   1201         if (mPaused) {
   1202             return;
   1203         }
   1204         mCameraDevice = cameraProxy;
   1205 
   1206         initializeCapabilities();
   1207         // mCameraCapabilities is guaranteed to initialized at this point.
   1208         mAppController.getCameraAppUI().showAccessibilityZoomUI(
   1209                 mCameraCapabilities.getMaxZoomRatio());
   1210 
   1211 
   1212         // Reset zoom value index.
   1213         mZoomValue = 1.0f;
   1214         if (mFocusManager == null) {
   1215             initializeFocusManager();
   1216         }
   1217         mFocusManager.updateCapabilities(mCameraCapabilities);
   1218 
   1219         // Do camera parameter dependent initialization.
   1220         mCameraSettings = mCameraDevice.getSettings();
   1221         // Set a default flash mode and focus mode
   1222         if (mCameraSettings.getCurrentFlashMode() == null) {
   1223             mCameraSettings.setFlashMode(CameraCapabilities.FlashMode.NO_FLASH);
   1224         }
   1225         if (mCameraSettings.getCurrentFocusMode() == null) {
   1226             mCameraSettings.setFocusMode(CameraCapabilities.FocusMode.AUTO);
   1227         }
   1228 
   1229         setCameraParameters(UPDATE_PARAM_ALL);
   1230         // Set a listener which updates camera parameters based
   1231         // on changed settings.
   1232         SettingsManager settingsManager = mActivity.getSettingsManager();
   1233         settingsManager.addListener(this);
   1234         mCameraPreviewParamsReady = true;
   1235 
   1236         startPreview();
   1237 
   1238         onCameraOpened();
   1239 
   1240         mHardwareSpec = new HardwareSpecImpl(getCameraProvider(), mCameraCapabilities,
   1241                 mAppController.getCameraFeatureConfig(), isCameraFrontFacing());
   1242 
   1243         ButtonManager buttonManager = mActivity.getButtonManager();
   1244         buttonManager.enableCameraButton();
   1245     }
   1246 
   1247     @Override
   1248     public void onCaptureCancelled() {
   1249         mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
   1250         mActivity.finish();
   1251     }
   1252 
   1253     @Override
   1254     public void onCaptureRetake() {
   1255         Log.i(TAG, "onCaptureRetake");
   1256         if (mPaused) {
   1257             return;
   1258         }
   1259         mUI.hidePostCaptureAlert();
   1260         mUI.hideIntentReviewImageView();
   1261         setupPreview();
   1262     }
   1263 
   1264     @Override
   1265     public void onCaptureDone() {
   1266         Log.i(TAG, "onCaptureDone");
   1267         if (mPaused) {
   1268             return;
   1269         }
   1270 
   1271         byte[] data = mJpegImageData;
   1272 
   1273         if (mCropValue == null) {
   1274             // First handle the no crop case -- just return the value. If the
   1275             // caller specifies a "save uri" then write the data to its
   1276             // stream. Otherwise, pass back a scaled down version of the bitmap
   1277             // directly in the extras.
   1278             if (mSaveUri != null) {
   1279                 OutputStream outputStream = null;
   1280                 try {
   1281                     outputStream = mContentResolver.openOutputStream(mSaveUri);
   1282                     outputStream.write(data);
   1283                     outputStream.close();
   1284 
   1285                     Log.v(TAG, "saved result to URI: " + mSaveUri);
   1286                     mActivity.setResultEx(Activity.RESULT_OK);
   1287                     mActivity.finish();
   1288                 } catch (IOException ex) {
   1289                     onError();
   1290                 } finally {
   1291                     CameraUtil.closeSilently(outputStream);
   1292                 }
   1293             } else {
   1294                 ExifInterface exif = Exif.getExif(data);
   1295                 int orientation = Exif.getOrientation(exif);
   1296                 Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024);
   1297                 bitmap = CameraUtil.rotate(bitmap, orientation);
   1298                 Log.v(TAG, "inlined bitmap into capture intent result");
   1299                 mActivity.setResultEx(Activity.RESULT_OK,
   1300                         new Intent("inline-data").putExtra("data", bitmap));
   1301                 mActivity.finish();
   1302             }
   1303         } else {
   1304             // Save the image to a temp file and invoke the cropper
   1305             Uri tempUri = null;
   1306             FileOutputStream tempStream = null;
   1307             try {
   1308                 File path = mActivity.getFileStreamPath(sTempCropFilename);
   1309                 path.delete();
   1310                 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
   1311                 tempStream.write(data);
   1312                 tempStream.close();
   1313                 tempUri = Uri.fromFile(path);
   1314                 Log.v(TAG, "wrote temp file for cropping to: " + sTempCropFilename);
   1315             } catch (FileNotFoundException ex) {
   1316                 Log.w(TAG, "error writing temp cropping file to: " + sTempCropFilename, ex);
   1317                 mActivity.setResultEx(Activity.RESULT_CANCELED);
   1318                 onError();
   1319                 return;
   1320             } catch (IOException ex) {
   1321                 Log.w(TAG, "error writing temp cropping file to: " + sTempCropFilename, ex);
   1322                 mActivity.setResultEx(Activity.RESULT_CANCELED);
   1323                 onError();
   1324                 return;
   1325             } finally {
   1326                 CameraUtil.closeSilently(tempStream);
   1327             }
   1328 
   1329             Bundle newExtras = new Bundle();
   1330             if (mCropValue.equals("circle")) {
   1331                 newExtras.putString("circleCrop", "true");
   1332             }
   1333             if (mSaveUri != null) {
   1334                 Log.v(TAG, "setting output of cropped file to: " + mSaveUri);
   1335                 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
   1336             } else {
   1337                 newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true);
   1338             }
   1339             if (mActivity.isSecureCamera()) {
   1340                 newExtras.putBoolean(CameraUtil.KEY_SHOW_WHEN_LOCKED, true);
   1341             }
   1342 
   1343             // TODO: Share this constant.
   1344             final String CROP_ACTION = "com.android.camera.action.CROP";
   1345             Intent cropIntent = new Intent(CROP_ACTION);
   1346 
   1347             cropIntent.setData(tempUri);
   1348             cropIntent.putExtras(newExtras);
   1349             Log.v(TAG, "starting CROP intent for capture");
   1350             mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
   1351         }
   1352     }
   1353 
   1354     @Override
   1355     public void onShutterCoordinate(TouchCoordinate coord) {
   1356         mShutterTouchCoordinate = coord;
   1357     }
   1358 
   1359     @Override
   1360     public void onShutterButtonFocus(boolean pressed) {
   1361         // Do nothing. We don't support half-press to focus anymore.
   1362     }
   1363 
   1364     @Override
   1365     public void onShutterButtonClick() {
   1366         if (mPaused || (mCameraState == SWITCHING_CAMERA)
   1367                 || (mCameraState == PREVIEW_STOPPED)
   1368                 || !mAppController.isShutterEnabled()) {
   1369             mVolumeButtonClickedFlag = false;
   1370             return;
   1371         }
   1372 
   1373         // Do not take the picture if there is not enough storage.
   1374         if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
   1375             Log.i(TAG, "Not enough space or storage not ready. remaining="
   1376                     + mActivity.getStorageSpaceBytes());
   1377             mVolumeButtonClickedFlag = false;
   1378             return;
   1379         }
   1380         Log.d(TAG, "onShutterButtonClick: mCameraState=" + mCameraState +
   1381                 " mVolumeButtonClickedFlag=" + mVolumeButtonClickedFlag);
   1382 
   1383         mAppController.setShutterEnabled(false);
   1384 
   1385         int countDownDuration = mActivity.getSettingsManager()
   1386             .getInteger(SettingsManager.SCOPE_GLOBAL, Keys.KEY_COUNTDOWN_DURATION);
   1387         mTimerDuration = countDownDuration;
   1388         if (countDownDuration > 0) {
   1389             // Start count down.
   1390             mAppController.getCameraAppUI().transitionToCancel();
   1391             mAppController.getCameraAppUI().hideModeOptions();
   1392             mUI.startCountdown(countDownDuration);
   1393             return;
   1394         } else {
   1395             focusAndCapture();
   1396         }
   1397     }
   1398 
   1399     private void focusAndCapture() {
   1400         if (mSceneMode == CameraCapabilities.SceneMode.HDR) {
   1401             mUI.setSwipingEnabled(false);
   1402         }
   1403         // If the user wants to do a snapshot while the previous one is still
   1404         // in progress, remember the fact and do it after we finish the previous
   1405         // one and re-start the preview. Snapshot in progress also includes the
   1406         // state that autofocus is focusing and a picture will be taken when
   1407         // focus callback arrives.
   1408         if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)) {
   1409             if (!mIsImageCaptureIntent) {
   1410                 mSnapshotOnIdle = true;
   1411             }
   1412             return;
   1413         }
   1414 
   1415         mSnapshotOnIdle = false;
   1416         mFocusManager.focusAndCapture(mCameraSettings.getCurrentFocusMode());
   1417     }
   1418 
   1419     @Override
   1420     public void onRemainingSecondsChanged(int remainingSeconds) {
   1421         if (remainingSeconds == 1) {
   1422             mCountdownSoundPlayer.play(R.raw.timer_final_second, 0.6f);
   1423         } else if (remainingSeconds == 2 || remainingSeconds == 3) {
   1424             mCountdownSoundPlayer.play(R.raw.timer_increment, 0.6f);
   1425         }
   1426     }
   1427 
   1428     @Override
   1429     public void onCountDownFinished() {
   1430         mAppController.getCameraAppUI().transitionToCapture();
   1431         mAppController.getCameraAppUI().showModeOptions();
   1432         if (mPaused) {
   1433             return;
   1434         }
   1435         focusAndCapture();
   1436     }
   1437 
   1438     @Override
   1439     public void resume() {
   1440         mPaused = false;
   1441 
   1442         mCountdownSoundPlayer.loadSound(R.raw.timer_final_second);
   1443         mCountdownSoundPlayer.loadSound(R.raw.timer_increment);
   1444         if (mFocusManager != null) {
   1445             // If camera is not open when resume is called, focus manager will
   1446             // not be initialized yet, in which case it will start listening to
   1447             // preview area size change later in the initialization.
   1448             mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
   1449         }
   1450         mAppController.addPreviewAreaSizeChangedListener(mUI);
   1451 
   1452         CameraProvider camProvider = mActivity.getCameraProvider();
   1453         if (camProvider == null) {
   1454             // No camera provider, the Activity is destroyed already.
   1455             return;
   1456         }
   1457 
   1458         requestCameraOpen();
   1459 
   1460         mJpegPictureCallbackTime = 0;
   1461         mZoomValue = 1.0f;
   1462 
   1463         mOnResumeTime = SystemClock.uptimeMillis();
   1464         checkDisplayRotation();
   1465 
   1466         // If first time initialization is not finished, put it in the
   1467         // message queue.
   1468         if (!mFirstTimeInitialized) {
   1469             mHandler.sendEmptyMessage(MSG_FIRST_TIME_INIT);
   1470         } else {
   1471             initializeSecondTime();
   1472         }
   1473 
   1474         mHeadingSensor.activate();
   1475 
   1476         getServices().getRemoteShutterListener().onModuleReady(this);
   1477         SessionStatsCollector.instance().sessionActive(true);
   1478     }
   1479 
   1480     /**
   1481      * @return Whether the currently active camera is front-facing.
   1482      */
   1483     private boolean isCameraFrontFacing() {
   1484         return mAppController.getCameraProvider().getCharacteristics(mCameraId)
   1485                 .isFacingFront();
   1486     }
   1487 
   1488     /**
   1489      * The focus manager is the first UI related element to get initialized, and
   1490      * it requires the RenderOverlay, so initialize it here
   1491      */
   1492     private void initializeFocusManager() {
   1493         // Create FocusManager object. startPreview needs it.
   1494         // if mFocusManager not null, reuse it
   1495         // otherwise create a new instance
   1496         if (mFocusManager != null) {
   1497             mFocusManager.removeMessages();
   1498         } else {
   1499             mMirror = isCameraFrontFacing();
   1500             String[] defaultFocusModesStrings = mActivity.getResources().getStringArray(
   1501                     R.array.pref_camera_focusmode_default_array);
   1502             ArrayList<CameraCapabilities.FocusMode> defaultFocusModes =
   1503                     new ArrayList<CameraCapabilities.FocusMode>();
   1504             CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
   1505             for (String modeString : defaultFocusModesStrings) {
   1506                 CameraCapabilities.FocusMode mode = stringifier.focusModeFromString(modeString);
   1507                 if (mode != null) {
   1508                     defaultFocusModes.add(mode);
   1509                 }
   1510             }
   1511             mFocusManager =
   1512                     new FocusOverlayManager(mAppController, defaultFocusModes,
   1513                             mCameraCapabilities, this, mMirror, mActivity.getMainLooper(),
   1514                             mUI.getFocusRing());
   1515             mMotionManager = getServices().getMotionManager();
   1516             if (mMotionManager != null) {
   1517                 mMotionManager.addListener(mFocusManager);
   1518             }
   1519         }
   1520         mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
   1521     }
   1522 
   1523     /**
   1524      * @return Whether we are resuming from within the lockscreen.
   1525      */
   1526     private boolean isResumeFromLockscreen() {
   1527         String action = mActivity.getIntent().getAction();
   1528         return (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action)
   1529                 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action));
   1530     }
   1531 
   1532     @Override
   1533     public void pause() {
   1534         Log.v(TAG, "pause");
   1535         mPaused = true;
   1536         getServices().getRemoteShutterListener().onModuleExit();
   1537         SessionStatsCollector.instance().sessionActive(false);
   1538 
   1539         mHeadingSensor.deactivate();
   1540 
   1541         // Reset the focus first. Camera CTS does not guarantee that
   1542         // cancelAutoFocus is allowed after preview stops.
   1543         if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
   1544             mCameraDevice.cancelAutoFocus();
   1545         }
   1546 
   1547         // If the camera has not been opened asynchronously yet,
   1548         // and startPreview hasn't been called, then this is a no-op.
   1549         // (e.g. onResume -> onPause -> onResume).
   1550         stopPreview();
   1551         cancelCountDown();
   1552         mCountdownSoundPlayer.unloadSound(R.raw.timer_final_second);
   1553         mCountdownSoundPlayer.unloadSound(R.raw.timer_increment);
   1554 
   1555         mNamedImages = null;
   1556         // If we are in an image capture intent and has taken
   1557         // a picture, we just clear it in onPause.
   1558         mJpegImageData = null;
   1559 
   1560         // Remove the messages and runnables in the queue.
   1561         mHandler.removeCallbacksAndMessages(null);
   1562 
   1563         if (mMotionManager != null) {
   1564             mMotionManager.removeListener(mFocusManager);
   1565             mMotionManager = null;
   1566         }
   1567 
   1568         closeCamera();
   1569         mActivity.enableKeepScreenOn(false);
   1570         mUI.onPause();
   1571 
   1572         mPendingSwitchCameraId = -1;
   1573         if (mFocusManager != null) {
   1574             mFocusManager.removeMessages();
   1575         }
   1576         getServices().getMemoryManager().removeListener(this);
   1577         mAppController.removePreviewAreaSizeChangedListener(mFocusManager);
   1578         mAppController.removePreviewAreaSizeChangedListener(mUI);
   1579 
   1580         SettingsManager settingsManager = mActivity.getSettingsManager();
   1581         settingsManager.removeListener(this);
   1582     }
   1583 
   1584     @Override
   1585     public void destroy() {
   1586         mCountdownSoundPlayer.release();
   1587     }
   1588 
   1589     @Override
   1590     public void onLayoutOrientationChanged(boolean isLandscape) {
   1591         setDisplayOrientation();
   1592     }
   1593 
   1594     @Override
   1595     public void updateCameraOrientation() {
   1596         if (mDisplayRotation != CameraUtil.getDisplayRotation()) {
   1597             setDisplayOrientation();
   1598         }
   1599     }
   1600 
   1601     private boolean canTakePicture() {
   1602         return isCameraIdle()
   1603                 && (mActivity.getStorageSpaceBytes() > Storage.LOW_STORAGE_THRESHOLD_BYTES);
   1604     }
   1605 
   1606     @Override
   1607     public void autoFocus() {
   1608         if (mCameraDevice == null) {
   1609             return;
   1610         }
   1611         Log.v(TAG,"Starting auto focus");
   1612         mFocusStartTime = System.currentTimeMillis();
   1613         mCameraDevice.autoFocus(mHandler, mAutoFocusCallback);
   1614         SessionStatsCollector.instance().autofocusManualTrigger();
   1615         setCameraState(FOCUSING);
   1616     }
   1617 
   1618     @Override
   1619     public void cancelAutoFocus() {
   1620         if (mCameraDevice == null) {
   1621             return;
   1622         }
   1623         mCameraDevice.cancelAutoFocus();
   1624         setCameraState(IDLE);
   1625         setCameraParameters(UPDATE_PARAM_PREFERENCE);
   1626     }
   1627 
   1628     @Override
   1629     public void onSingleTapUp(View view, int x, int y) {
   1630         if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
   1631                 || mCameraState == SNAPSHOT_IN_PROGRESS
   1632                 || mCameraState == SWITCHING_CAMERA
   1633                 || mCameraState == PREVIEW_STOPPED) {
   1634             return;
   1635         }
   1636 
   1637         // Check if metering area or focus area is supported.
   1638         if (!mFocusAreaSupported && !mMeteringAreaSupported) {
   1639             return;
   1640         }
   1641         mFocusManager.onSingleTapUp(x, y);
   1642     }
   1643 
   1644     @Override
   1645     public boolean onBackPressed() {
   1646         return mUI.onBackPressed();
   1647     }
   1648 
   1649     @Override
   1650     public boolean onKeyDown(int keyCode, KeyEvent event) {
   1651         switch (keyCode) {
   1652             case KeyEvent.KEYCODE_VOLUME_UP:
   1653             case KeyEvent.KEYCODE_VOLUME_DOWN:
   1654             case KeyEvent.KEYCODE_FOCUS:
   1655                 if (/* TODO: mActivity.isInCameraApp() && */mFirstTimeInitialized &&
   1656                     !mActivity.getCameraAppUI().isInIntentReview()) {
   1657                     if (event.getRepeatCount() == 0) {
   1658                         onShutterButtonFocus(true);
   1659                     }
   1660                     return true;
   1661                 }
   1662                 return false;
   1663             case KeyEvent.KEYCODE_CAMERA:
   1664                 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
   1665                     onShutterButtonClick();
   1666                 }
   1667                 return true;
   1668             case KeyEvent.KEYCODE_DPAD_CENTER:
   1669                 // If we get a dpad center event without any focused view, move
   1670                 // the focus to the shutter button and press it.
   1671                 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
   1672                     // Start auto-focus immediately to reduce shutter lag. After
   1673                     // the shutter button gets the focus, onShutterButtonFocus()
   1674                     // will be called again but it is fine.
   1675                     onShutterButtonFocus(true);
   1676                 }
   1677                 return true;
   1678         }
   1679         return false;
   1680     }
   1681 
   1682     @Override
   1683     public boolean onKeyUp(int keyCode, KeyEvent event) {
   1684         switch (keyCode) {
   1685             case KeyEvent.KEYCODE_VOLUME_UP:
   1686             case KeyEvent.KEYCODE_VOLUME_DOWN:
   1687                 if (/* mActivity.isInCameraApp() && */mFirstTimeInitialized &&
   1688                     !mActivity.getCameraAppUI().isInIntentReview()) {
   1689                     if (mUI.isCountingDown()) {
   1690                         cancelCountDown();
   1691                     } else {
   1692                         mVolumeButtonClickedFlag = true;
   1693                         onShutterButtonClick();
   1694                     }
   1695                     return true;
   1696                 }
   1697                 return false;
   1698             case KeyEvent.KEYCODE_FOCUS:
   1699                 if (mFirstTimeInitialized) {
   1700                     onShutterButtonFocus(false);
   1701                 }
   1702                 return true;
   1703         }
   1704         return false;
   1705     }
   1706 
   1707     private void closeCamera() {
   1708         if (mCameraDevice != null) {
   1709             stopFaceDetection();
   1710             mCameraDevice.setZoomChangeListener(null);
   1711             mCameraDevice.setFaceDetectionCallback(null, null);
   1712 
   1713             mFaceDetectionStarted = false;
   1714             mActivity.getCameraProvider().releaseCamera(mCameraDevice.getCameraId());
   1715             mCameraDevice = null;
   1716             setCameraState(PREVIEW_STOPPED);
   1717             mFocusManager.onCameraReleased();
   1718         }
   1719     }
   1720 
   1721     private void setDisplayOrientation() {
   1722         mDisplayRotation = CameraUtil.getDisplayRotation();
   1723         Characteristics info =
   1724                 mActivity.getCameraProvider().getCharacteristics(mCameraId);
   1725         mDisplayOrientation = info.getPreviewOrientation(mDisplayRotation);
   1726         mUI.setDisplayOrientation(mDisplayOrientation);
   1727         if (mFocusManager != null) {
   1728             mFocusManager.setDisplayOrientation(mDisplayOrientation);
   1729         }
   1730         // Change the camera display orientation
   1731         if (mCameraDevice != null) {
   1732             mCameraDevice.setDisplayOrientation(mDisplayRotation);
   1733         }
   1734         Log.v(TAG, "setDisplayOrientation (screen:preview) " +
   1735                 mDisplayRotation + ":" + mDisplayOrientation);
   1736     }
   1737 
   1738     /** Only called by UI thread. */
   1739     private void setupPreview() {
   1740         Log.i(TAG, "setupPreview");
   1741         mFocusManager.resetTouchFocus();
   1742         startPreview();
   1743     }
   1744 
   1745     /**
   1746      * Returns whether we can/should start the preview or not.
   1747      */
   1748     private boolean checkPreviewPreconditions() {
   1749         if (mPaused) {
   1750             return false;
   1751         }
   1752 
   1753         if (mCameraDevice == null) {
   1754             Log.w(TAG, "startPreview: camera device not ready yet.");
   1755             return false;
   1756         }
   1757 
   1758         SurfaceTexture st = mActivity.getCameraAppUI().getSurfaceTexture();
   1759         if (st == null) {
   1760             Log.w(TAG, "startPreview: surfaceTexture is not ready.");
   1761             return false;
   1762         }
   1763 
   1764         if (!mCameraPreviewParamsReady) {
   1765             Log.w(TAG, "startPreview: parameters for preview is not ready.");
   1766             return false;
   1767         }
   1768         return true;
   1769     }
   1770 
   1771     /**
   1772      * The start/stop preview should only run on the UI thread.
   1773      */
   1774     private void startPreview() {
   1775         if (mCameraDevice == null) {
   1776             Log.i(TAG, "attempted to start preview before camera device");
   1777             // do nothing
   1778             return;
   1779         }
   1780 
   1781         if (!checkPreviewPreconditions()) {
   1782             return;
   1783         }
   1784 
   1785         setDisplayOrientation();
   1786 
   1787         if (!mSnapshotOnIdle) {
   1788             // If the focus mode is continuous autofocus, call cancelAutoFocus
   1789             // to resume it because it may have been paused by autoFocus call.
   1790             if (mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()) ==
   1791                     CameraCapabilities.FocusMode.CONTINUOUS_PICTURE) {
   1792                 mCameraDevice.cancelAutoFocus();
   1793             }
   1794             mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
   1795         }
   1796 
   1797         // Nexus 4 must have picture size set to > 640x480 before other
   1798         // parameters are set in setCameraParameters, b/18227551. This call to
   1799         // updateParametersPictureSize should occur before setCameraParameters
   1800         // to address the issue.
   1801         updateParametersPictureSize();
   1802 
   1803         setCameraParameters(UPDATE_PARAM_ALL);
   1804 
   1805         mCameraDevice.setPreviewTexture(mActivity.getCameraAppUI().getSurfaceTexture());
   1806 
   1807         Log.i(TAG, "startPreview");
   1808         // If we're using API2 in portability layers, don't use startPreviewWithCallback()
   1809         // b/17576554
   1810         CameraAgent.CameraStartPreviewCallback startPreviewCallback =
   1811             new CameraAgent.CameraStartPreviewCallback() {
   1812                 @Override
   1813                 public void onPreviewStarted() {
   1814                     mFocusManager.onPreviewStarted();
   1815                     PhotoModule.this.onPreviewStarted();
   1816                     SessionStatsCollector.instance().previewActive(true);
   1817                     if (mSnapshotOnIdle) {
   1818                         mHandler.post(mDoSnapRunnable);
   1819                     }
   1820                 }
   1821             };
   1822         if (GservicesHelper.useCamera2ApiThroughPortabilityLayer(mActivity.getContentResolver())) {
   1823             mCameraDevice.startPreview();
   1824             startPreviewCallback.onPreviewStarted();
   1825         } else {
   1826             mCameraDevice.startPreviewWithCallback(new Handler(Looper.getMainLooper()),
   1827                     startPreviewCallback);
   1828         }
   1829     }
   1830 
   1831     @Override
   1832     public void stopPreview() {
   1833         if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
   1834             Log.i(TAG, "stopPreview");
   1835             mCameraDevice.stopPreview();
   1836             mFaceDetectionStarted = false;
   1837         }
   1838         setCameraState(PREVIEW_STOPPED);
   1839         if (mFocusManager != null) {
   1840             mFocusManager.onPreviewStopped();
   1841         }
   1842         SessionStatsCollector.instance().previewActive(false);
   1843     }
   1844 
   1845     @Override
   1846     public void onSettingChanged(SettingsManager settingsManager, String key) {
   1847         if (key.equals(Keys.KEY_FLASH_MODE)) {
   1848             updateParametersFlashMode();
   1849         }
   1850         if (key.equals(Keys.KEY_CAMERA_HDR)) {
   1851             if (settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
   1852                                            Keys.KEY_CAMERA_HDR)) {
   1853                 // HDR is on.
   1854                 mAppController.getButtonManager().disableButton(ButtonManager.BUTTON_FLASH);
   1855                 mFlashModeBeforeSceneMode = settingsManager.getString(
   1856                         mAppController.getCameraScope(), Keys.KEY_FLASH_MODE);
   1857             } else {
   1858                 if (mFlashModeBeforeSceneMode != null) {
   1859                     settingsManager.set(mAppController.getCameraScope(),
   1860                                         Keys.KEY_FLASH_MODE,
   1861                                         mFlashModeBeforeSceneMode);
   1862                     updateParametersFlashMode();
   1863                     mFlashModeBeforeSceneMode = null;
   1864                 }
   1865                 mAppController.getButtonManager().enableButton(ButtonManager.BUTTON_FLASH);
   1866             }
   1867         }
   1868 
   1869         if (mCameraDevice != null) {
   1870             mCameraDevice.applySettings(mCameraSettings);
   1871         }
   1872     }
   1873 
   1874     private void updateCameraParametersInitialize() {
   1875         // Reset preview frame rate to the maximum because it may be lowered by
   1876         // video camera application.
   1877         int[] fpsRange = CameraUtil.getPhotoPreviewFpsRange(mCameraCapabilities);
   1878         if (fpsRange != null && fpsRange.length > 0) {
   1879             mCameraSettings.setPreviewFpsRange(fpsRange[0], fpsRange[1]);
   1880         }
   1881 
   1882         mCameraSettings.setRecordingHintEnabled(false);
   1883 
   1884         if (mCameraCapabilities.supports(CameraCapabilities.Feature.VIDEO_STABILIZATION)) {
   1885             mCameraSettings.setVideoStabilization(false);
   1886         }
   1887     }
   1888 
   1889     private void updateCameraParametersZoom() {
   1890         // Set zoom.
   1891         if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
   1892             mCameraSettings.setZoomRatio(mZoomValue);
   1893         }
   1894     }
   1895 
   1896     @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
   1897     private void setAutoExposureLockIfSupported() {
   1898         if (mAeLockSupported) {
   1899             mCameraSettings.setAutoExposureLock(mFocusManager.getAeAwbLock());
   1900         }
   1901     }
   1902 
   1903     @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
   1904     private void setAutoWhiteBalanceLockIfSupported() {
   1905         if (mAwbLockSupported) {
   1906             mCameraSettings.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
   1907         }
   1908     }
   1909 
   1910     private void setFocusAreasIfSupported() {
   1911         if (mFocusAreaSupported) {
   1912             mCameraSettings.setFocusAreas(mFocusManager.getFocusAreas());
   1913         }
   1914     }
   1915 
   1916     private void setMeteringAreasIfSupported() {
   1917         if (mMeteringAreaSupported) {
   1918             mCameraSettings.setMeteringAreas(mFocusManager.getMeteringAreas());
   1919         }
   1920     }
   1921 
   1922     private void updateCameraParametersPreference() {
   1923         // some monkey tests can get here when shutting the app down
   1924         // make sure mCameraDevice is still valid, b/17580046
   1925         if (mCameraDevice == null) {
   1926             return;
   1927         }
   1928 
   1929         setAutoExposureLockIfSupported();
   1930         setAutoWhiteBalanceLockIfSupported();
   1931         setFocusAreasIfSupported();
   1932         setMeteringAreasIfSupported();
   1933 
   1934         // Initialize focus mode.
   1935         mFocusManager.overrideFocusMode(null);
   1936         mCameraSettings
   1937                 .setFocusMode(mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()));
   1938         SessionStatsCollector.instance().autofocusActive(
   1939                 mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()) ==
   1940                         CameraCapabilities.FocusMode.CONTINUOUS_PICTURE
   1941         );
   1942 
   1943         // Set JPEG quality.
   1944         updateParametersPictureQuality();
   1945 
   1946         // For the following settings, we need to check if the settings are
   1947         // still supported by latest driver, if not, ignore the settings.
   1948 
   1949         // Set exposure compensation
   1950         updateParametersExposureCompensation();
   1951 
   1952         // Set the scene mode: also sets flash and white balance.
   1953         updateParametersSceneMode();
   1954 
   1955         if (mContinuousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
   1956             updateAutoFocusMoveCallback();
   1957         }
   1958     }
   1959 
   1960     /**
   1961      * This method sets picture size parameters. Size parameters should only be
   1962      * set when the preview is stopped, and so this method is only invoked in
   1963      * {@link #startPreview()} just before starting the preview.
   1964      */
   1965     private void updateParametersPictureSize() {
   1966         if (mCameraDevice == null) {
   1967             Log.w(TAG, "attempting to set picture size without caemra device");
   1968             return;
   1969         }
   1970 
   1971         List<Size> supported = Size.convert(mCameraCapabilities.getSupportedPhotoSizes());
   1972         CameraPictureSizesCacher.updateSizesForCamera(mAppController.getAndroidContext(),
   1973                 mCameraDevice.getCameraId(), supported);
   1974 
   1975         OneCamera.Facing cameraFacing =
   1976               isCameraFrontFacing() ? OneCamera.Facing.FRONT : OneCamera.Facing.BACK;
   1977         Size pictureSize;
   1978         try {
   1979             pictureSize = mAppController.getResolutionSetting().getPictureSize(
   1980                   mAppController.getCameraProvider().getCurrentCameraId(),
   1981                   cameraFacing);
   1982         } catch (OneCameraAccessException ex) {
   1983             mAppController.getFatalErrorHandler().onGenericCameraAccessFailure();
   1984             return;
   1985         }
   1986 
   1987         mCameraSettings.setPhotoSize(pictureSize.toPortabilitySize());
   1988 
   1989         if (ApiHelper.IS_NEXUS_5) {
   1990             if (ResolutionUtil.NEXUS_5_LARGE_16_BY_9.equals(pictureSize)) {
   1991                 mShouldResizeTo16x9 = true;
   1992             } else {
   1993                 mShouldResizeTo16x9 = false;
   1994             }
   1995         }
   1996 
   1997         // Set a preview size that is closest to the viewfinder height and has
   1998         // the right aspect ratio.
   1999         List<Size> sizes = Size.convert(mCameraCapabilities.getSupportedPreviewSizes());
   2000         Size optimalSize = CameraUtil.getOptimalPreviewSize(sizes,
   2001                 (double) pictureSize.width() / pictureSize.height());
   2002         Size original = new Size(mCameraSettings.getCurrentPreviewSize());
   2003         if (!optimalSize.equals(original)) {
   2004             Log.v(TAG, "setting preview size. optimal: " + optimalSize + "original: " + original);
   2005             mCameraSettings.setPreviewSize(optimalSize.toPortabilitySize());
   2006 
   2007             mCameraDevice.applySettings(mCameraSettings);
   2008             mCameraSettings = mCameraDevice.getSettings();
   2009         }
   2010 
   2011         if (optimalSize.width() != 0 && optimalSize.height() != 0) {
   2012             Log.v(TAG, "updating aspect ratio");
   2013             mUI.updatePreviewAspectRatio((float) optimalSize.width()
   2014                     / (float) optimalSize.height());
   2015         }
   2016         Log.d(TAG, "Preview size is " + optimalSize);
   2017     }
   2018 
   2019     private void updateParametersPictureQuality() {
   2020         int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
   2021                 CameraProfile.QUALITY_HIGH);
   2022         mCameraSettings.setPhotoJpegCompressionQuality(jpegQuality);
   2023     }
   2024 
   2025     private void updateParametersExposureCompensation() {
   2026         SettingsManager settingsManager = mActivity.getSettingsManager();
   2027         if (settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
   2028                                        Keys.KEY_EXPOSURE_COMPENSATION_ENABLED)) {
   2029             int value = settingsManager.getInteger(mAppController.getCameraScope(),
   2030                                                    Keys.KEY_EXPOSURE);
   2031             int max = mCameraCapabilities.getMaxExposureCompensation();
   2032             int min = mCameraCapabilities.getMinExposureCompensation();
   2033             if (value >= min && value <= max) {
   2034                 mCameraSettings.setExposureCompensationIndex(value);
   2035             } else {
   2036                 Log.w(TAG, "invalid exposure range: " + value);
   2037             }
   2038         } else {
   2039             // If exposure compensation is not enabled, reset the exposure compensation value.
   2040             setExposureCompensation(0);
   2041         }
   2042     }
   2043 
   2044     private void updateParametersSceneMode() {
   2045         CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
   2046         SettingsManager settingsManager = mActivity.getSettingsManager();
   2047 
   2048         mSceneMode = stringifier.
   2049             sceneModeFromString(settingsManager.getString(mAppController.getCameraScope(),
   2050                                                           Keys.KEY_SCENE_MODE));
   2051         if (mCameraCapabilities.supports(mSceneMode)) {
   2052             if (mCameraSettings.getCurrentSceneMode() != mSceneMode) {
   2053                 mCameraSettings.setSceneMode(mSceneMode);
   2054 
   2055                 // Setting scene mode will change the settings of flash mode,
   2056                 // white balance, and focus mode. Here we read back the
   2057                 // parameters, so we can know those settings.
   2058                 mCameraDevice.applySettings(mCameraSettings);
   2059                 mCameraSettings = mCameraDevice.getSettings();
   2060             }
   2061         } else {
   2062             mSceneMode = mCameraSettings.getCurrentSceneMode();
   2063             if (mSceneMode == null) {
   2064                 mSceneMode = CameraCapabilities.SceneMode.AUTO;
   2065             }
   2066         }
   2067 
   2068         if (CameraCapabilities.SceneMode.AUTO == mSceneMode) {
   2069             // Set flash mode.
   2070             updateParametersFlashMode();
   2071 
   2072             // Set focus mode.
   2073             mFocusManager.overrideFocusMode(null);
   2074             mCameraSettings.setFocusMode(
   2075                     mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()));
   2076         } else {
   2077             mFocusManager.overrideFocusMode(mCameraSettings.getCurrentFocusMode());
   2078         }
   2079     }
   2080 
   2081     private void updateParametersFlashMode() {
   2082         SettingsManager settingsManager = mActivity.getSettingsManager();
   2083 
   2084         CameraCapabilities.FlashMode flashMode = mCameraCapabilities.getStringifier()
   2085             .flashModeFromString(settingsManager.getString(mAppController.getCameraScope(),
   2086                                                            Keys.KEY_FLASH_MODE));
   2087         if (mCameraCapabilities.supports(flashMode)) {
   2088             mCameraSettings.setFlashMode(flashMode);
   2089         }
   2090     }
   2091 
   2092     @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
   2093     private void updateAutoFocusMoveCallback() {
   2094         if (mCameraDevice == null) {
   2095             return;
   2096         }
   2097         if (mCameraSettings.getCurrentFocusMode() ==
   2098                 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE) {
   2099             mCameraDevice.setAutoFocusMoveCallback(mHandler,
   2100                     (CameraAFMoveCallback) mAutoFocusMoveCallback);
   2101         } else {
   2102             mCameraDevice.setAutoFocusMoveCallback(null, null);
   2103         }
   2104     }
   2105 
   2106     /**
   2107      * Sets the exposure compensation to the given value and also updates settings.
   2108      *
   2109      * @param value exposure compensation value to be set
   2110      */
   2111     public void setExposureCompensation(int value) {
   2112         int max = mCameraCapabilities.getMaxExposureCompensation();
   2113         int min = mCameraCapabilities.getMinExposureCompensation();
   2114         if (value >= min && value <= max) {
   2115             mCameraSettings.setExposureCompensationIndex(value);
   2116             SettingsManager settingsManager = mActivity.getSettingsManager();
   2117             settingsManager.set(mAppController.getCameraScope(),
   2118                                 Keys.KEY_EXPOSURE, value);
   2119         } else {
   2120             Log.w(TAG, "invalid exposure range: " + value);
   2121         }
   2122     }
   2123 
   2124     // We separate the parameters into several subsets, so we can update only
   2125     // the subsets actually need updating. The PREFERENCE set needs extra
   2126     // locking because the preference can be changed from GLThread as well.
   2127     private void setCameraParameters(int updateSet) {
   2128         if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
   2129             updateCameraParametersInitialize();
   2130         }
   2131 
   2132         if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
   2133             updateCameraParametersZoom();
   2134         }
   2135 
   2136         if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
   2137             updateCameraParametersPreference();
   2138         }
   2139 
   2140         if (mCameraDevice != null) {
   2141             mCameraDevice.applySettings(mCameraSettings);
   2142         }
   2143     }
   2144 
   2145     // If the Camera is idle, update the parameters immediately, otherwise
   2146     // accumulate them in mUpdateSet and update later.
   2147     private void setCameraParametersWhenIdle(int additionalUpdateSet) {
   2148         mUpdateSet |= additionalUpdateSet;
   2149         if (mCameraDevice == null) {
   2150             // We will update all the parameters when we open the device, so
   2151             // we don't need to do anything now.
   2152             mUpdateSet = 0;
   2153             return;
   2154         } else if (isCameraIdle()) {
   2155             setCameraParameters(mUpdateSet);
   2156             updateSceneMode();
   2157             mUpdateSet = 0;
   2158         } else {
   2159             if (!mHandler.hasMessages(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
   2160                 mHandler.sendEmptyMessageDelayed(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
   2161             }
   2162         }
   2163     }
   2164 
   2165     @Override
   2166     public boolean isCameraIdle() {
   2167         return (mCameraState == IDLE) ||
   2168                 (mCameraState == PREVIEW_STOPPED) ||
   2169                 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
   2170                 && (mCameraState != SWITCHING_CAMERA));
   2171     }
   2172 
   2173     @Override
   2174     public boolean isImageCaptureIntent() {
   2175         String action = mActivity.getIntent().getAction();
   2176         return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
   2177         || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
   2178     }
   2179 
   2180     private void setupCaptureParams() {
   2181         Bundle myExtras = mActivity.getIntent().getExtras();
   2182         if (myExtras != null) {
   2183             mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
   2184             mCropValue = myExtras.getString("crop");
   2185         }
   2186     }
   2187 
   2188     private void initializeCapabilities() {
   2189         mCameraCapabilities = mCameraDevice.getCapabilities();
   2190         mFocusAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA);
   2191         mMeteringAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.METERING_AREA);
   2192         mAeLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK);
   2193         mAwbLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK);
   2194         mContinuousFocusSupported =
   2195                 mCameraCapabilities.supports(CameraCapabilities.FocusMode.CONTINUOUS_PICTURE);
   2196     }
   2197 
   2198     @Override
   2199     public void onZoomChanged(float ratio) {
   2200         // Not useful to change zoom value when the activity is paused.
   2201         if (mPaused) {
   2202             return;
   2203         }
   2204         mZoomValue = ratio;
   2205         if (mCameraSettings == null || mCameraDevice == null) {
   2206             return;
   2207         }
   2208         // Set zoom parameters asynchronously
   2209         mCameraSettings.setZoomRatio(mZoomValue);
   2210         mCameraDevice.applySettings(mCameraSettings);
   2211     }
   2212 
   2213     @Override
   2214     public int getCameraState() {
   2215         return mCameraState;
   2216     }
   2217 
   2218     @Override
   2219     public void onMemoryStateChanged(int state) {
   2220         mAppController.setShutterEnabled(state == MemoryManager.STATE_OK);
   2221     }
   2222 
   2223     @Override
   2224     public void onLowMemory() {
   2225         // Not much we can do in the photo module.
   2226     }
   2227 
   2228     // For debugging only.
   2229     public void setDebugUri(Uri uri) {
   2230         mDebugUri = uri;
   2231     }
   2232 
   2233     // For debugging only.
   2234     private void saveToDebugUri(byte[] data) {
   2235         if (mDebugUri != null) {
   2236             OutputStream outputStream = null;
   2237             try {
   2238                 outputStream = mContentResolver.openOutputStream(mDebugUri);
   2239                 outputStream.write(data);
   2240                 outputStream.close();
   2241             } catch (IOException e) {
   2242                 Log.e(TAG, "Exception while writing debug jpeg file", e);
   2243             } finally {
   2244                 CameraUtil.closeSilently(outputStream);
   2245             }
   2246         }
   2247     }
   2248 
   2249     @Override
   2250     public void onRemoteShutterPress() {
   2251         mHandler.post(new Runnable() {
   2252             @Override
   2253             public void run() {
   2254                 focusAndCapture();
   2255             }
   2256         });
   2257     }
   2258 }
   2259