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