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