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