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