PhotoModule.java revision 4cdaf5178704224673d0e7ba948830d1cabd8fa0
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.SurfaceTexture;
26import android.hardware.Camera.CameraInfo;
27import android.hardware.Camera.Parameters;
28import android.hardware.Camera.Size;
29import android.hardware.Sensor;
30import android.hardware.SensorEvent;
31import android.hardware.SensorEventListener;
32import android.hardware.SensorManager;
33import android.location.Location;
34import android.media.CameraProfile;
35import android.net.Uri;
36import android.os.Build;
37import android.os.Bundle;
38import android.os.Handler;
39import android.os.Looper;
40import android.os.Message;
41import android.os.MessageQueue;
42import android.os.SystemClock;
43import android.provider.MediaStore;
44import android.util.Log;
45import android.view.KeyEvent;
46import android.view.OrientationEventListener;
47import android.view.View;
48
49import com.android.camera.PhotoModule.NamedImages.NamedEntity;
50import com.android.camera.app.AppController;
51import com.android.camera.app.CameraAppUI;
52import com.android.camera.app.CameraManager.CameraAFCallback;
53import com.android.camera.app.CameraManager.CameraAFMoveCallback;
54import com.android.camera.app.CameraManager.CameraPictureCallback;
55import com.android.camera.app.CameraManager.CameraProxy;
56import com.android.camera.app.CameraManager.CameraShutterCallback;
57import com.android.camera.app.LocationManager;
58import com.android.camera.app.MediaSaver;
59import com.android.camera.app.MemoryManager;
60import com.android.camera.app.MemoryManager.MemoryListener;
61import com.android.camera.exif.ExifInterface;
62import com.android.camera.exif.ExifTag;
63import com.android.camera.exif.Rational;
64import com.android.camera.hardware.HardwareSpec;
65import com.android.camera.hardware.HardwareSpecImpl;
66import com.android.camera.module.ModuleController;
67import com.android.camera.settings.SettingsManager;
68import com.android.camera.settings.SettingsUtil;
69import com.android.camera.util.ApiHelper;
70import com.android.camera.util.CameraUtil;
71import com.android.camera.util.GcamHelper;
72import com.android.camera.util.UsageStatistics;
73import com.android.camera2.R;
74import com.google.common.logging.eventprotos;
75
76import java.io.File;
77import java.io.FileNotFoundException;
78import java.io.FileOutputStream;
79import java.io.IOException;
80import java.io.OutputStream;
81import java.lang.ref.WeakReference;
82import java.util.List;
83import java.util.Vector;
84
85public class PhotoModule
86        extends CameraModule
87        implements PhotoController,
88        ModuleController,
89        MemoryListener,
90        FocusOverlayManager.Listener,
91        SensorEventListener,
92        SettingsManager.OnSettingChangedListener {
93
94    private static final String TAG = "PhotoModule";
95
96    // We number the request code from 1000 to avoid collision with Gallery.
97    private static final int REQUEST_CROP = 1000;
98
99    // Messages defined for the UI thread handler.
100    private static final int MSG_FIRST_TIME_INIT = 1;
101    private static final int MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE = 2;
102
103    // The subset of parameters we need to update in setCameraParameters().
104    private static final int UPDATE_PARAM_INITIALIZE = 1;
105    private static final int UPDATE_PARAM_ZOOM = 2;
106    private static final int UPDATE_PARAM_PREFERENCE = 4;
107    private static final int UPDATE_PARAM_ALL = -1;
108
109    // This is the delay before we execute onResume tasks when coming
110    // from the lock screen, to allow time for onPause to execute.
111    private static final int ON_RESUME_TASKS_DELAY_MSEC = 20;
112
113    private static final String DEBUG_IMAGE_PREFIX = "DEBUG_";
114
115    private CameraActivity mActivity;
116    private CameraProxy mCameraDevice;
117    private int mCameraId;
118    private Parameters mParameters;
119    private boolean mPaused;
120
121    private PhotoUI mUI;
122
123    // The activity is going to switch to the specified camera id. This is
124    // needed because texture copy is done in GL thread. -1 means camera is not
125    // switching.
126    protected int mPendingSwitchCameraId = -1;
127
128    // When setCameraParametersWhenIdle() is called, we accumulate the subsets
129    // needed to be updated in mUpdateSet.
130    private int mUpdateSet;
131
132    private static final int SCREEN_DELAY = 2 * 60 * 1000;
133
134    private int mZoomValue; // The current zoom value.
135
136    private Parameters mInitialParams;
137    private boolean mFocusAreaSupported;
138    private boolean mMeteringAreaSupported;
139    private boolean mAeLockSupported;
140    private boolean mAwbLockSupported;
141    private boolean mContinuousFocusSupported;
142
143    // The degrees of the device rotated clockwise from its natural orientation.
144    private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
145
146    private static final String sTempCropFilename = "crop-temp";
147
148    private boolean mFaceDetectionStarted = false;
149
150    // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
151    private String mCropValue;
152    private Uri mSaveUri;
153
154    private Uri mDebugUri;
155
156    // We use a queue to generated names of the images to be used later
157    // when the image is ready to be saved.
158    private NamedImages mNamedImages;
159
160    private final Runnable mDoSnapRunnable = new Runnable() {
161        @Override
162        public void run() {
163            onShutterButtonClick();
164        }
165    };
166
167    /**
168     * An unpublished intent flag requesting to return as soon as capturing is
169     * completed. TODO: consider publishing by moving into MediaStore.
170     */
171    private static final String EXTRA_QUICK_CAPTURE =
172            "android.intent.extra.quickCapture";
173
174    // The display rotation in degrees. This is only valid when mCameraState is
175    // not PREVIEW_STOPPED.
176    private int mDisplayRotation;
177    // The value for android.hardware.Camera.setDisplayOrientation.
178    private int mCameraDisplayOrientation;
179    // The value for UI components like indicators.
180    private int mDisplayOrientation;
181    // The value for android.hardware.Camera.Parameters.setRotation.
182    private int mJpegRotation;
183    // Indicates whether we are using front camera
184    private boolean mMirror;
185    private boolean mFirstTimeInitialized;
186    private boolean mIsImageCaptureIntent;
187
188    private int mCameraState = PREVIEW_STOPPED;
189    private boolean mSnapshotOnIdle = false;
190
191    private ContentResolver mContentResolver;
192
193    private LocationManager mLocationManager;
194    private AppController mAppController;
195
196    private final PostViewPictureCallback mPostViewPictureCallback =
197            new PostViewPictureCallback();
198    private final RawPictureCallback mRawPictureCallback =
199            new RawPictureCallback();
200    private final AutoFocusCallback mAutoFocusCallback =
201            new AutoFocusCallback();
202    private final Object mAutoFocusMoveCallback =
203            ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
204                    ? new AutoFocusMoveCallback()
205                    : null;
206
207    private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
208
209    private long mFocusStartTime;
210    private long mShutterCallbackTime;
211    private long mPostViewPictureCallbackTime;
212    private long mRawPictureCallbackTime;
213    private long mJpegPictureCallbackTime;
214    private long mOnResumeTime;
215    private byte[] mJpegImageData;
216
217    // These latency time are for the CameraLatency test.
218    public long mAutoFocusTime;
219    public long mShutterLag;
220    public long mShutterToPictureDisplayedTime;
221    public long mPictureDisplayedToJpegCallbackTime;
222    public long mJpegCallbackFinishTime;
223    public long mCaptureStartTime;
224
225    // This handles everything about focus.
226    private FocusOverlayManager mFocusManager;
227
228    private final int mGcamModeIndex;
229
230    private String mSceneMode;
231
232    private final Handler mHandler = new MainHandler(this);
233
234    private boolean mQuickCapture;
235    private SensorManager mSensorManager;
236    private final float[] mGData = new float[3];
237    private final float[] mMData = new float[3];
238    private final float[] mR = new float[16];
239    private int mHeading = -1;
240
241    /** Whether shutter is enabled. */
242    private boolean mShutterEnabled = true;
243
244    /** True if all the parameters needed to start preview is ready. */
245    private boolean mCameraPreviewParamsReady = false;
246
247    private final MediaSaver.OnMediaSavedListener mOnMediaSavedListener =
248            new MediaSaver.OnMediaSavedListener() {
249                @Override
250                public void onMediaSaved(Uri uri) {
251                    if (uri != null) {
252                        mActivity.notifyNewMedia(uri);
253                    }
254                }
255            };
256
257    private void checkDisplayRotation() {
258        // Set the display orientation if display rotation has changed.
259        // Sometimes this happens when the device is held upside
260        // down and camera app is opened. Rotation animation will
261        // take some time and the rotation value we have got may be
262        // wrong. Framework does not have a callback for this now.
263        if (CameraUtil.getDisplayRotation(mActivity) != mDisplayRotation) {
264            setDisplayOrientation();
265        }
266        if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
267            mHandler.postDelayed(new Runnable() {
268                @Override
269                public void run() {
270                    checkDisplayRotation();
271                }
272            }, 100);
273        }
274    }
275
276    /**
277     * This Handler is used to post message back onto the main thread of the
278     * application
279     */
280    private static class MainHandler extends Handler {
281        private final WeakReference<PhotoModule> mModule;
282
283        public MainHandler(PhotoModule module) {
284            super(Looper.getMainLooper());
285            mModule = new WeakReference<PhotoModule>(module);
286        }
287
288        @Override
289        public void handleMessage(Message msg) {
290            PhotoModule module = mModule.get();
291            if (module == null) {
292                return;
293            }
294            switch (msg.what) {
295                case MSG_FIRST_TIME_INIT: {
296                    module.initializeFirstTime();
297                    break;
298                }
299
300                case MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE: {
301                    module.setCameraParametersWhenIdle(0);
302                    break;
303                }
304            }
305        }
306    }
307
308    private void switchToGcamCapture() {
309        if (mActivity != null && mGcamModeIndex != 0) {
310            SettingsManager settingsManager = mActivity.getSettingsManager();
311            settingsManager.set(SettingsManager.SETTING_CAMERA_HDR,
312                SettingsManager.VALUE_ON);
313
314            // Disable the HDR+ button to prevent callbacks from being
315            // queued before the correct callback is attached to the button
316            // in the new module.  The new module will set the enabled/disabled
317            // of this button when the module's preferred camera becomes available.
318            ButtonManager buttonManager = mActivity.getButtonManager();
319            buttonManager.disableButton(ButtonManager.BUTTON_HDRPLUS);
320
321            // Do not post this to avoid this module switch getting interleaved with
322            // other button callbacks.
323            mActivity.onModeSelected(mGcamModeIndex);
324        }
325    }
326
327    /**
328     * Constructs a new photo module.
329     */
330    public PhotoModule(AppController app) {
331        super(app);
332        mGcamModeIndex = app.getAndroidContext().getResources()
333                .getInteger(R.integer.camera_mode_gcam);
334    }
335
336    @Override
337    public void init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent) {
338        mActivity = activity;
339        // TODO: Need to look at the controller interface to see if we can get
340        // rid of passing in the activity directly.
341        mAppController = mActivity;
342
343        mUI = new PhotoUI(mActivity, this, mActivity.getModuleLayoutRoot());
344        mActivity.setPreviewStatusListener(mUI);
345
346        SettingsManager settingsManager = mActivity.getSettingsManager();
347        mCameraId = Integer.parseInt(settingsManager.get(SettingsManager.SETTING_CAMERA_ID));
348
349        mContentResolver = mActivity.getContentResolver();
350
351        // Surface texture is from camera screen nail and startPreview needs it.
352        // This must be done before startPreview.
353        mIsImageCaptureIntent = isImageCaptureIntent();
354
355        mActivity.getCameraProvider().requestCamera(mCameraId);
356
357        mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
358        mLocationManager = mActivity.getLocationManager();
359        mSensorManager = (SensorManager) (mActivity.getSystemService(Context.SENSOR_SERVICE));
360    }
361
362    @Override
363    public boolean isUsingBottomBar() {
364        return true;
365    }
366
367    private void initializeControlByIntent() {
368        if (mIsImageCaptureIntent) {
369            mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
370            setupCaptureParams();
371        }
372    }
373
374    private void onPreviewStarted() {
375        mAppController.onPreviewStarted();
376        setCameraState(IDLE);
377        startFaceDetection();
378        locationFirstRun();
379    }
380
381    @Override
382    public void onPreviewInitialDataReceived() {
383    }
384
385    // Prompt the user to pick to record location for the very first run of
386    // camera only
387    private void locationFirstRun() {
388        SettingsManager settingsManager = mActivity.getSettingsManager();
389
390        if (settingsManager.isSet(SettingsManager.SETTING_RECORD_LOCATION)) {
391            return;
392        }
393        if (mActivity.isSecureCamera()) {
394            return;
395        }
396        // Check if the back camera exists
397        int backCameraId = mAppController.getCameraProvider().getFirstBackCameraId();
398        if (backCameraId == -1) {
399            // If there is no back camera, do not show the prompt.
400            return;
401        }
402        mUI.showLocationDialog();
403    }
404
405    @Override
406    public void onPreviewUIReady() {
407        startPreview();
408    }
409
410    @Override
411    public void onPreviewUIDestroyed() {
412        if (mCameraDevice == null) {
413            return;
414        }
415        mCameraDevice.setPreviewTexture(null);
416        stopPreview();
417    }
418
419    @Override
420    public void startPreCaptureAnimation() {
421        mAppController.startPreCaptureAnimation();
422    }
423
424    private void onCameraOpened() {
425        openCameraCommon();
426        initializeControlByIntent();
427    }
428
429    private void switchCamera() {
430        if (mPaused) {
431            return;
432        }
433        SettingsManager settingsManager = mActivity.getSettingsManager();
434
435        Log.v(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
436        closeCamera();
437        mCameraId = mPendingSwitchCameraId;
438        settingsManager.set(SettingsManager.SETTING_CAMERA_ID, "" + mCameraId);
439        mActivity.getCameraProvider().requestCamera(mCameraId);
440        mUI.clearFaces();
441        if (mFocusManager != null) {
442            mFocusManager.removeMessages();
443        }
444
445        mMirror = isCameraFrontFacing();
446        mFocusManager.setMirror(mMirror);
447        // Start switch camera animation. Post a message because
448        // onFrameAvailable from the old camera may already exist.
449    }
450
451    private final ButtonManager.ButtonCallback mCameraCallback =
452            new ButtonManager.ButtonCallback() {
453                @Override
454                public void onStateChanged(int state) {
455                    // At the time this callback is fired, the camera id
456                    // has be set to the desired camera.
457
458                    if (mPaused || mAppController.getCameraProvider().waitingForCamera()) {
459                        return;
460                    }
461                    // If switching to back camera, and HDR+ is still on,
462                    // switch back to gcam, otherwise handle callback normally.
463                    SettingsManager settingsManager = mActivity.getSettingsManager();
464                    if (settingsManager.isCameraBackFacing()) {
465                        if (settingsManager.requestsReturnToHdrPlus()) {
466                            switchToGcamCapture();
467                            return;
468                        }
469                    }
470
471                    mPendingSwitchCameraId = state;
472
473                    Log.v(TAG, "Start to switch camera. cameraId=" + state);
474                    // We need to keep a preview frame for the animation before
475                    // releasing the camera. This will trigger
476                    // onPreviewTextureCopied.
477                    // TODO: Need to animate the camera switch
478                    switchCamera();
479                }
480            };
481
482    private final ButtonManager.ButtonCallback mHdrPlusCallback =
483            new ButtonManager.ButtonCallback() {
484                @Override
485                public void onStateChanged(int state) {
486                    if (GcamHelper.hasGcamCapture()) {
487                        // Set the camera setting to default backfacing.
488                        SettingsManager settingsManager = mActivity.getSettingsManager();
489                        settingsManager.setDefault(SettingsManager.SETTING_CAMERA_ID);
490                        switchToGcamCapture();
491                    } else {
492                        mSceneMode = CameraUtil.SCENE_MODE_HDR;
493                        updateParametersSceneMode();
494                    }
495                }
496            };
497
498    private final View.OnClickListener mCancelCallback = new View.OnClickListener() {
499        @Override
500        public void onClick(View v) {
501            onCaptureCancelled();
502        }
503    };
504
505    private final View.OnClickListener mDoneCallback = new View.OnClickListener() {
506        @Override
507        public void onClick(View v) {
508            onCaptureDone();
509        }
510    };
511
512    private final View.OnClickListener mRetakeCallback = new View.OnClickListener() {
513        @Override
514        public void onClick(View v) {
515            mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
516            onCaptureRetake();
517        }
518    };
519
520    @Override
521    public HardwareSpec getHardwareSpec() {
522        return (mParameters != null ? new HardwareSpecImpl(mParameters) : null);
523    }
524
525    @Override
526    public CameraAppUI.BottomBarUISpec getBottomBarSpec() {
527        CameraAppUI.BottomBarUISpec bottomBarSpec = new CameraAppUI.BottomBarUISpec();
528
529        bottomBarSpec.enableCamera = true;
530        bottomBarSpec.cameraCallback = mCameraCallback;
531        bottomBarSpec.enableFlash = true;
532        bottomBarSpec.enableHdr = true;
533        bottomBarSpec.hdrCallback = mHdrPlusCallback;
534        bottomBarSpec.enableGridLines = true;
535
536        if (isImageCaptureIntent()) {
537            bottomBarSpec.showCancel = true;
538            bottomBarSpec.cancelCallback = mCancelCallback;
539            bottomBarSpec.showDone = true;
540            bottomBarSpec.doneCallback = mDoneCallback;
541            bottomBarSpec.showRetake = true;
542            bottomBarSpec.retakeCallback = mRetakeCallback;
543        }
544
545        return bottomBarSpec;
546    }
547
548    // either open a new camera or switch cameras
549    private void openCameraCommon() {
550        mUI.onCameraOpened(mParameters);
551        if (mIsImageCaptureIntent) {
552            // Set hdr plus to default: off.
553            SettingsManager settingsManager = mActivity.getSettingsManager();
554            settingsManager.setDefault(SettingsManager.SETTING_CAMERA_HDR_PLUS);
555        }
556        updateSceneMode();
557    }
558
559    @Override
560    public void updatePreviewAspectRatio(float aspectRatio) {
561        mAppController.updatePreviewAspectRatio(aspectRatio);
562    }
563
564    private void resetExposureCompensation() {
565        SettingsManager settingsManager = mActivity.getSettingsManager();
566        if (settingsManager == null) {
567            Log.e(TAG, "Settings manager is null!");
568            return;
569        }
570        settingsManager.setDefault(SettingsManager.SETTING_EXPOSURE);
571    }
572
573    // Snapshots can only be taken after this is called. It should be called
574    // once only. We could have done these things in onCreate() but we want to
575    // make preview screen appear as soon as possible.
576    private void initializeFirstTime() {
577        if (mFirstTimeInitialized || mPaused) {
578            return;
579        }
580
581        mUI.initializeFirstTime();
582
583        // We set the listener only when both service and shutterbutton
584        // are initialized.
585        getServices().getMemoryManager().addListener(this);
586
587        mNamedImages = new NamedImages();
588
589        mFirstTimeInitialized = true;
590        addIdleHandler();
591
592        mActivity.updateStorageSpaceAndHint();
593    }
594
595    // If the activity is paused and resumed, this method will be called in
596    // onResume.
597    private void initializeSecondTime() {
598        getServices().getMemoryManager().addListener(this);
599        mNamedImages = new NamedImages();
600        mUI.initializeSecondTime(mParameters);
601    }
602
603    private void addIdleHandler() {
604        MessageQueue queue = Looper.myQueue();
605        queue.addIdleHandler(new MessageQueue.IdleHandler() {
606            @Override
607            public boolean queueIdle() {
608                Storage.ensureOSXCompatible();
609                return false;
610            }
611        });
612    }
613
614    @Override
615    public void startFaceDetection() {
616        if (mFaceDetectionStarted) {
617            return;
618        }
619        if (mParameters.getMaxNumDetectedFaces() > 0) {
620            mFaceDetectionStarted = true;
621            mUI.onStartFaceDetection(mDisplayOrientation, isCameraFrontFacing());
622            mCameraDevice.setFaceDetectionCallback(mHandler, mUI);
623            mCameraDevice.startFaceDetection();
624        }
625    }
626
627    @Override
628    public void stopFaceDetection() {
629        if (!mFaceDetectionStarted) {
630            return;
631        }
632        if (mParameters.getMaxNumDetectedFaces() > 0) {
633            mFaceDetectionStarted = false;
634            mCameraDevice.setFaceDetectionCallback(null, null);
635            mCameraDevice.stopFaceDetection();
636            mUI.clearFaces();
637        }
638    }
639
640    private final class ShutterCallback
641            implements CameraShutterCallback {
642
643        private final boolean mNeedsAnimation;
644
645        public ShutterCallback(boolean needsAnimation) {
646            mNeedsAnimation = needsAnimation;
647        }
648
649        @Override
650        public void onShutter(CameraProxy camera) {
651            mShutterCallbackTime = System.currentTimeMillis();
652            mShutterLag = mShutterCallbackTime - mCaptureStartTime;
653            Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
654            if (mNeedsAnimation) {
655                mActivity.runOnUiThread(new Runnable() {
656                    @Override
657                    public void run() {
658                        animateAfterShutter();
659                    }
660                });
661            }
662        }
663    }
664
665    private final class PostViewPictureCallback
666            implements CameraPictureCallback {
667        @Override
668        public void onPictureTaken(byte[] data, CameraProxy camera) {
669            mPostViewPictureCallbackTime = System.currentTimeMillis();
670            Log.v(TAG, "mShutterToPostViewCallbackTime = "
671                    + (mPostViewPictureCallbackTime - mShutterCallbackTime)
672                    + "ms");
673        }
674    }
675
676    private final class RawPictureCallback
677            implements CameraPictureCallback {
678        @Override
679        public void onPictureTaken(byte[] rawData, CameraProxy camera) {
680            mRawPictureCallbackTime = System.currentTimeMillis();
681            Log.v(TAG, "mShutterToRawCallbackTime = "
682                    + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
683        }
684    }
685
686    private final class JpegPictureCallback
687            implements CameraPictureCallback {
688        Location mLocation;
689
690        public JpegPictureCallback(Location loc) {
691            mLocation = loc;
692        }
693
694        @Override
695        public void onPictureTaken(final byte[] jpegData, CameraProxy camera) {
696            setShutterEnabled(true);
697            if (mPaused) {
698                return;
699            }
700            if (mIsImageCaptureIntent) {
701                stopPreview();
702            }
703            if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {
704                mUI.setSwipingEnabled(true);
705            }
706
707            mJpegPictureCallbackTime = System.currentTimeMillis();
708            // If postview callback has arrived, the captured image is displayed
709            // in postview callback. If not, the captured image is displayed in
710            // raw picture callback.
711            if (mPostViewPictureCallbackTime != 0) {
712                mShutterToPictureDisplayedTime =
713                        mPostViewPictureCallbackTime - mShutterCallbackTime;
714                mPictureDisplayedToJpegCallbackTime =
715                        mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
716            } else {
717                mShutterToPictureDisplayedTime =
718                        mRawPictureCallbackTime - mShutterCallbackTime;
719                mPictureDisplayedToJpegCallbackTime =
720                        mJpegPictureCallbackTime - mRawPictureCallbackTime;
721            }
722            Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
723                    + mPictureDisplayedToJpegCallbackTime + "ms");
724
725            mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
726            if (!mIsImageCaptureIntent) {
727                setupPreview();
728            }
729
730            ExifInterface exif = Exif.getExif(jpegData);
731            int orientation = Exif.getOrientation(exif);
732
733            if (!mIsImageCaptureIntent) {
734                // Calculate the width and the height of the jpeg.
735                Size s = mParameters.getPictureSize();
736                int width, height;
737                if ((mJpegRotation + orientation) % 180 == 0) {
738                    width = s.width;
739                    height = s.height;
740                } else {
741                    width = s.height;
742                    height = s.width;
743                }
744                NamedEntity name = mNamedImages.getNextNameEntity();
745                String title = (name == null) ? null : name.title;
746                long date = (name == null) ? -1 : name.date;
747
748                // Handle debug mode outputs
749                if (mDebugUri != null) {
750                    // If using a debug uri, save jpeg there.
751                    saveToDebugUri(jpegData);
752
753                    // Adjust the title of the debug image shown in mediastore.
754                    if (title != null) {
755                        title = DEBUG_IMAGE_PREFIX + title;
756                    }
757                }
758
759                if (title == null) {
760                    Log.e(TAG, "Unbalanced name/data pair");
761                } else {
762                    if (date == -1) {
763                        date = mCaptureStartTime;
764                    }
765                    if (mHeading >= 0) {
766                        // heading direction has been updated by the sensor.
767                        ExifTag directionRefTag = exif.buildTag(
768                                ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
769                                ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
770                        ExifTag directionTag = exif.buildTag(
771                                ExifInterface.TAG_GPS_IMG_DIRECTION,
772                                new Rational(mHeading, 1));
773                        exif.setTag(directionRefTag);
774                        exif.setTag(directionTag);
775                    }
776                    getServices().getMediaSaver().addImage(
777                            jpegData, title, date, mLocation, width, height,
778                            orientation, exif, mOnMediaSavedListener, mContentResolver);
779                }
780                // Animate capture with real jpeg data instead of a preview
781                // frame.
782                mUI.animateCapture(jpegData, orientation, mMirror);
783            } else {
784                mJpegImageData = jpegData;
785                if (!mQuickCapture) {
786                    mUI.showCapturedImageForReview(jpegData, orientation, mMirror);
787                } else {
788                    onCaptureDone();
789                }
790            }
791
792            // Check this in advance of each shot so we don't add to shutter
793            // latency. It's true that someone else could write to the SD card
794            // in the mean time and fill it, but that could have happened
795            // between the shutter press and saving the JPEG too.
796            mActivity.updateStorageSpaceAndHint();
797
798            long now = System.currentTimeMillis();
799            mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
800            Log.v(TAG, "mJpegCallbackFinishTime = "
801                    + mJpegCallbackFinishTime + "ms");
802            mJpegPictureCallbackTime = 0;
803        }
804    }
805
806    private final class AutoFocusCallback implements CameraAFCallback {
807        @Override
808        public void onAutoFocus(
809                boolean focused, CameraProxy camera) {
810            if (mPaused) {
811                return;
812            }
813
814            mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
815            Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms");
816            setCameraState(IDLE);
817            mFocusManager.onAutoFocus(focused, mUI.isShutterPressed());
818        }
819    }
820
821    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
822    private final class AutoFocusMoveCallback
823            implements CameraAFMoveCallback {
824        @Override
825        public void onAutoFocusMoving(
826                boolean moving, CameraProxy camera) {
827            mFocusManager.onAutoFocusMoving(moving);
828        }
829    }
830
831    /**
832     * This class is just a thread-safe queue for name,date holder objects.
833     */
834    public static class NamedImages {
835        private final Vector<NamedEntity> mQueue;
836
837        public NamedImages() {
838            mQueue = new Vector<NamedEntity>();
839        }
840
841        public void nameNewImage(long date) {
842            NamedEntity r = new NamedEntity();
843            r.title = CameraUtil.createJpegName(date);
844            r.date = date;
845            mQueue.add(r);
846        }
847
848        public NamedEntity getNextNameEntity() {
849            synchronized (mQueue) {
850                if (!mQueue.isEmpty()) {
851                    return mQueue.remove(0);
852                }
853            }
854            return null;
855        }
856
857        public static class NamedEntity {
858            public String title;
859            public long date;
860        }
861    }
862
863    private void setCameraState(int state) {
864        mCameraState = state;
865        switch (state) {
866            case PREVIEW_STOPPED:
867            case SNAPSHOT_IN_PROGRESS:
868            case SWITCHING_CAMERA:
869                // TODO: Tell app UI to disable swipe
870                break;
871            case PhotoController.IDLE:
872                // TODO: Tell app UI to enable swipe
873                break;
874        }
875    }
876
877    private void animateAfterShutter() {
878        // Only animate when in full screen capture mode
879        // i.e. If monkey/a user swipes to the gallery during picture taking,
880        // don't show animation
881        if (!mIsImageCaptureIntent) {
882            mUI.animateFlash();
883        }
884    }
885
886    @Override
887    public boolean capture() {
888        // If we are already in the middle of taking a snapshot or the image
889        // save request is full then ignore.
890        if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
891                || mCameraState == SWITCHING_CAMERA || !mShutterEnabled) {
892            return false;
893        }
894        mCaptureStartTime = System.currentTimeMillis();
895        mPostViewPictureCallbackTime = 0;
896        mJpegImageData = null;
897
898        final boolean animateBefore = (mSceneMode == CameraUtil.SCENE_MODE_HDR);
899
900        if (animateBefore) {
901            animateAfterShutter();
902        }
903
904        // Set rotation and gps data.
905        int orientation;
906
907        // We need to be consistent with the framework orientation (i.e. the
908        // orientation of the UI.) when the auto-rotate screen setting is on.
909        if (mActivity.isAutoRotateScreen()) {
910            orientation = (360 - mDisplayRotation) % 360;
911        } else {
912            orientation = mOrientation;
913        }
914        CameraInfo info = mActivity.getCameraProvider().getCameraInfo()[mCameraId];
915        mJpegRotation = CameraUtil.getJpegRotation(info, orientation);
916        mParameters.setRotation(mJpegRotation);
917        Location loc = mActivity.getLocationManager().getCurrentLocation();
918        CameraUtil.setGpsParameters(mParameters, loc);
919        mCameraDevice.setParameters(mParameters);
920
921        // We don't want user to press the button again while taking a
922        // multi-second HDR photo.
923        setShutterEnabled(false);
924        mCameraDevice.takePicture(mHandler,
925                new ShutterCallback(!animateBefore),
926                mRawPictureCallback, mPostViewPictureCallback,
927                new JpegPictureCallback(loc));
928
929        mNamedImages.nameNewImage(mCaptureStartTime);
930
931        mFaceDetectionStarted = false;
932        setCameraState(SNAPSHOT_IN_PROGRESS);
933        UsageStatistics.captureEvent(eventprotos.NavigationChange.Mode.PHOTO_CAPTURE,
934                UsageStatistics.hashFileName(mNamedImages.mQueue.lastElement().title + ".jpg"),
935                mParameters);
936        return true;
937    }
938
939    @Override
940    public void setFocusParameters() {
941        setCameraParameters(UPDATE_PARAM_PREFERENCE);
942    }
943
944    private void updateSceneMode() {
945        // If scene mode is set, we cannot set flash mode, white balance, and
946        // focus mode, instead, we read it from driver
947        if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
948            overrideCameraSettings(mParameters.getFlashMode(),
949                    mParameters.getWhiteBalance(), mParameters.getFocusMode());
950        }
951    }
952
953    private void overrideCameraSettings(final String flashMode,
954            final String whiteBalance, final String focusMode) {
955        SettingsManager settingsManager = mActivity.getSettingsManager();
956        settingsManager.set(SettingsManager.SETTING_FLASH_MODE, flashMode);
957        settingsManager.set(SettingsManager.SETTING_WHITE_BALANCE, whiteBalance);
958        settingsManager.set(SettingsManager.SETTING_FOCUS_MODE, focusMode);
959    }
960
961    @Override
962    public void onOrientationChanged(int orientation) {
963        // We keep the last known orientation. So if the user first orient
964        // the camera then point the camera to floor or sky, we still have
965        // the correct orientation.
966        if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
967            return;
968        }
969        mOrientation = CameraUtil.roundOrientation(orientation, mOrientation);
970    }
971
972    @Override
973    public void onCameraAvailable(CameraProxy cameraProxy) {
974        if (mPaused) {
975            return;
976        }
977        mCameraDevice = cameraProxy;
978
979        resetExposureCompensation();
980        initializeCapabilities();
981
982        // Reset zoom value index.
983        mZoomValue = 0;
984        if (mFocusManager == null) {
985            initializeFocusManager();
986        }
987        mFocusManager.setParameters(mInitialParams);
988
989        // Do camera parameter dependent initialization.
990        mParameters = mCameraDevice.getParameters();
991        setCameraParameters(UPDATE_PARAM_ALL);
992        // Set a listener which updates camera parameters based
993        // on changed settings.
994        SettingsManager settingsManager = mActivity.getSettingsManager();
995        settingsManager.addListener(this);
996        mCameraPreviewParamsReady = true;
997
998        startPreview();
999
1000        onCameraOpened();
1001    }
1002
1003    @Override
1004    public void onCaptureCancelled() {
1005        mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
1006        mActivity.finish();
1007    }
1008
1009    @Override
1010    public void onCaptureRetake() {
1011        if (mPaused) {
1012            return;
1013        }
1014        mUI.hidePostCaptureAlert();
1015        setupPreview();
1016    }
1017
1018    @Override
1019    public void onCaptureDone() {
1020        if (mPaused) {
1021            return;
1022        }
1023
1024        byte[] data = mJpegImageData;
1025
1026        if (mCropValue == null) {
1027            // First handle the no crop case -- just return the value. If the
1028            // caller specifies a "save uri" then write the data to its
1029            // stream. Otherwise, pass back a scaled down version of the bitmap
1030            // directly in the extras.
1031            if (mSaveUri != null) {
1032                OutputStream outputStream = null;
1033                try {
1034                    outputStream = mContentResolver.openOutputStream(mSaveUri);
1035                    outputStream.write(data);
1036                    outputStream.close();
1037
1038                    mActivity.setResultEx(Activity.RESULT_OK);
1039                    mActivity.finish();
1040                } catch (IOException ex) {
1041                    // ignore exception
1042                } finally {
1043                    CameraUtil.closeSilently(outputStream);
1044                }
1045            } else {
1046                ExifInterface exif = Exif.getExif(data);
1047                int orientation = Exif.getOrientation(exif);
1048                Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024);
1049                bitmap = CameraUtil.rotate(bitmap, orientation);
1050                mActivity.setResultEx(Activity.RESULT_OK,
1051                        new Intent("inline-data").putExtra("data", bitmap));
1052                mActivity.finish();
1053            }
1054        } else {
1055            // Save the image to a temp file and invoke the cropper
1056            Uri tempUri = null;
1057            FileOutputStream tempStream = null;
1058            try {
1059                File path = mActivity.getFileStreamPath(sTempCropFilename);
1060                path.delete();
1061                tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1062                tempStream.write(data);
1063                tempStream.close();
1064                tempUri = Uri.fromFile(path);
1065            } catch (FileNotFoundException ex) {
1066                mActivity.setResultEx(Activity.RESULT_CANCELED);
1067                mActivity.finish();
1068                return;
1069            } catch (IOException ex) {
1070                mActivity.setResultEx(Activity.RESULT_CANCELED);
1071                mActivity.finish();
1072                return;
1073            } finally {
1074                CameraUtil.closeSilently(tempStream);
1075            }
1076
1077            Bundle newExtras = new Bundle();
1078            if (mCropValue.equals("circle")) {
1079                newExtras.putString("circleCrop", "true");
1080            }
1081            if (mSaveUri != null) {
1082                newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1083            } else {
1084                newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true);
1085            }
1086            if (mActivity.isSecureCamera()) {
1087                newExtras.putBoolean(CameraUtil.KEY_SHOW_WHEN_LOCKED, true);
1088            }
1089
1090            // TODO: Share this constant.
1091            final String CROP_ACTION = "com.android.camera.action.CROP";
1092            Intent cropIntent = new Intent(CROP_ACTION);
1093
1094            cropIntent.setData(tempUri);
1095            cropIntent.putExtras(newExtras);
1096
1097            mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1098        }
1099    }
1100
1101    @Override
1102    public void onShutterButtonFocus(boolean pressed) {
1103        // Do nothing. We don't support half-press to focus anymore.
1104    }
1105
1106    @Override
1107    public void onShutterButtonClick() {
1108        if (mPaused || (mCameraState == SWITCHING_CAMERA)
1109                || (mCameraState == PREVIEW_STOPPED)) {
1110            return;
1111        }
1112
1113        // Do not take the picture if there is not enough storage.
1114        if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
1115            Log.i(TAG, "Not enough space or storage not ready. remaining="
1116                    + mActivity.getStorageSpaceBytes());
1117            return;
1118        }
1119        Log.v(TAG, "onShutterButtonClick: mCameraState=" + mCameraState);
1120
1121        if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {
1122            mUI.setSwipingEnabled(false);
1123        }
1124        // If the user wants to do a snapshot while the previous one is still
1125        // in progress, remember the fact and do it after we finish the previous
1126        // one and re-start the preview. Snapshot in progress also includes the
1127        // state that autofocus is focusing and a picture will be taken when
1128        // focus callback arrives.
1129        if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)) {
1130            if (!mIsImageCaptureIntent) {
1131                mSnapshotOnIdle = true;
1132            }
1133            return;
1134        }
1135
1136        mSnapshotOnIdle = false;
1137        mFocusManager.focusAndCapture();
1138    }
1139
1140    private void onResumeTasks() {
1141        Log.v(TAG, "Executing onResumeTasks.");
1142        mActivity.getCameraProvider().requestCamera(mCameraId);
1143
1144        mJpegPictureCallbackTime = 0;
1145        mZoomValue = 0;
1146
1147        mOnResumeTime = SystemClock.uptimeMillis();
1148        checkDisplayRotation();
1149
1150        // If first time initialization is not finished, put it in the
1151        // message queue.
1152        if (!mFirstTimeInitialized) {
1153            mHandler.sendEmptyMessage(MSG_FIRST_TIME_INIT);
1154        } else {
1155            initializeSecondTime();
1156        }
1157
1158        UsageStatistics.changeScreen(eventprotos.NavigationChange.Mode.PHOTO_CAPTURE,
1159                eventprotos.CameraEvent.InteractionCause.BUTTON);
1160
1161        Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1162        if (gsensor != null) {
1163            mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL);
1164        }
1165
1166        Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1167        if (msensor != null) {
1168            mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL);
1169        }
1170    }
1171
1172    /**
1173     * @return Whether the currently active camera is front-facing.
1174     */
1175    private boolean isCameraFrontFacing() {
1176        CameraInfo info = mAppController.getCameraProvider().getCameraInfo()[mCameraId];
1177        return info.facing == CameraInfo.CAMERA_FACING_FRONT;
1178    }
1179
1180    /**
1181     * The focus manager is the first UI related element to get initialized, and
1182     * it requires the RenderOverlay, so initialize it here
1183     */
1184    private void initializeFocusManager() {
1185        // Create FocusManager object. startPreview needs it.
1186        // if mFocusManager not null, reuse it
1187        // otherwise create a new instance
1188        if (mFocusManager != null) {
1189            mFocusManager.removeMessages();
1190        } else {
1191            mMirror = isCameraFrontFacing();
1192            String[] defaultFocusModes = mActivity.getResources().getStringArray(
1193                    R.array.pref_camera_focusmode_default_array);
1194            mFocusManager = new FocusOverlayManager(mActivity.getSettingsManager(),
1195                    defaultFocusModes,
1196                    mInitialParams, this, mMirror,
1197                    mActivity.getMainLooper(), mUI.getFocusUI());
1198        }
1199        mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
1200    }
1201
1202    @Override
1203    public void resume() {
1204        mPaused = false;
1205        if (mFocusManager != null) {
1206            // If camera is not open when resume is called, focus manager will
1207            // not
1208            // be initialized yet, in which case it will start listening to
1209            // preview area size change later in the initialization.
1210            mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
1211        }
1212
1213        if (mUI.getPreviewAreaSizeChangedListener() != null) {
1214            mAppController.addPreviewAreaSizeChangedListener(
1215                    mUI.getPreviewAreaSizeChangedListener());
1216        }
1217
1218        // Add delay on resume from lock screen only, in order to to speed up
1219        // the onResume --> onPause --> onResume cycle from lock screen.
1220        // Don't do always because letting go of thread can cause delay.
1221        String action = mActivity.getIntent().getAction();
1222        if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action)
1223                || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)) {
1224            Log.v(TAG, "On resume, from lock screen.");
1225            // Note: onPauseAfterSuper() will delete this runnable, so we will
1226            // at most have 1 copy queued up.
1227            mHandler.postDelayed(new Runnable() {
1228                @Override
1229                public void run() {
1230                    onResumeTasks();
1231                }
1232            }, ON_RESUME_TASKS_DELAY_MSEC);
1233        } else {
1234            Log.v(TAG, "On resume.");
1235            onResumeTasks();
1236        }
1237    }
1238
1239    @Override
1240    public void pause() {
1241        mPaused = true;
1242        Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1243        if (gsensor != null) {
1244            mSensorManager.unregisterListener(this, gsensor);
1245        }
1246
1247        Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1248        if (msensor != null) {
1249            mSensorManager.unregisterListener(this, msensor);
1250        }
1251
1252        // Reset the focus first. Camera CTS does not guarantee that
1253        // cancelAutoFocus is allowed after preview stops.
1254        if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1255            mCameraDevice.cancelAutoFocus();
1256        }
1257
1258        // If the camera has not been opened asynchronously yet,
1259        // and startPreview hasn't been called, then this is a no-op.
1260        // (e.g. onResume -> onPause -> onResume).
1261        stopPreview();
1262
1263        mNamedImages = null;
1264
1265        // If we are in an image capture intent and has taken
1266        // a picture, we just clear it in onPause.
1267        mJpegImageData = null;
1268
1269        // Remove the messages and runnables in the queue.
1270        mHandler.removeCallbacksAndMessages(null);
1271
1272        closeCamera();
1273        mActivity.enableKeepScreenOn(false);
1274        mUI.onPause();
1275
1276        mPendingSwitchCameraId = -1;
1277        if (mFocusManager != null) {
1278            mFocusManager.removeMessages();
1279        }
1280        getServices().getMemoryManager().removeListener(this);
1281        mAppController.removePreviewAreaSizeChangedListener(mFocusManager);
1282        if (mUI.getPreviewAreaSizeChangedListener() != null) {
1283            mAppController.removePreviewAreaSizeChangedListener(
1284                    mUI.getPreviewAreaSizeChangedListener());
1285        }
1286
1287        SettingsManager settingsManager = mActivity.getSettingsManager();
1288        settingsManager.removeListener(this);
1289    }
1290
1291    @Override
1292    public void destroy() {
1293        // TODO: implement this.
1294    }
1295
1296    @Override
1297    public void onLayoutOrientationChanged(boolean isLandscape) {
1298        setDisplayOrientation();
1299    }
1300
1301    @Override
1302    public void updateCameraOrientation() {
1303        if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) {
1304            setDisplayOrientation();
1305        }
1306    }
1307
1308    private boolean canTakePicture() {
1309        return isCameraIdle()
1310                && (mActivity.getStorageSpaceBytes() > Storage.LOW_STORAGE_THRESHOLD_BYTES);
1311    }
1312
1313    @Override
1314    public void autoFocus() {
1315        mFocusStartTime = System.currentTimeMillis();
1316        mCameraDevice.autoFocus(mHandler, mAutoFocusCallback);
1317        setCameraState(FOCUSING);
1318    }
1319
1320    @Override
1321    public void cancelAutoFocus() {
1322        mCameraDevice.cancelAutoFocus();
1323        setCameraState(IDLE);
1324        setCameraParameters(UPDATE_PARAM_PREFERENCE);
1325    }
1326
1327    @Override
1328    public void onSingleTapUp(View view, int x, int y) {
1329        if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1330                || mCameraState == SNAPSHOT_IN_PROGRESS
1331                || mCameraState == SWITCHING_CAMERA
1332                || mCameraState == PREVIEW_STOPPED) {
1333            return;
1334        }
1335
1336        // Check if metering area or focus area is supported.
1337        if (!mFocusAreaSupported && !mMeteringAreaSupported) {
1338            return;
1339        }
1340        mFocusManager.onSingleTapUp(x, y);
1341    }
1342
1343    @Override
1344    public boolean onBackPressed() {
1345        return mUI.onBackPressed();
1346    }
1347
1348    @Override
1349    public boolean onKeyDown(int keyCode, KeyEvent event) {
1350        switch (keyCode) {
1351            case KeyEvent.KEYCODE_VOLUME_UP:
1352            case KeyEvent.KEYCODE_VOLUME_DOWN:
1353            case KeyEvent.KEYCODE_FOCUS:
1354                if (/* TODO: mActivity.isInCameraApp() && */mFirstTimeInitialized) {
1355                    if (event.getRepeatCount() == 0) {
1356                        onShutterButtonFocus(true);
1357                    }
1358                    return true;
1359                }
1360                return false;
1361            case KeyEvent.KEYCODE_CAMERA:
1362                if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1363                    onShutterButtonClick();
1364                }
1365                return true;
1366            case KeyEvent.KEYCODE_DPAD_CENTER:
1367                // If we get a dpad center event without any focused view, move
1368                // the focus to the shutter button and press it.
1369                if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1370                    // Start auto-focus immediately to reduce shutter lag. After
1371                    // the shutter button gets the focus, onShutterButtonFocus()
1372                    // will be called again but it is fine.
1373                    onShutterButtonFocus(true);
1374                    mUI.pressShutterButton();
1375                }
1376                return true;
1377        }
1378        return false;
1379    }
1380
1381    @Override
1382    public boolean onKeyUp(int keyCode, KeyEvent event) {
1383        switch (keyCode) {
1384            case KeyEvent.KEYCODE_VOLUME_UP:
1385            case KeyEvent.KEYCODE_VOLUME_DOWN:
1386                if (/* mActivity.isInCameraApp() && */mFirstTimeInitialized) {
1387                    onShutterButtonClick();
1388                    return true;
1389                }
1390                return false;
1391            case KeyEvent.KEYCODE_FOCUS:
1392                if (mFirstTimeInitialized) {
1393                    onShutterButtonFocus(false);
1394                }
1395                return true;
1396        }
1397        return false;
1398    }
1399
1400    private void closeCamera() {
1401        if (mCameraDevice != null) {
1402            stopFaceDetection();
1403            mCameraDevice.setZoomChangeListener(null);
1404            mCameraDevice.setFaceDetectionCallback(null, null);
1405            mCameraDevice.setErrorCallback(null);
1406
1407            mFaceDetectionStarted = false;
1408            mActivity.getCameraProvider().releaseCamera(mCameraDevice.getCameraId());
1409            mCameraDevice = null;
1410            setCameraState(PREVIEW_STOPPED);
1411            mFocusManager.onCameraReleased();
1412        }
1413    }
1414
1415    private void setDisplayOrientation() {
1416        mDisplayRotation = CameraUtil.getDisplayRotation(mActivity);
1417        mDisplayOrientation = CameraUtil.getDisplayOrientation(mDisplayRotation, mCameraId);
1418        mCameraDisplayOrientation = mDisplayOrientation;
1419        mUI.setDisplayOrientation(mDisplayOrientation);
1420        if (mFocusManager != null) {
1421            mFocusManager.setDisplayOrientation(mDisplayOrientation);
1422        }
1423        // Change the camera display orientation
1424        if (mCameraDevice != null) {
1425            mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);
1426        }
1427    }
1428
1429    /** Only called by UI thread. */
1430    private void setupPreview() {
1431        mFocusManager.resetTouchFocus();
1432        startPreview();
1433    }
1434
1435    /**
1436     * Returns whether we can/should start the preview or not.
1437     */
1438    private boolean checkPreviewPreconditions() {
1439        if (mPaused) {
1440            return false;
1441        }
1442
1443        if (mCameraDevice == null) {
1444            Log.w(TAG, "startPreview: camera device not ready yet.");
1445            return false;
1446        }
1447
1448        SurfaceTexture st = mActivity.getCameraAppUI().getSurfaceTexture();
1449        if (st == null) {
1450            Log.w(TAG, "startPreview: surfaceTexture is not ready.");
1451            return false;
1452        }
1453
1454        if (!mCameraPreviewParamsReady) {
1455            Log.w(TAG, "startPreview: parameters for preview is not ready.");
1456            return false;
1457        }
1458        return true;
1459    }
1460
1461    /**
1462     * The start/stop preview should only run on the UI thread.
1463     */
1464    private void startPreview() {
1465        if (!checkPreviewPreconditions()) {
1466            return;
1467        }
1468
1469        mCameraDevice.setErrorCallback(mErrorCallback);
1470        // ICS camera frameworks has a bug. Face detection state is not cleared
1471        // after taking a picture. Stop the preview to work around it. The bug
1472        // was fixed in JB.
1473        if (mCameraState != PREVIEW_STOPPED) {
1474            stopPreview();
1475        }
1476
1477        setDisplayOrientation();
1478
1479        if (!mSnapshotOnIdle) {
1480            // If the focus mode is continuous autofocus, call cancelAutoFocus
1481            // to resume it because it may have been paused by autoFocus call.
1482            String focusMode = mFocusManager.getFocusMode();
1483            if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(focusMode)) {
1484                mCameraDevice.cancelAutoFocus();
1485            }
1486            mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1487        }
1488        setCameraParameters(UPDATE_PARAM_ALL);
1489        // Let UI set its expected aspect ratio
1490        mCameraDevice.setPreviewTexture(mActivity.getCameraAppUI().getSurfaceTexture());
1491
1492        Log.v(TAG, "startPreview");
1493        mCameraDevice.startPreview();
1494
1495        mFocusManager.onPreviewStarted();
1496        onPreviewStarted();
1497
1498        if (mSnapshotOnIdle) {
1499            mHandler.post(mDoSnapRunnable);
1500        }
1501    }
1502
1503    @Override
1504    public void stopPreview() {
1505        if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1506            Log.v(TAG, "stopPreview");
1507            mCameraDevice.stopPreview();
1508            mFaceDetectionStarted = false;
1509        }
1510        setCameraState(PREVIEW_STOPPED);
1511        if (mFocusManager != null) {
1512            mFocusManager.onPreviewStopped();
1513        }
1514    }
1515
1516    @Override
1517    public void onSettingChanged(SettingsManager settingsManager, int id) {
1518        switch (id) {
1519            case SettingsManager.SETTING_FLASH_MODE: {
1520                updateParametersFlashMode();
1521                break;
1522            }
1523            default: {
1524                // Do nothing.
1525            }
1526        }
1527
1528        if (mCameraDevice != null) {
1529            mCameraDevice.setParameters(mParameters);
1530        }
1531    }
1532
1533    private void updateCameraParametersInitialize() {
1534        // Reset preview frame rate to the maximum because it may be lowered by
1535        // video camera application.
1536        int[] fpsRange = CameraUtil.getPhotoPreviewFpsRange(mParameters);
1537        if (fpsRange != null && fpsRange.length > 0) {
1538            mParameters.setPreviewFpsRange(
1539                    fpsRange[Parameters.PREVIEW_FPS_MIN_INDEX],
1540                    fpsRange[Parameters.PREVIEW_FPS_MAX_INDEX]);
1541        }
1542
1543        mParameters.set(CameraUtil.RECORDING_HINT, CameraUtil.FALSE);
1544
1545        // Disable video stabilization. Convenience methods not available in API
1546        // level <= 14
1547        String vstabSupported = mParameters.get("video-stabilization-supported");
1548        if ("true".equals(vstabSupported)) {
1549            mParameters.set("video-stabilization", "false");
1550        }
1551    }
1552
1553    private void updateCameraParametersZoom() {
1554        // Set zoom.
1555        if (mParameters.isZoomSupported()) {
1556            mParameters.setZoom(mZoomValue);
1557        }
1558    }
1559
1560    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
1561    private void setAutoExposureLockIfSupported() {
1562        if (mAeLockSupported) {
1563            mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock());
1564        }
1565    }
1566
1567    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
1568    private void setAutoWhiteBalanceLockIfSupported() {
1569        if (mAwbLockSupported) {
1570            mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
1571        }
1572    }
1573
1574    private void setFocusAreasIfSupported() {
1575        if (mFocusAreaSupported) {
1576            mParameters.setFocusAreas(mFocusManager.getFocusAreas());
1577        }
1578    }
1579
1580    private void setMeteringAreasIfSupported() {
1581        if (mMeteringAreaSupported) {
1582            mParameters.setMeteringAreas(mFocusManager.getMeteringAreas());
1583        }
1584    }
1585
1586    private void updateCameraParametersPreference() {
1587        setAutoExposureLockIfSupported();
1588        setAutoWhiteBalanceLockIfSupported();
1589        setFocusAreasIfSupported();
1590        setMeteringAreasIfSupported();
1591
1592        // Initialize focus mode.
1593        mFocusManager.overrideFocusMode(null);
1594        mParameters.setFocusMode(mFocusManager.getFocusMode());
1595
1596        // Set picture size.
1597        updateParametersPictureSize();
1598
1599        // Set JPEG quality.
1600        updateParametersPictureQuality();
1601
1602        // For the following settings, we need to check if the settings are
1603        // still supported by latest driver, if not, ignore the settings.
1604
1605        // Set exposure compensation
1606        updateParametersExposureCompensation();
1607
1608        // Set the scene mode: also sets flash and white balance.
1609        updateParametersSceneMode();
1610
1611        if (mContinuousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
1612            updateAutoFocusMoveCallback();
1613        }
1614    }
1615
1616    private void updateParametersPictureSize() {
1617        SettingsManager settingsManager = mActivity.getSettingsManager();
1618        String pictureSize = settingsManager
1619                .get(isCameraFrontFacing() ? SettingsManager.SETTING_PICTURE_SIZE_FRONT
1620                        : SettingsManager.SETTING_PICTURE_SIZE_BACK);
1621
1622        List<Size> supported = mParameters.getSupportedPictureSizes();
1623        SettingsUtil.setCameraPictureSize(pictureSize, supported, mParameters,
1624                mCameraDevice.getCameraId());
1625        Size size = mParameters.getPictureSize();
1626
1627        // Set a preview size that is closest to the viewfinder height and has
1628        // the right aspect ratio.
1629        List<Size> sizes = mParameters.getSupportedPreviewSizes();
1630        Size optimalSize = CameraUtil.getOptimalPreviewSize(mActivity, sizes,
1631                (double) size.width / size.height);
1632        Size original = mParameters.getPreviewSize();
1633        if (!original.equals(optimalSize)) {
1634            mParameters.setPreviewSize(optimalSize.width, optimalSize.height);
1635
1636            // Zoom related settings will be changed for different preview
1637            // sizes, so set and read the parameters to get latest values
1638            if (mHandler.getLooper() == Looper.myLooper()) {
1639                // On UI thread only, not when camera starts up
1640                setupPreview();
1641            } else {
1642                mCameraDevice.setParameters(mParameters);
1643            }
1644            mParameters = mCameraDevice.getParameters();
1645        }
1646
1647        if (optimalSize.width != 0 && optimalSize.height != 0) {
1648            mUI.updatePreviewAspectRatio((float) optimalSize.width
1649                    / (float) optimalSize.height);
1650        }
1651        Log.v(TAG, "Preview size is " + optimalSize.width + "x" + optimalSize.height);
1652    }
1653
1654    private void updateParametersPictureQuality() {
1655        int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
1656                CameraProfile.QUALITY_HIGH);
1657        mParameters.setJpegQuality(jpegQuality);
1658    }
1659
1660    private void updateParametersExposureCompensation() {
1661        SettingsManager settingsManager = mActivity.getSettingsManager();
1662
1663        int value = Integer.parseInt(settingsManager.get(SettingsManager.SETTING_EXPOSURE));
1664        int max = mParameters.getMaxExposureCompensation();
1665        int min = mParameters.getMinExposureCompensation();
1666        if (value >= min && value <= max) {
1667            mParameters.setExposureCompensation(value);
1668        } else {
1669            Log.w(TAG, "invalid exposure range: " + value);
1670        }
1671    }
1672
1673    private void updateParametersSceneMode() {
1674        SettingsManager settingsManager = mActivity.getSettingsManager();
1675
1676        mSceneMode = settingsManager.get(SettingsManager.SETTING_SCENE_MODE);
1677        if (CameraUtil.isSupported(mSceneMode, mParameters.getSupportedSceneModes())) {
1678            if (!mParameters.getSceneMode().equals(mSceneMode)) {
1679                mParameters.setSceneMode(mSceneMode);
1680
1681                // Setting scene mode will change the settings of flash mode,
1682                // white balance, and focus mode. Here we read back the
1683                // parameters, so we can know those settings.
1684                mCameraDevice.setParameters(mParameters);
1685                mParameters = mCameraDevice.getParameters();
1686            }
1687        } else {
1688            mSceneMode = mParameters.getSceneMode();
1689            if (mSceneMode == null) {
1690                mSceneMode = Parameters.SCENE_MODE_AUTO;
1691            }
1692        }
1693
1694        if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
1695            // Set flash mode.
1696            updateParametersFlashMode();
1697
1698            // Set white balance mode.
1699            updateParametersWhiteBalanceMode();
1700
1701            // Set focus mode.
1702            mFocusManager.overrideFocusMode(null);
1703            mParameters.setFocusMode(mFocusManager.getFocusMode());
1704        } else {
1705            mFocusManager.overrideFocusMode(mParameters.getFocusMode());
1706        }
1707    }
1708
1709    private void updateParametersFlashMode() {
1710        SettingsManager settingsManager = mActivity.getSettingsManager();
1711
1712        String flashMode = settingsManager.get(SettingsManager.SETTING_FLASH_MODE);
1713        List<String> supportedFlash = mParameters.getSupportedFlashModes();
1714        if (CameraUtil.isSupported(flashMode, supportedFlash)) {
1715            mParameters.setFlashMode(flashMode);
1716        }
1717    }
1718
1719    private void updateParametersWhiteBalanceMode() {
1720        SettingsManager settingsManager = mActivity.getSettingsManager();
1721
1722        // Set white balance parameter.
1723        String whiteBalance = settingsManager.get(SettingsManager.SETTING_WHITE_BALANCE);
1724        if (CameraUtil.isSupported(whiteBalance,
1725                mParameters.getSupportedWhiteBalance())) {
1726            mParameters.setWhiteBalance(whiteBalance);
1727        }
1728    }
1729
1730    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
1731    private void updateAutoFocusMoveCallback() {
1732        if (mParameters.getFocusMode().equals(CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE)) {
1733            mCameraDevice.setAutoFocusMoveCallback(mHandler,
1734                    (CameraAFMoveCallback) mAutoFocusMoveCallback);
1735        } else {
1736            mCameraDevice.setAutoFocusMoveCallback(null, null);
1737        }
1738    }
1739
1740    // We separate the parameters into several subsets, so we can update only
1741    // the subsets actually need updating. The PREFERENCE set needs extra
1742    // locking because the preference can be changed from GLThread as well.
1743    private void setCameraParameters(int updateSet) {
1744        if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
1745            updateCameraParametersInitialize();
1746        }
1747
1748        if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
1749            updateCameraParametersZoom();
1750        }
1751
1752        if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
1753            updateCameraParametersPreference();
1754        }
1755
1756        mCameraDevice.setParameters(mParameters);
1757    }
1758
1759    // If the Camera is idle, update the parameters immediately, otherwise
1760    // accumulate them in mUpdateSet and update later.
1761    private void setCameraParametersWhenIdle(int additionalUpdateSet) {
1762        mUpdateSet |= additionalUpdateSet;
1763        if (mCameraDevice == null) {
1764            // We will update all the parameters when we open the device, so
1765            // we don't need to do anything now.
1766            mUpdateSet = 0;
1767            return;
1768        } else if (isCameraIdle()) {
1769            setCameraParameters(mUpdateSet);
1770            updateSceneMode();
1771            mUpdateSet = 0;
1772        } else {
1773            if (!mHandler.hasMessages(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
1774                mHandler.sendEmptyMessageDelayed(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
1775            }
1776        }
1777    }
1778
1779    @Override
1780    public boolean isCameraIdle() {
1781        return (mCameraState == IDLE) ||
1782                (mCameraState == PREVIEW_STOPPED) ||
1783                ((mFocusManager != null) && mFocusManager.isFocusCompleted()
1784                && (mCameraState != SWITCHING_CAMERA));
1785    }
1786
1787    @Override
1788    public boolean isImageCaptureIntent() {
1789        String action = mActivity.getIntent().getAction();
1790        return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
1791        || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
1792    }
1793
1794    private void setupCaptureParams() {
1795        Bundle myExtras = mActivity.getIntent().getExtras();
1796        if (myExtras != null) {
1797            mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
1798            mCropValue = myExtras.getString("crop");
1799        }
1800    }
1801
1802    private void initializeCapabilities() {
1803        mInitialParams = mCameraDevice.getParameters();
1804        mFocusAreaSupported = CameraUtil.isFocusAreaSupported(mInitialParams);
1805        mMeteringAreaSupported = CameraUtil.isMeteringAreaSupported(mInitialParams);
1806        mAeLockSupported = CameraUtil.isAutoExposureLockSupported(mInitialParams);
1807        mAwbLockSupported = CameraUtil.isAutoWhiteBalanceLockSupported(mInitialParams);
1808        mContinuousFocusSupported = mInitialParams.getSupportedFocusModes().contains(
1809                CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE);
1810    }
1811
1812    private void setShutterEnabled(boolean enabled) {
1813        mShutterEnabled = enabled;
1814        mUI.enableShutter(enabled);
1815    }
1816
1817    // TODO: Remove this
1818    @Override
1819    public int onZoomChanged(int index) {
1820        // Not useful to change zoom value when the activity is paused.
1821        if (mPaused) {
1822            return index;
1823        }
1824        mZoomValue = index;
1825        if (mParameters == null || mCameraDevice == null) {
1826            return index;
1827        }
1828        // Set zoom parameters asynchronously
1829        mParameters.setZoom(mZoomValue);
1830        mCameraDevice.setParameters(mParameters);
1831        Parameters p = mCameraDevice.getParameters();
1832        if (p != null) {
1833            return p.getZoom();
1834        }
1835        return index;
1836    }
1837
1838    @Override
1839    public int getCameraState() {
1840        return mCameraState;
1841    }
1842
1843    @Override
1844    public void onMemoryStateChanged(int state) {
1845        setShutterEnabled(state == MemoryManager.STATE_OK);
1846    }
1847
1848    @Override
1849    public void onLowMemory() {
1850        // Not much we can do in the photo module.
1851    }
1852
1853    @Override
1854    public void onAccuracyChanged(Sensor sensor, int accuracy) {
1855    }
1856
1857    @Override
1858    public void onSensorChanged(SensorEvent event) {
1859        int type = event.sensor.getType();
1860        float[] data;
1861        if (type == Sensor.TYPE_ACCELEROMETER) {
1862            data = mGData;
1863        } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
1864            data = mMData;
1865        } else {
1866            // we should not be here.
1867            return;
1868        }
1869        for (int i = 0; i < 3; i++) {
1870            data[i] = event.values[i];
1871        }
1872        float[] orientation = new float[3];
1873        SensorManager.getRotationMatrix(mR, null, mGData, mMData);
1874        SensorManager.getOrientation(mR, orientation);
1875        mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
1876        if (mHeading < 0) {
1877            mHeading += 360;
1878        }
1879    }
1880
1881    // For debugging only.
1882    public void setDebugUri(Uri uri) {
1883        mDebugUri = uri;
1884    }
1885
1886    // For debugging only.
1887    private void saveToDebugUri(byte[] data) {
1888        if (mDebugUri != null) {
1889            OutputStream outputStream = null;
1890            try {
1891                outputStream = mContentResolver.openOutputStream(mDebugUri);
1892                outputStream.write(data);
1893                outputStream.close();
1894            } catch (IOException e) {
1895                Log.e(TAG, "Exception while writing debug jpeg file", e);
1896            } finally {
1897                CameraUtil.closeSilently(outputStream);
1898            }
1899        }
1900    }
1901}
1902