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