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         // Close the review UI if it's currently visible.
   1459         mUI.hidePostCaptureAlert();
   1460         mUI.hideIntentReviewImageView();
   1461 
   1462         requestCameraOpen();
   1463 
   1464         mJpegPictureCallbackTime = 0;
   1465         mZoomValue = 1.0f;
   1466 
   1467         mOnResumeTime = SystemClock.uptimeMillis();
   1468         checkDisplayRotation();
   1469 
   1470         // If first time initialization is not finished, put it in the
   1471         // message queue.
   1472         if (!mFirstTimeInitialized) {
   1473             mHandler.sendEmptyMessage(MSG_FIRST_TIME_INIT);
   1474         } else {
   1475             initializeSecondTime();
   1476         }
   1477 
   1478         mHeadingSensor.activate();
   1479 
   1480         getServices().getRemoteShutterListener().onModuleReady(this);
   1481         SessionStatsCollector.instance().sessionActive(true);
   1482     }
   1483 
   1484     /**
   1485      * @return Whether the currently active camera is front-facing.
   1486      */
   1487     private boolean isCameraFrontFacing() {
   1488         return mAppController.getCameraProvider().getCharacteristics(mCameraId)
   1489                 .isFacingFront();
   1490     }
   1491 
   1492     /**
   1493      * The focus manager is the first UI related element to get initialized, and
   1494      * it requires the RenderOverlay, so initialize it here
   1495      */
   1496     private void initializeFocusManager() {
   1497         // Create FocusManager object. startPreview needs it.
   1498         // if mFocusManager not null, reuse it
   1499         // otherwise create a new instance
   1500         if (mFocusManager != null) {
   1501             mFocusManager.removeMessages();
   1502         } else {
   1503             mMirror = isCameraFrontFacing();
   1504             String[] defaultFocusModesStrings = mActivity.getResources().getStringArray(
   1505                     R.array.pref_camera_focusmode_default_array);
   1506             ArrayList<CameraCapabilities.FocusMode> defaultFocusModes =
   1507                     new ArrayList<CameraCapabilities.FocusMode>();
   1508             CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
   1509             for (String modeString : defaultFocusModesStrings) {
   1510                 CameraCapabilities.FocusMode mode = stringifier.focusModeFromString(modeString);
   1511                 if (mode != null) {
   1512                     defaultFocusModes.add(mode);
   1513                 }
   1514             }
   1515             mFocusManager =
   1516                     new FocusOverlayManager(mAppController, defaultFocusModes,
   1517                             mCameraCapabilities, this, mMirror, mActivity.getMainLooper(),
   1518                             mUI.getFocusRing());
   1519             mMotionManager = getServices().getMotionManager();
   1520             if (mMotionManager != null) {
   1521                 mMotionManager.addListener(mFocusManager);
   1522             }
   1523         }
   1524         mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
   1525     }
   1526 
   1527     /**
   1528      * @return Whether we are resuming from within the lockscreen.
   1529      */
   1530     private boolean isResumeFromLockscreen() {
   1531         String action = mActivity.getIntent().getAction();
   1532         return (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action)
   1533                 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action));
   1534     }
   1535 
   1536     @Override
   1537     public void pause() {
   1538         Log.v(TAG, "pause");
   1539         mPaused = true;
   1540         getServices().getRemoteShutterListener().onModuleExit();
   1541         SessionStatsCollector.instance().sessionActive(false);
   1542 
   1543         mHeadingSensor.deactivate();
   1544 
   1545         // Reset the focus first. Camera CTS does not guarantee that
   1546         // cancelAutoFocus is allowed after preview stops.
   1547         if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
   1548             mCameraDevice.cancelAutoFocus();
   1549         }
   1550 
   1551         // If the camera has not been opened asynchronously yet,
   1552         // and startPreview hasn't been called, then this is a no-op.
   1553         // (e.g. onResume -> onPause -> onResume).
   1554         stopPreview();
   1555         cancelCountDown();
   1556         mCountdownSoundPlayer.unloadSound(R.raw.timer_final_second);
   1557         mCountdownSoundPlayer.unloadSound(R.raw.timer_increment);
   1558 
   1559         mNamedImages = null;
   1560         // If we are in an image capture intent and has taken
   1561         // a picture, we just clear it in onPause.
   1562         mJpegImageData = null;
   1563 
   1564         // Remove the messages and runnables in the queue.
   1565         mHandler.removeCallbacksAndMessages(null);
   1566 
   1567         if (mMotionManager != null) {
   1568             mMotionManager.removeListener(mFocusManager);
   1569             mMotionManager = null;
   1570         }
   1571 
   1572         closeCamera();
   1573         mActivity.enableKeepScreenOn(false);
   1574         mUI.onPause();
   1575 
   1576         mPendingSwitchCameraId = -1;
   1577         if (mFocusManager != null) {
   1578             mFocusManager.removeMessages();
   1579         }
   1580         getServices().getMemoryManager().removeListener(this);
   1581         mAppController.removePreviewAreaSizeChangedListener(mFocusManager);
   1582         mAppController.removePreviewAreaSizeChangedListener(mUI);
   1583 
   1584         SettingsManager settingsManager = mActivity.getSettingsManager();
   1585         settingsManager.removeListener(this);
   1586     }
   1587 
   1588     @Override
   1589     public void destroy() {
   1590         mCountdownSoundPlayer.release();
   1591     }
   1592 
   1593     @Override
   1594     public void onLayoutOrientationChanged(boolean isLandscape) {
   1595         setDisplayOrientation();
   1596     }
   1597 
   1598     @Override
   1599     public void updateCameraOrientation() {
   1600         if (mDisplayRotation != CameraUtil.getDisplayRotation()) {
   1601             setDisplayOrientation();
   1602         }
   1603     }
   1604 
   1605     private boolean canTakePicture() {
   1606         return isCameraIdle()
   1607                 && (mActivity.getStorageSpaceBytes() > Storage.LOW_STORAGE_THRESHOLD_BYTES);
   1608     }
   1609 
   1610     @Override
   1611     public void autoFocus() {
   1612         if (mCameraDevice == null) {
   1613             return;
   1614         }
   1615         Log.v(TAG,"Starting auto focus");
   1616         mFocusStartTime = System.currentTimeMillis();
   1617         mCameraDevice.autoFocus(mHandler, mAutoFocusCallback);
   1618         SessionStatsCollector.instance().autofocusManualTrigger();
   1619         setCameraState(FOCUSING);
   1620     }
   1621 
   1622     @Override
   1623     public void cancelAutoFocus() {
   1624         if (mCameraDevice == null) {
   1625             return;
   1626         }
   1627         mCameraDevice.cancelAutoFocus();
   1628         setCameraState(IDLE);
   1629         setCameraParameters(UPDATE_PARAM_PREFERENCE);
   1630     }
   1631 
   1632     @Override
   1633     public void onSingleTapUp(View view, int x, int y) {
   1634         if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
   1635                 || mCameraState == SNAPSHOT_IN_PROGRESS
   1636                 || mCameraState == SWITCHING_CAMERA
   1637                 || mCameraState == PREVIEW_STOPPED) {
   1638             return;
   1639         }
   1640 
   1641         // Check if metering area or focus area is supported.
   1642         if (!mFocusAreaSupported && !mMeteringAreaSupported) {
   1643             return;
   1644         }
   1645         mFocusManager.onSingleTapUp(x, y);
   1646     }
   1647 
   1648     @Override
   1649     public boolean onBackPressed() {
   1650         return mUI.onBackPressed();
   1651     }
   1652 
   1653     @Override
   1654     public boolean onKeyDown(int keyCode, KeyEvent event) {
   1655         switch (keyCode) {
   1656             case KeyEvent.KEYCODE_VOLUME_UP:
   1657             case KeyEvent.KEYCODE_VOLUME_DOWN:
   1658             case KeyEvent.KEYCODE_FOCUS:
   1659                 if (/* TODO: mActivity.isInCameraApp() && */mFirstTimeInitialized &&
   1660                     !mActivity.getCameraAppUI().isInIntentReview()) {
   1661                     if (event.getRepeatCount() == 0) {
   1662                         onShutterButtonFocus(true);
   1663                     }
   1664                     return true;
   1665                 }
   1666                 return false;
   1667             case KeyEvent.KEYCODE_CAMERA:
   1668                 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
   1669                     onShutterButtonClick();
   1670                 }
   1671                 return true;
   1672             case KeyEvent.KEYCODE_DPAD_CENTER:
   1673                 // If we get a dpad center event without any focused view, move
   1674                 // the focus to the shutter button and press it.
   1675                 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
   1676                     // Start auto-focus immediately to reduce shutter lag. After
   1677                     // the shutter button gets the focus, onShutterButtonFocus()
   1678                     // will be called again but it is fine.
   1679                     onShutterButtonFocus(true);
   1680                 }
   1681                 return true;
   1682         }
   1683         return false;
   1684     }
   1685 
   1686     @Override
   1687     public boolean onKeyUp(int keyCode, KeyEvent event) {
   1688         switch (keyCode) {
   1689             case KeyEvent.KEYCODE_VOLUME_UP:
   1690             case KeyEvent.KEYCODE_VOLUME_DOWN:
   1691                 if (/* mActivity.isInCameraApp() && */mFirstTimeInitialized &&
   1692                     !mActivity.getCameraAppUI().isInIntentReview()) {
   1693                     if (mUI.isCountingDown()) {
   1694                         cancelCountDown();
   1695                     } else {
   1696                         mVolumeButtonClickedFlag = true;
   1697                         onShutterButtonClick();
   1698                     }
   1699                     return true;
   1700                 }
   1701                 return false;
   1702             case KeyEvent.KEYCODE_FOCUS:
   1703                 if (mFirstTimeInitialized) {
   1704                     onShutterButtonFocus(false);
   1705                 }
   1706                 return true;
   1707         }
   1708         return false;
   1709     }
   1710 
   1711     private void closeCamera() {
   1712         if (mCameraDevice != null) {
   1713             stopFaceDetection();
   1714             mCameraDevice.setZoomChangeListener(null);
   1715             mCameraDevice.setFaceDetectionCallback(null, null);
   1716 
   1717             mFaceDetectionStarted = false;
   1718             mActivity.getCameraProvider().releaseCamera(mCameraDevice.getCameraId());
   1719             mCameraDevice = null;
   1720             setCameraState(PREVIEW_STOPPED);
   1721             mFocusManager.onCameraReleased();
   1722         }
   1723     }
   1724 
   1725     private void setDisplayOrientation() {
   1726         mDisplayRotation = CameraUtil.getDisplayRotation();
   1727         Characteristics info =
   1728                 mActivity.getCameraProvider().getCharacteristics(mCameraId);
   1729         mDisplayOrientation = info.getPreviewOrientation(mDisplayRotation);
   1730         mUI.setDisplayOrientation(mDisplayOrientation);
   1731         if (mFocusManager != null) {
   1732             mFocusManager.setDisplayOrientation(mDisplayOrientation);
   1733         }
   1734         // Change the camera display orientation
   1735         if (mCameraDevice != null) {
   1736             mCameraDevice.setDisplayOrientation(mDisplayRotation);
   1737         }
   1738         Log.v(TAG, "setDisplayOrientation (screen:preview) " +
   1739                 mDisplayRotation + ":" + mDisplayOrientation);
   1740     }
   1741 
   1742     /** Only called by UI thread. */
   1743     private void setupPreview() {
   1744         Log.i(TAG, "setupPreview");
   1745         mFocusManager.resetTouchFocus();
   1746         startPreview();
   1747     }
   1748 
   1749     /**
   1750      * Returns whether we can/should start the preview or not.
   1751      */
   1752     private boolean checkPreviewPreconditions() {
   1753         if (mPaused) {
   1754             return false;
   1755         }
   1756 
   1757         if (mCameraDevice == null) {
   1758             Log.w(TAG, "startPreview: camera device not ready yet.");
   1759             return false;
   1760         }
   1761 
   1762         SurfaceTexture st = mActivity.getCameraAppUI().getSurfaceTexture();
   1763         if (st == null) {
   1764             Log.w(TAG, "startPreview: surfaceTexture is not ready.");
   1765             return false;
   1766         }
   1767 
   1768         if (!mCameraPreviewParamsReady) {
   1769             Log.w(TAG, "startPreview: parameters for preview is not ready.");
   1770             return false;
   1771         }
   1772         return true;
   1773     }
   1774 
   1775     /**
   1776      * The start/stop preview should only run on the UI thread.
   1777      */
   1778     private void startPreview() {
   1779         if (mCameraDevice == null) {
   1780             Log.i(TAG, "attempted to start preview before camera device");
   1781             // do nothing
   1782             return;
   1783         }
   1784 
   1785         if (!checkPreviewPreconditions()) {
   1786             return;
   1787         }
   1788 
   1789         setDisplayOrientation();
   1790 
   1791         if (!mSnapshotOnIdle) {
   1792             // If the focus mode is continuous autofocus, call cancelAutoFocus
   1793             // to resume it because it may have been paused by autoFocus call.
   1794             if (mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()) ==
   1795                     CameraCapabilities.FocusMode.CONTINUOUS_PICTURE) {
   1796                 mCameraDevice.cancelAutoFocus();
   1797             }
   1798             mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
   1799         }
   1800 
   1801         // Nexus 4 must have picture size set to > 640x480 before other
   1802         // parameters are set in setCameraParameters, b/18227551. This call to
   1803         // updateParametersPictureSize should occur before setCameraParameters
   1804         // to address the issue.
   1805         updateParametersPictureSize();
   1806 
   1807         setCameraParameters(UPDATE_PARAM_ALL);
   1808 
   1809         mCameraDevice.setPreviewTexture(mActivity.getCameraAppUI().getSurfaceTexture());
   1810 
   1811         Log.i(TAG, "startPreview");
   1812         // If we're using API2 in portability layers, don't use startPreviewWithCallback()
   1813         // b/17576554
   1814         CameraAgent.CameraStartPreviewCallback startPreviewCallback =
   1815             new CameraAgent.CameraStartPreviewCallback() {
   1816                 @Override
   1817                 public void onPreviewStarted() {
   1818                     mFocusManager.onPreviewStarted();
   1819                     PhotoModule.this.onPreviewStarted();
   1820                     SessionStatsCollector.instance().previewActive(true);
   1821                     if (mSnapshotOnIdle) {
   1822                         mHandler.post(mDoSnapRunnable);
   1823                     }
   1824                 }
   1825             };
   1826         if (GservicesHelper.useCamera2ApiThroughPortabilityLayer(mActivity.getContentResolver())) {
   1827             mCameraDevice.startPreview();
   1828             startPreviewCallback.onPreviewStarted();
   1829         } else {
   1830             mCameraDevice.startPreviewWithCallback(new Handler(Looper.getMainLooper()),
   1831                     startPreviewCallback);
   1832         }
   1833     }
   1834 
   1835     @Override
   1836     public void stopPreview() {
   1837         if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
   1838             Log.i(TAG, "stopPreview");
   1839             mCameraDevice.stopPreview();
   1840             mFaceDetectionStarted = false;
   1841         }
   1842         setCameraState(PREVIEW_STOPPED);
   1843         if (mFocusManager != null) {
   1844             mFocusManager.onPreviewStopped();
   1845         }
   1846         SessionStatsCollector.instance().previewActive(false);
   1847     }
   1848 
   1849     @Override
   1850     public void onSettingChanged(SettingsManager settingsManager, String key) {
   1851         if (key.equals(Keys.KEY_FLASH_MODE)) {
   1852             updateParametersFlashMode();
   1853         }
   1854         if (key.equals(Keys.KEY_CAMERA_HDR)) {
   1855             if (settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
   1856                                            Keys.KEY_CAMERA_HDR)) {
   1857                 // HDR is on.
   1858                 mAppController.getButtonManager().disableButton(ButtonManager.BUTTON_FLASH);
   1859                 mFlashModeBeforeSceneMode = settingsManager.getString(
   1860                         mAppController.getCameraScope(), Keys.KEY_FLASH_MODE);
   1861             } else {
   1862                 if (mFlashModeBeforeSceneMode != null) {
   1863                     settingsManager.set(mAppController.getCameraScope(),
   1864                                         Keys.KEY_FLASH_MODE,
   1865                                         mFlashModeBeforeSceneMode);
   1866                     updateParametersFlashMode();
   1867                     mFlashModeBeforeSceneMode = null;
   1868                 }
   1869                 mAppController.getButtonManager().enableButton(ButtonManager.BUTTON_FLASH);
   1870             }
   1871         }
   1872 
   1873         if (mCameraDevice != null) {
   1874             mCameraDevice.applySettings(mCameraSettings);
   1875         }
   1876     }
   1877 
   1878     private void updateCameraParametersInitialize() {
   1879         // Reset preview frame rate to the maximum because it may be lowered by
   1880         // video camera application.
   1881         int[] fpsRange = CameraUtil.getPhotoPreviewFpsRange(mCameraCapabilities);
   1882         if (fpsRange != null && fpsRange.length > 0) {
   1883             mCameraSettings.setPreviewFpsRange(fpsRange[0], fpsRange[1]);
   1884         }
   1885 
   1886         mCameraSettings.setRecordingHintEnabled(false);
   1887 
   1888         if (mCameraCapabilities.supports(CameraCapabilities.Feature.VIDEO_STABILIZATION)) {
   1889             mCameraSettings.setVideoStabilization(false);
   1890         }
   1891     }
   1892 
   1893     private void updateCameraParametersZoom() {
   1894         // Set zoom.
   1895         if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
   1896             mCameraSettings.setZoomRatio(mZoomValue);
   1897         }
   1898     }
   1899 
   1900     @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
   1901     private void setAutoExposureLockIfSupported() {
   1902         if (mAeLockSupported) {
   1903             mCameraSettings.setAutoExposureLock(mFocusManager.getAeAwbLock());
   1904         }
   1905     }
   1906 
   1907     @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
   1908     private void setAutoWhiteBalanceLockIfSupported() {
   1909         if (mAwbLockSupported) {
   1910             mCameraSettings.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
   1911         }
   1912     }
   1913 
   1914     private void setFocusAreasIfSupported() {
   1915         if (mFocusAreaSupported) {
   1916             mCameraSettings.setFocusAreas(mFocusManager.getFocusAreas());
   1917         }
   1918     }
   1919 
   1920     private void setMeteringAreasIfSupported() {
   1921         if (mMeteringAreaSupported) {
   1922             mCameraSettings.setMeteringAreas(mFocusManager.getMeteringAreas());
   1923         }
   1924     }
   1925 
   1926     private void updateCameraParametersPreference() {
   1927         // some monkey tests can get here when shutting the app down
   1928         // make sure mCameraDevice is still valid, b/17580046
   1929         if (mCameraDevice == null) {
   1930             return;
   1931         }
   1932 
   1933         setAutoExposureLockIfSupported();
   1934         setAutoWhiteBalanceLockIfSupported();
   1935         setFocusAreasIfSupported();
   1936         setMeteringAreasIfSupported();
   1937 
   1938         // Initialize focus mode.
   1939         mFocusManager.overrideFocusMode(null);
   1940         mCameraSettings
   1941                 .setFocusMode(mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()));
   1942         SessionStatsCollector.instance().autofocusActive(
   1943                 mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()) ==
   1944                         CameraCapabilities.FocusMode.CONTINUOUS_PICTURE
   1945         );
   1946 
   1947         // Set JPEG quality.
   1948         updateParametersPictureQuality();
   1949 
   1950         // For the following settings, we need to check if the settings are
   1951         // still supported by latest driver, if not, ignore the settings.
   1952 
   1953         // Set exposure compensation
   1954         updateParametersExposureCompensation();
   1955 
   1956         // Set the scene mode: also sets flash and white balance.
   1957         updateParametersSceneMode();
   1958 
   1959         if (mContinuousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
   1960             updateAutoFocusMoveCallback();
   1961         }
   1962     }
   1963 
   1964     /**
   1965      * This method sets picture size parameters. Size parameters should only be
   1966      * set when the preview is stopped, and so this method is only invoked in
   1967      * {@link #startPreview()} just before starting the preview.
   1968      */
   1969     private void updateParametersPictureSize() {
   1970         if (mCameraDevice == null) {
   1971             Log.w(TAG, "attempting to set picture size without caemra device");
   1972             return;
   1973         }
   1974 
   1975         List<Size> supported = Size.convert(mCameraCapabilities.getSupportedPhotoSizes());
   1976         CameraPictureSizesCacher.updateSizesForCamera(mAppController.getAndroidContext(),
   1977                 mCameraDevice.getCameraId(), supported);
   1978 
   1979         OneCamera.Facing cameraFacing =
   1980               isCameraFrontFacing() ? OneCamera.Facing.FRONT : OneCamera.Facing.BACK;
   1981         Size pictureSize;
   1982         try {
   1983             pictureSize = mAppController.getResolutionSetting().getPictureSize(
   1984                   mAppController.getCameraProvider().getCurrentCameraId(),
   1985                   cameraFacing);
   1986         } catch (OneCameraAccessException ex) {
   1987             mAppController.getFatalErrorHandler().onGenericCameraAccessFailure();
   1988             return;
   1989         }
   1990 
   1991         mCameraSettings.setPhotoSize(pictureSize.toPortabilitySize());
   1992 
   1993         if (ApiHelper.IS_NEXUS_5) {
   1994             if (ResolutionUtil.NEXUS_5_LARGE_16_BY_9.equals(pictureSize)) {
   1995                 mShouldResizeTo16x9 = true;
   1996             } else {
   1997                 mShouldResizeTo16x9 = false;
   1998             }
   1999         }
   2000 
   2001         // Set a preview size that is closest to the viewfinder height and has
   2002         // the right aspect ratio.
   2003         List<Size> sizes = Size.convert(mCameraCapabilities.getSupportedPreviewSizes());
   2004         Size optimalSize = CameraUtil.getOptimalPreviewSize(sizes,
   2005                 (double) pictureSize.width() / pictureSize.height());
   2006         Size original = new Size(mCameraSettings.getCurrentPreviewSize());
   2007         if (!optimalSize.equals(original)) {
   2008             Log.v(TAG, "setting preview size. optimal: " + optimalSize + "original: " + original);
   2009             mCameraSettings.setPreviewSize(optimalSize.toPortabilitySize());
   2010 
   2011             mCameraDevice.applySettings(mCameraSettings);
   2012             mCameraSettings = mCameraDevice.getSettings();
   2013         }
   2014 
   2015         if (optimalSize.width() != 0 && optimalSize.height() != 0) {
   2016             Log.v(TAG, "updating aspect ratio");
   2017             mUI.updatePreviewAspectRatio((float) optimalSize.width()
   2018                     / (float) optimalSize.height());
   2019         }
   2020         Log.d(TAG, "Preview size is " + optimalSize);
   2021     }
   2022 
   2023     private void updateParametersPictureQuality() {
   2024         int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
   2025                 CameraProfile.QUALITY_HIGH);
   2026         mCameraSettings.setPhotoJpegCompressionQuality(jpegQuality);
   2027     }
   2028 
   2029     private void updateParametersExposureCompensation() {
   2030         SettingsManager settingsManager = mActivity.getSettingsManager();
   2031         if (settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
   2032                                        Keys.KEY_EXPOSURE_COMPENSATION_ENABLED)) {
   2033             int value = settingsManager.getInteger(mAppController.getCameraScope(),
   2034                                                    Keys.KEY_EXPOSURE);
   2035             int max = mCameraCapabilities.getMaxExposureCompensation();
   2036             int min = mCameraCapabilities.getMinExposureCompensation();
   2037             if (value >= min && value <= max) {
   2038                 mCameraSettings.setExposureCompensationIndex(value);
   2039             } else {
   2040                 Log.w(TAG, "invalid exposure range: " + value);
   2041             }
   2042         } else {
   2043             // If exposure compensation is not enabled, reset the exposure compensation value.
   2044             setExposureCompensation(0);
   2045         }
   2046     }
   2047 
   2048     private void updateParametersSceneMode() {
   2049         CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
   2050         SettingsManager settingsManager = mActivity.getSettingsManager();
   2051 
   2052         mSceneMode = stringifier.
   2053             sceneModeFromString(settingsManager.getString(mAppController.getCameraScope(),
   2054                                                           Keys.KEY_SCENE_MODE));
   2055         if (mCameraCapabilities.supports(mSceneMode)) {
   2056             if (mCameraSettings.getCurrentSceneMode() != mSceneMode) {
   2057                 mCameraSettings.setSceneMode(mSceneMode);
   2058 
   2059                 // Setting scene mode will change the settings of flash mode,
   2060                 // white balance, and focus mode. Here we read back the
   2061                 // parameters, so we can know those settings.
   2062                 mCameraDevice.applySettings(mCameraSettings);
   2063                 mCameraSettings = mCameraDevice.getSettings();
   2064             }
   2065         } else {
   2066             mSceneMode = mCameraSettings.getCurrentSceneMode();
   2067             if (mSceneMode == null) {
   2068                 mSceneMode = CameraCapabilities.SceneMode.AUTO;
   2069             }
   2070         }
   2071 
   2072         if (CameraCapabilities.SceneMode.AUTO == mSceneMode) {
   2073             // Set flash mode.
   2074             updateParametersFlashMode();
   2075 
   2076             // Set focus mode.
   2077             mFocusManager.overrideFocusMode(null);
   2078             mCameraSettings.setFocusMode(
   2079                     mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()));
   2080         } else {
   2081             mFocusManager.overrideFocusMode(mCameraSettings.getCurrentFocusMode());
   2082         }
   2083     }
   2084 
   2085     private void updateParametersFlashMode() {
   2086         SettingsManager settingsManager = mActivity.getSettingsManager();
   2087 
   2088         CameraCapabilities.FlashMode flashMode = mCameraCapabilities.getStringifier()
   2089             .flashModeFromString(settingsManager.getString(mAppController.getCameraScope(),
   2090                                                            Keys.KEY_FLASH_MODE));
   2091         if (mCameraCapabilities.supports(flashMode)) {
   2092             mCameraSettings.setFlashMode(flashMode);
   2093         }
   2094     }
   2095 
   2096     @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
   2097     private void updateAutoFocusMoveCallback() {
   2098         if (mCameraDevice == null) {
   2099             return;
   2100         }
   2101         if (mCameraSettings.getCurrentFocusMode() ==
   2102                 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE) {
   2103             mCameraDevice.setAutoFocusMoveCallback(mHandler,
   2104                     (CameraAFMoveCallback) mAutoFocusMoveCallback);
   2105         } else {
   2106             mCameraDevice.setAutoFocusMoveCallback(null, null);
   2107         }
   2108     }
   2109 
   2110     /**
   2111      * Sets the exposure compensation to the given value and also updates settings.
   2112      *
   2113      * @param value exposure compensation value to be set
   2114      */
   2115     public void setExposureCompensation(int value) {
   2116         int max = mCameraCapabilities.getMaxExposureCompensation();
   2117         int min = mCameraCapabilities.getMinExposureCompensation();
   2118         if (value >= min && value <= max) {
   2119             mCameraSettings.setExposureCompensationIndex(value);
   2120             SettingsManager settingsManager = mActivity.getSettingsManager();
   2121             settingsManager.set(mAppController.getCameraScope(),
   2122                                 Keys.KEY_EXPOSURE, value);
   2123         } else {
   2124             Log.w(TAG, "invalid exposure range: " + value);
   2125         }
   2126     }
   2127 
   2128     // We separate the parameters into several subsets, so we can update only
   2129     // the subsets actually need updating. The PREFERENCE set needs extra
   2130     // locking because the preference can be changed from GLThread as well.
   2131     private void setCameraParameters(int updateSet) {
   2132         if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
   2133             updateCameraParametersInitialize();
   2134         }
   2135 
   2136         if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
   2137             updateCameraParametersZoom();
   2138         }
   2139 
   2140         if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
   2141             updateCameraParametersPreference();
   2142         }
   2143 
   2144         if (mCameraDevice != null) {
   2145             mCameraDevice.applySettings(mCameraSettings);
   2146         }
   2147     }
   2148 
   2149     // If the Camera is idle, update the parameters immediately, otherwise
   2150     // accumulate them in mUpdateSet and update later.
   2151     private void setCameraParametersWhenIdle(int additionalUpdateSet) {
   2152         mUpdateSet |= additionalUpdateSet;
   2153         if (mCameraDevice == null) {
   2154             // We will update all the parameters when we open the device, so
   2155             // we don't need to do anything now.
   2156             mUpdateSet = 0;
   2157             return;
   2158         } else if (isCameraIdle()) {
   2159             setCameraParameters(mUpdateSet);
   2160             updateSceneMode();
   2161             mUpdateSet = 0;
   2162         } else {
   2163             if (!mHandler.hasMessages(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
   2164                 mHandler.sendEmptyMessageDelayed(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
   2165             }
   2166         }
   2167     }
   2168 
   2169     @Override
   2170     public boolean isCameraIdle() {
   2171         return (mCameraState == IDLE) ||
   2172                 (mCameraState == PREVIEW_STOPPED) ||
   2173                 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
   2174                 && (mCameraState != SWITCHING_CAMERA));
   2175     }
   2176 
   2177     @Override
   2178     public boolean isImageCaptureIntent() {
   2179         String action = mActivity.getIntent().getAction();
   2180         return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
   2181         || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
   2182     }
   2183 
   2184     private void setupCaptureParams() {
   2185         Bundle myExtras = mActivity.getIntent().getExtras();
   2186         if (myExtras != null) {
   2187             mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
   2188             mCropValue = myExtras.getString("crop");
   2189         }
   2190     }
   2191 
   2192     private void initializeCapabilities() {
   2193         mCameraCapabilities = mCameraDevice.getCapabilities();
   2194         mFocusAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA);
   2195         mMeteringAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.METERING_AREA);
   2196         mAeLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK);
   2197         mAwbLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK);
   2198         mContinuousFocusSupported =
   2199                 mCameraCapabilities.supports(CameraCapabilities.FocusMode.CONTINUOUS_PICTURE);
   2200     }
   2201 
   2202     @Override
   2203     public void onZoomChanged(float ratio) {
   2204         // Not useful to change zoom value when the activity is paused.
   2205         if (mPaused) {
   2206             return;
   2207         }
   2208         mZoomValue = ratio;
   2209         if (mCameraSettings == null || mCameraDevice == null) {
   2210             return;
   2211         }
   2212         // Set zoom parameters asynchronously
   2213         mCameraSettings.setZoomRatio(mZoomValue);
   2214         mCameraDevice.applySettings(mCameraSettings);
   2215     }
   2216 
   2217     @Override
   2218     public int getCameraState() {
   2219         return mCameraState;
   2220     }
   2221 
   2222     @Override
   2223     public void onMemoryStateChanged(int state) {
   2224         mAppController.setShutterEnabled(state == MemoryManager.STATE_OK);
   2225     }
   2226 
   2227     @Override
   2228     public void onLowMemory() {
   2229         // Not much we can do in the photo module.
   2230     }
   2231 
   2232     // For debugging only.
   2233     public void setDebugUri(Uri uri) {
   2234         mDebugUri = uri;
   2235     }
   2236 
   2237     // For debugging only.
   2238     private void saveToDebugUri(byte[] data) {
   2239         if (mDebugUri != null) {
   2240             OutputStream outputStream = null;
   2241             try {
   2242                 outputStream = mContentResolver.openOutputStream(mDebugUri);
   2243                 outputStream.write(data);
   2244                 outputStream.close();
   2245             } catch (IOException e) {
   2246                 Log.e(TAG, "Exception while writing debug jpeg file", e);
   2247             } finally {
   2248                 CameraUtil.closeSilently(outputStream);
   2249             }
   2250         }
   2251     }
   2252 
   2253     @Override
   2254     public void onRemoteShutterPress() {
   2255         mHandler.post(new Runnable() {
   2256             @Override
   2257             public void run() {
   2258                 focusAndCapture();
   2259             }
   2260         });
   2261     }
   2262 }
   2263