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