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