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