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