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