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