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