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