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