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