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.app.AlertDialog;
     22 import android.content.ContentProviderClient;
     23 import android.content.ContentResolver;
     24 import android.content.DialogInterface;
     25 import android.content.Intent;
     26 import android.content.SharedPreferences.Editor;
     27 import android.content.res.Configuration;
     28 import android.graphics.Bitmap;
     29 import android.graphics.SurfaceTexture;
     30 import android.hardware.Camera.CameraInfo;
     31 import android.hardware.Camera.Face;
     32 import android.hardware.Camera.FaceDetectionListener;
     33 import android.hardware.Camera.Parameters;
     34 import android.hardware.Camera.PictureCallback;
     35 import android.hardware.Camera.Size;
     36 import android.location.Location;
     37 import android.media.CameraProfile;
     38 import android.net.Uri;
     39 import android.os.Bundle;
     40 import android.os.ConditionVariable;
     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.Gravity;
     49 import android.view.KeyEvent;
     50 import android.view.LayoutInflater;
     51 import android.view.MotionEvent;
     52 import android.view.OrientationEventListener;
     53 import android.view.SurfaceHolder;
     54 import android.view.View;
     55 import android.view.View.OnClickListener;
     56 import android.view.ViewGroup;
     57 import android.view.WindowManager;
     58 import android.widget.FrameLayout;
     59 import android.widget.FrameLayout.LayoutParams;
     60 import android.widget.ImageView;
     61 import android.widget.Toast;
     62 
     63 import com.android.camera.CameraManager.CameraProxy;
     64 import com.android.camera.ui.AbstractSettingPopup;
     65 import com.android.camera.ui.FaceView;
     66 import com.android.camera.ui.PieRenderer;
     67 import com.android.camera.ui.PopupManager;
     68 import com.android.camera.ui.PreviewSurfaceView;
     69 import com.android.camera.ui.RenderOverlay;
     70 import com.android.camera.ui.Rotatable;
     71 import com.android.camera.ui.RotateLayout;
     72 import com.android.camera.ui.RotateTextToast;
     73 import com.android.camera.ui.TwoStateImageView;
     74 import com.android.camera.ui.ZoomRenderer;
     75 import com.android.gallery3d.app.CropImage;
     76 import com.android.gallery3d.common.ApiHelper;
     77 
     78 import java.io.File;
     79 import java.io.FileNotFoundException;
     80 import java.io.FileOutputStream;
     81 import java.io.IOException;
     82 import java.io.OutputStream;
     83 import java.util.ArrayList;
     84 import java.util.Collections;
     85 import java.util.Formatter;
     86 import java.util.List;
     87 
     88 public class PhotoModule
     89     implements CameraModule,
     90     FocusOverlayManager.Listener,
     91     CameraPreference.OnPreferenceChangedListener,
     92     LocationManager.Listener,
     93     PreviewFrameLayout.OnSizeChangedListener,
     94     ShutterButton.OnShutterButtonListener,
     95     SurfaceHolder.Callback,
     96     PieRenderer.PieListener {
     97 
     98     private static final String TAG = "CAM_PhotoModule";
     99 
    100     // We number the request code from 1000 to avoid collision with Gallery.
    101     private static final int REQUEST_CROP = 1000;
    102 
    103     private static final int SETUP_PREVIEW = 1;
    104     private static final int FIRST_TIME_INIT = 2;
    105     private static final int CLEAR_SCREEN_DELAY = 3;
    106     private static final int SET_CAMERA_PARAMETERS_WHEN_IDLE = 4;
    107     private static final int CHECK_DISPLAY_ROTATION = 5;
    108     private static final int SHOW_TAP_TO_FOCUS_TOAST = 6;
    109     private static final int SWITCH_CAMERA = 7;
    110     private static final int SWITCH_CAMERA_START_ANIMATION = 8;
    111     private static final int CAMERA_OPEN_DONE = 9;
    112     private static final int START_PREVIEW_DONE = 10;
    113     private static final int OPEN_CAMERA_FAIL = 11;
    114     private static final int CAMERA_DISABLED = 12;
    115 
    116     // The subset of parameters we need to update in setCameraParameters().
    117     private static final int UPDATE_PARAM_INITIALIZE = 1;
    118     private static final int UPDATE_PARAM_ZOOM = 2;
    119     private static final int UPDATE_PARAM_PREFERENCE = 4;
    120     private static final int UPDATE_PARAM_ALL = -1;
    121 
    122     // This is the timeout to keep the camera in onPause for the first time
    123     // after screen on if the activity is started from secure lock screen.
    124     private static final int KEEP_CAMERA_TIMEOUT = 1000; // ms
    125 
    126     // copied from Camera hierarchy
    127     private CameraActivity mActivity;
    128     private View mRootView;
    129     private CameraProxy mCameraDevice;
    130     private int mCameraId;
    131     private Parameters mParameters;
    132     private boolean mPaused;
    133     private AbstractSettingPopup mPopup;
    134 
    135     // these are only used by Camera
    136 
    137     // The activity is going to switch to the specified camera id. This is
    138     // needed because texture copy is done in GL thread. -1 means camera is not
    139     // switching.
    140     protected int mPendingSwitchCameraId = -1;
    141     private boolean mOpenCameraFail;
    142     private boolean mCameraDisabled;
    143 
    144     // When setCameraParametersWhenIdle() is called, we accumulate the subsets
    145     // needed to be updated in mUpdateSet.
    146     private int mUpdateSet;
    147 
    148     private static final int SCREEN_DELAY = 2 * 60 * 1000;
    149 
    150     private int mZoomValue;  // The current zoom value.
    151     private int mZoomMax;
    152     private List<Integer> mZoomRatios;
    153 
    154     private Parameters mInitialParams;
    155     private boolean mFocusAreaSupported;
    156     private boolean mMeteringAreaSupported;
    157     private boolean mAeLockSupported;
    158     private boolean mAwbLockSupported;
    159     private boolean mContinousFocusSupported;
    160 
    161     // The degrees of the device rotated clockwise from its natural orientation.
    162     private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
    163     // The orientation compensation for icons and dialogs. Ex: if the value
    164     // is 90, the UI components should be rotated 90 degrees counter-clockwise.
    165     private int mOrientationCompensation = 0;
    166     private ComboPreferences mPreferences;
    167 
    168     private static final String sTempCropFilename = "crop-temp";
    169 
    170     private ContentProviderClient mMediaProviderClient;
    171     private ShutterButton mShutterButton;
    172     private boolean mFaceDetectionStarted = false;
    173 
    174     private PreviewFrameLayout mPreviewFrameLayout;
    175     private Object mSurfaceTexture;
    176 
    177     // for API level 10
    178     private PreviewSurfaceView mPreviewSurfaceView;
    179     private volatile SurfaceHolder mCameraSurfaceHolder;
    180 
    181     private FaceView mFaceView;
    182     private RenderOverlay mRenderOverlay;
    183     private Rotatable mReviewCancelButton;
    184     private Rotatable mReviewDoneButton;
    185     private View mReviewRetakeButton;
    186 
    187     // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
    188     private String mCropValue;
    189     private Uri mSaveUri;
    190 
    191     private View mMenu;
    192     private View mBlocker;
    193 
    194     // Small indicators which show the camera settings in the viewfinder.
    195     private ImageView mExposureIndicator;
    196     private ImageView mFlashIndicator;
    197     private ImageView mSceneIndicator;
    198     private ImageView mHdrIndicator;
    199     // A view group that contains all the small indicators.
    200     private View mOnScreenIndicators;
    201 
    202     // We use a thread in ImageSaver to do the work of saving images. This
    203     // reduces the shot-to-shot time.
    204     private ImageSaver mImageSaver;
    205     // Similarly, we use a thread to generate the name of the picture and insert
    206     // it into MediaStore while picture taking is still in progress.
    207     private ImageNamer mImageNamer;
    208 
    209     private Runnable mDoSnapRunnable = new Runnable() {
    210         @Override
    211         public void run() {
    212             onShutterButtonClick();
    213         }
    214     };
    215 
    216     private final StringBuilder mBuilder = new StringBuilder();
    217     private final Formatter mFormatter = new Formatter(mBuilder);
    218     private final Object[] mFormatterArgs = new Object[1];
    219 
    220     /**
    221      * An unpublished intent flag requesting to return as soon as capturing
    222      * is completed.
    223      *
    224      * TODO: consider publishing by moving into MediaStore.
    225      */
    226     private static final String EXTRA_QUICK_CAPTURE =
    227             "android.intent.extra.quickCapture";
    228 
    229     // The display rotation in degrees. This is only valid when mCameraState is
    230     // not PREVIEW_STOPPED.
    231     private int mDisplayRotation;
    232     // The value for android.hardware.Camera.setDisplayOrientation.
    233     private int mCameraDisplayOrientation;
    234     // The value for UI components like indicators.
    235     private int mDisplayOrientation;
    236     // The value for android.hardware.Camera.Parameters.setRotation.
    237     private int mJpegRotation;
    238     private boolean mFirstTimeInitialized;
    239     private boolean mIsImageCaptureIntent;
    240 
    241     private static final int PREVIEW_STOPPED = 0;
    242     private static final int IDLE = 1;  // preview is active
    243     // Focus is in progress. The exact focus state is in Focus.java.
    244     private static final int FOCUSING = 2;
    245     private static final int SNAPSHOT_IN_PROGRESS = 3;
    246     // Switching between cameras.
    247     private static final int SWITCHING_CAMERA = 4;
    248     private int mCameraState = PREVIEW_STOPPED;
    249     private boolean mSnapshotOnIdle = false;
    250 
    251     private ContentResolver mContentResolver;
    252 
    253     private LocationManager mLocationManager;
    254 
    255     private final ShutterCallback mShutterCallback = new ShutterCallback();
    256     private final PostViewPictureCallback mPostViewPictureCallback =
    257             new PostViewPictureCallback();
    258     private final RawPictureCallback mRawPictureCallback =
    259             new RawPictureCallback();
    260     private final AutoFocusCallback mAutoFocusCallback =
    261             new AutoFocusCallback();
    262     private final Object mAutoFocusMoveCallback =
    263             ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
    264             ? new AutoFocusMoveCallback()
    265             : null;
    266 
    267     private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
    268 
    269     private long mFocusStartTime;
    270     private long mShutterCallbackTime;
    271     private long mPostViewPictureCallbackTime;
    272     private long mRawPictureCallbackTime;
    273     private long mJpegPictureCallbackTime;
    274     private long mOnResumeTime;
    275     private byte[] mJpegImageData;
    276 
    277     // These latency time are for the CameraLatency test.
    278     public long mAutoFocusTime;
    279     public long mShutterLag;
    280     public long mShutterToPictureDisplayedTime;
    281     public long mPictureDisplayedToJpegCallbackTime;
    282     public long mJpegCallbackFinishTime;
    283     public long mCaptureStartTime;
    284 
    285     // This handles everything about focus.
    286     private FocusOverlayManager mFocusManager;
    287 
    288     private PieRenderer mPieRenderer;
    289     private PhotoController mPhotoControl;
    290 
    291     private ZoomRenderer mZoomRenderer;
    292 
    293     private String mSceneMode;
    294     private Toast mNotSelectableToast;
    295 
    296     private final Handler mHandler = new MainHandler();
    297     private PreferenceGroup mPreferenceGroup;
    298 
    299     private boolean mQuickCapture;
    300 
    301     CameraStartUpThread mCameraStartUpThread;
    302     ConditionVariable mStartPreviewPrerequisiteReady = new ConditionVariable();
    303 
    304     private PreviewGestures mGestures;
    305 
    306     // The purpose is not to block the main thread in onCreate and onResume.
    307     private class CameraStartUpThread extends Thread {
    308         private volatile boolean mCancelled;
    309 
    310         public void cancel() {
    311             mCancelled = true;
    312         }
    313 
    314         @Override
    315         public void run() {
    316             try {
    317                 // We need to check whether the activity is paused before long
    318                 // operations to ensure that onPause() can be done ASAP.
    319                 if (mCancelled) return;
    320                 mCameraDevice = Util.openCamera(mActivity, mCameraId);
    321                 mParameters = mCameraDevice.getParameters();
    322                 // Wait until all the initialization needed by startPreview are
    323                 // done.
    324                 mStartPreviewPrerequisiteReady.block();
    325 
    326                 initializeCapabilities();
    327                 if (mFocusManager == null) initializeFocusManager();
    328                 if (mCancelled) return;
    329                 setCameraParameters(UPDATE_PARAM_ALL);
    330                 mHandler.sendEmptyMessage(CAMERA_OPEN_DONE);
    331                 if (mCancelled) return;
    332                 startPreview();
    333                 mHandler.sendEmptyMessage(START_PREVIEW_DONE);
    334                 mOnResumeTime = SystemClock.uptimeMillis();
    335                 mHandler.sendEmptyMessage(CHECK_DISPLAY_ROTATION);
    336             } catch (CameraHardwareException e) {
    337                 mHandler.sendEmptyMessage(OPEN_CAMERA_FAIL);
    338             } catch (CameraDisabledException e) {
    339                 mHandler.sendEmptyMessage(CAMERA_DISABLED);
    340             }
    341         }
    342     }
    343 
    344     /**
    345      * This Handler is used to post message back onto the main thread of the
    346      * application
    347      */
    348     private class MainHandler extends Handler {
    349         @Override
    350         public void handleMessage(Message msg) {
    351             switch (msg.what) {
    352                 case SETUP_PREVIEW: {
    353                     setupPreview();
    354                     break;
    355                 }
    356 
    357                 case CLEAR_SCREEN_DELAY: {
    358                     mActivity.getWindow().clearFlags(
    359                             WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    360                     break;
    361                 }
    362 
    363                 case FIRST_TIME_INIT: {
    364                     initializeFirstTime();
    365                     break;
    366                 }
    367 
    368                 case SET_CAMERA_PARAMETERS_WHEN_IDLE: {
    369                     setCameraParametersWhenIdle(0);
    370                     break;
    371                 }
    372 
    373                 case CHECK_DISPLAY_ROTATION: {
    374                     // Set the display orientation if display rotation has changed.
    375                     // Sometimes this happens when the device is held upside
    376                     // down and camera app is opened. Rotation animation will
    377                     // take some time and the rotation value we have got may be
    378                     // wrong. Framework does not have a callback for this now.
    379                     if (Util.getDisplayRotation(mActivity) != mDisplayRotation) {
    380                         setDisplayOrientation();
    381                     }
    382                     if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
    383                         mHandler.sendEmptyMessageDelayed(CHECK_DISPLAY_ROTATION, 100);
    384                     }
    385                     break;
    386                 }
    387 
    388                 case SHOW_TAP_TO_FOCUS_TOAST: {
    389                     showTapToFocusToast();
    390                     break;
    391                 }
    392 
    393                 case SWITCH_CAMERA: {
    394                     switchCamera();
    395                     break;
    396                 }
    397 
    398                 case SWITCH_CAMERA_START_ANIMATION: {
    399                     ((CameraScreenNail) mActivity.mCameraScreenNail).animateSwitchCamera();
    400                     break;
    401                 }
    402 
    403                 case CAMERA_OPEN_DONE: {
    404                     initializeAfterCameraOpen();
    405                     break;
    406                 }
    407 
    408                 case START_PREVIEW_DONE: {
    409                     mCameraStartUpThread = null;
    410                     setCameraState(IDLE);
    411                     if (!ApiHelper.HAS_SURFACE_TEXTURE) {
    412                         // This may happen if surfaceCreated has arrived.
    413                         mCameraDevice.setPreviewDisplayAsync(mCameraSurfaceHolder);
    414                     }
    415                     startFaceDetection();
    416                     locationFirstRun();
    417                     break;
    418                 }
    419 
    420                 case OPEN_CAMERA_FAIL: {
    421                     mCameraStartUpThread = null;
    422                     mOpenCameraFail = true;
    423                     Util.showErrorAndFinish(mActivity,
    424                             R.string.cannot_connect_camera);
    425                     break;
    426                 }
    427 
    428                 case CAMERA_DISABLED: {
    429                     mCameraStartUpThread = null;
    430                     mCameraDisabled = true;
    431                     Util.showErrorAndFinish(mActivity,
    432                             R.string.camera_disabled);
    433                     break;
    434                 }
    435             }
    436         }
    437     }
    438 
    439     @Override
    440     public void init(CameraActivity activity, View parent, boolean reuseNail) {
    441         mActivity = activity;
    442         mRootView = parent;
    443         mPreferences = new ComboPreferences(mActivity);
    444         CameraSettings.upgradeGlobalPreferences(mPreferences.getGlobal());
    445         mCameraId = getPreferredCameraId(mPreferences);
    446 
    447         mContentResolver = mActivity.getContentResolver();
    448 
    449         // To reduce startup time, open the camera and start the preview in
    450         // another thread.
    451         mCameraStartUpThread = new CameraStartUpThread();
    452         mCameraStartUpThread.start();
    453 
    454         mActivity.getLayoutInflater().inflate(R.layout.photo_module, (ViewGroup) mRootView);
    455 
    456         // Surface texture is from camera screen nail and startPreview needs it.
    457         // This must be done before startPreview.
    458         mIsImageCaptureIntent = isImageCaptureIntent();
    459         if (reuseNail) {
    460             mActivity.reuseCameraScreenNail(!mIsImageCaptureIntent);
    461         } else {
    462             mActivity.createCameraScreenNail(!mIsImageCaptureIntent);
    463         }
    464 
    465         mPreferences.setLocalId(mActivity, mCameraId);
    466         CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
    467         // we need to reset exposure for the preview
    468         resetExposureCompensation();
    469         // Starting the preview needs preferences, camera screen nail, and
    470         // focus area indicator.
    471         mStartPreviewPrerequisiteReady.open();
    472 
    473         initializeControlByIntent();
    474         mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
    475         initializeMiscControls();
    476         mLocationManager = new LocationManager(mActivity, this);
    477         initOnScreenIndicator();
    478     }
    479 
    480     // Prompt the user to pick to record location for the very first run of
    481     // camera only
    482     private void locationFirstRun() {
    483         if (RecordLocationPreference.isSet(mPreferences)) {
    484             return;
    485         }
    486         // Check if the back camera exists
    487         int backCameraId = CameraHolder.instance().getBackCameraId();
    488         if (backCameraId == -1) {
    489             // If there is no back camera, do not show the prompt.
    490             return;
    491         }
    492 
    493         new AlertDialog.Builder(mActivity)
    494             .setTitle(R.string.remember_location_title)
    495             .setMessage(R.string.remember_location_prompt)
    496             .setPositiveButton(R.string.remember_location_yes, new DialogInterface.OnClickListener() {
    497                 @Override
    498                 public void onClick(DialogInterface dialog, int arg1) {
    499                     setLocationPreference(RecordLocationPreference.VALUE_ON);
    500                 }
    501             })
    502             .setNegativeButton(R.string.remember_location_no, new DialogInterface.OnClickListener() {
    503                 @Override
    504                 public void onClick(DialogInterface dialog, int arg1) {
    505                     dialog.cancel();
    506                 }
    507             })
    508             .setOnCancelListener(new DialogInterface.OnCancelListener() {
    509                 @Override
    510                 public void onCancel(DialogInterface dialog) {
    511                     setLocationPreference(RecordLocationPreference.VALUE_OFF);
    512                 }
    513             })
    514             .show();
    515     }
    516 
    517     private void setLocationPreference(String value) {
    518         mPreferences.edit()
    519             .putString(RecordLocationPreference.KEY, value)
    520             .apply();
    521         // TODO: Fix this to use the actual onSharedPreferencesChanged listener
    522         // instead of invoking manually
    523         onSharedPreferenceChanged();
    524     }
    525 
    526     private void initializeRenderOverlay() {
    527         if (mPieRenderer != null) {
    528             mRenderOverlay.addRenderer(mPieRenderer);
    529             mFocusManager.setFocusRenderer(mPieRenderer);
    530         }
    531         if (mZoomRenderer != null) {
    532             mRenderOverlay.addRenderer(mZoomRenderer);
    533         }
    534         if (mGestures != null) {
    535             mGestures.clearTouchReceivers();
    536             mGestures.setRenderOverlay(mRenderOverlay);
    537             mGestures.addTouchReceiver(mMenu);
    538             mGestures.addTouchReceiver(mBlocker);
    539 
    540             if (isImageCaptureIntent()) {
    541                 if (mReviewCancelButton != null) {
    542                     mGestures.addTouchReceiver((View) mReviewCancelButton);
    543                 }
    544                 if (mReviewDoneButton != null) {
    545                     mGestures.addTouchReceiver((View) mReviewDoneButton);
    546                 }
    547             }
    548         }
    549         mRenderOverlay.requestLayout();
    550     }
    551 
    552     private void initializeAfterCameraOpen() {
    553         if (mPieRenderer == null) {
    554             mPieRenderer = new PieRenderer(mActivity);
    555             mPhotoControl = new PhotoController(mActivity, this, mPieRenderer);
    556             mPhotoControl.setListener(this);
    557             mPieRenderer.setPieListener(this);
    558         }
    559         if (mZoomRenderer == null) {
    560             mZoomRenderer = new ZoomRenderer(mActivity);
    561         }
    562         if (mGestures == null) {
    563             // this will handle gesture disambiguation and dispatching
    564             mGestures = new PreviewGestures(mActivity, this, mZoomRenderer, mPieRenderer);
    565         }
    566         initializeRenderOverlay();
    567         initializePhotoControl();
    568 
    569         // These depend on camera parameters.
    570         setPreviewFrameLayoutAspectRatio();
    571         mFocusManager.setPreviewSize(mPreviewFrameLayout.getWidth(),
    572                 mPreviewFrameLayout.getHeight());
    573         loadCameraPreferences();
    574         initializeZoom();
    575         updateOnScreenIndicators();
    576         showTapToFocusToastIfNeeded();
    577     }
    578 
    579     private void initializePhotoControl() {
    580         loadCameraPreferences();
    581         if (mPhotoControl != null) {
    582             mPhotoControl.initialize(mPreferenceGroup);
    583         }
    584         updateSceneModeUI();
    585     }
    586 
    587 
    588     private void resetExposureCompensation() {
    589         String value = mPreferences.getString(CameraSettings.KEY_EXPOSURE,
    590                 CameraSettings.EXPOSURE_DEFAULT_VALUE);
    591         if (!CameraSettings.EXPOSURE_DEFAULT_VALUE.equals(value)) {
    592             Editor editor = mPreferences.edit();
    593             editor.putString(CameraSettings.KEY_EXPOSURE, "0");
    594             editor.apply();
    595         }
    596     }
    597 
    598     private void keepMediaProviderInstance() {
    599         // We want to keep a reference to MediaProvider in camera's lifecycle.
    600         // TODO: Utilize mMediaProviderClient instance to replace
    601         // ContentResolver calls.
    602         if (mMediaProviderClient == null) {
    603             mMediaProviderClient = mContentResolver
    604                     .acquireContentProviderClient(MediaStore.AUTHORITY);
    605         }
    606     }
    607 
    608     // Snapshots can only be taken after this is called. It should be called
    609     // once only. We could have done these things in onCreate() but we want to
    610     // make preview screen appear as soon as possible.
    611     private void initializeFirstTime() {
    612         if (mFirstTimeInitialized) return;
    613 
    614         // Initialize location service.
    615         boolean recordLocation = RecordLocationPreference.get(
    616                 mPreferences, mContentResolver);
    617         mLocationManager.recordLocation(recordLocation);
    618 
    619         keepMediaProviderInstance();
    620 
    621         // Initialize shutter button.
    622         mShutterButton = mActivity.getShutterButton();
    623         mShutterButton.setImageResource(R.drawable.btn_new_shutter);
    624         mShutterButton.setOnShutterButtonListener(this);
    625         mShutterButton.setVisibility(View.VISIBLE);
    626 
    627         mImageSaver = new ImageSaver();
    628         mImageNamer = new ImageNamer();
    629 
    630         mFirstTimeInitialized = true;
    631         addIdleHandler();
    632 
    633         mActivity.updateStorageSpaceAndHint();
    634     }
    635 
    636     private void showTapToFocusToastIfNeeded() {
    637         // Show the tap to focus toast if this is the first start.
    638         if (mFocusAreaSupported &&
    639                 mPreferences.getBoolean(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, true)) {
    640             // Delay the toast for one second to wait for orientation.
    641             mHandler.sendEmptyMessageDelayed(SHOW_TAP_TO_FOCUS_TOAST, 1000);
    642         }
    643     }
    644 
    645     private void addIdleHandler() {
    646         MessageQueue queue = Looper.myQueue();
    647         queue.addIdleHandler(new MessageQueue.IdleHandler() {
    648             @Override
    649             public boolean queueIdle() {
    650                 Storage.ensureOSXCompatible();
    651                 return false;
    652             }
    653         });
    654     }
    655 
    656     // If the activity is paused and resumed, this method will be called in
    657     // onResume.
    658     private void initializeSecondTime() {
    659 
    660         // Start location update if needed.
    661         boolean recordLocation = RecordLocationPreference.get(
    662                 mPreferences, mContentResolver);
    663         mLocationManager.recordLocation(recordLocation);
    664 
    665         mImageSaver = new ImageSaver();
    666         mImageNamer = new ImageNamer();
    667         initializeZoom();
    668         keepMediaProviderInstance();
    669         hidePostCaptureAlert();
    670 
    671         if (mPhotoControl != null) {
    672             mPhotoControl.reloadPreferences();
    673         }
    674     }
    675 
    676     private class ZoomChangeListener implements ZoomRenderer.OnZoomChangedListener {
    677         @Override
    678         public void onZoomValueChanged(int index) {
    679             // Not useful to change zoom value when the activity is paused.
    680             if (mPaused) return;
    681             mZoomValue = index;
    682             if (mParameters == null || mCameraDevice == null) return;
    683             // Set zoom parameters asynchronously
    684             mParameters.setZoom(mZoomValue);
    685             mCameraDevice.setParametersAsync(mParameters);
    686             if (mZoomRenderer != null) {
    687                 Parameters p = mCameraDevice.getParameters();
    688                 mZoomRenderer.setZoomValue(mZoomRatios.get(p.getZoom()));
    689             }
    690         }
    691 
    692         @Override
    693         public void onZoomStart() {
    694             if (mPieRenderer != null) {
    695                 mPieRenderer.setBlockFocus(true);
    696             }
    697         }
    698 
    699         @Override
    700         public void onZoomEnd() {
    701             if (mPieRenderer != null) {
    702                 mPieRenderer.setBlockFocus(false);
    703             }
    704         }
    705     }
    706 
    707     private void initializeZoom() {
    708         if ((mParameters == null) || !mParameters.isZoomSupported()
    709                 || (mZoomRenderer == null)) return;
    710         mZoomMax = mParameters.getMaxZoom();
    711         mZoomRatios = mParameters.getZoomRatios();
    712         // Currently we use immediate zoom for fast zooming to get better UX and
    713         // there is no plan to take advantage of the smooth zoom.
    714         if (mZoomRenderer != null) {
    715             mZoomRenderer.setZoomMax(mZoomMax);
    716             mZoomRenderer.setZoom(mParameters.getZoom());
    717             mZoomRenderer.setZoomValue(mZoomRatios.get(mParameters.getZoom()));
    718             mZoomRenderer.setOnZoomChangeListener(new ZoomChangeListener());
    719         }
    720     }
    721 
    722     @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
    723     @Override
    724     public void startFaceDetection() {
    725         if (!ApiHelper.HAS_FACE_DETECTION) return;
    726         if (mFaceDetectionStarted) return;
    727         if (mParameters.getMaxNumDetectedFaces() > 0) {
    728             mFaceDetectionStarted = true;
    729             mFaceView.clear();
    730             mFaceView.setVisibility(View.VISIBLE);
    731             mFaceView.setDisplayOrientation(mDisplayOrientation);
    732             CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
    733             mFaceView.setMirror(info.facing == CameraInfo.CAMERA_FACING_FRONT);
    734             mFaceView.resume();
    735             mFocusManager.setFaceView(mFaceView);
    736             mCameraDevice.setFaceDetectionListener(new FaceDetectionListener() {
    737                 @Override
    738                 public void onFaceDetection(Face[] faces, android.hardware.Camera camera) {
    739                     mFaceView.setFaces(faces);
    740                 }
    741             });
    742             mCameraDevice.startFaceDetection();
    743         }
    744     }
    745 
    746     @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
    747     @Override
    748     public void stopFaceDetection() {
    749         if (!ApiHelper.HAS_FACE_DETECTION) return;
    750         if (!mFaceDetectionStarted) return;
    751         if (mParameters.getMaxNumDetectedFaces() > 0) {
    752             mFaceDetectionStarted = false;
    753             mCameraDevice.setFaceDetectionListener(null);
    754             mCameraDevice.stopFaceDetection();
    755             if (mFaceView != null) mFaceView.clear();
    756         }
    757     }
    758 
    759     @Override
    760     public boolean dispatchTouchEvent(MotionEvent m) {
    761         if (mCameraState == SWITCHING_CAMERA) return true;
    762         if (mPopup != null) {
    763             return mActivity.superDispatchTouchEvent(m);
    764         } else if (mGestures != null && mRenderOverlay != null) {
    765             return mGestures.dispatchTouch(m);
    766         }
    767         return false;
    768     }
    769 
    770     private void initOnScreenIndicator() {
    771         mOnScreenIndicators = mRootView.findViewById(R.id.on_screen_indicators);
    772         mExposureIndicator = (ImageView) mOnScreenIndicators.findViewById(R.id.menu_exposure_indicator);
    773         mFlashIndicator = (ImageView) mOnScreenIndicators.findViewById(R.id.menu_flash_indicator);
    774         mSceneIndicator = (ImageView) mOnScreenIndicators.findViewById(R.id.menu_scenemode_indicator);
    775         mHdrIndicator = (ImageView) mOnScreenIndicators.findViewById(R.id.menu_hdr_indicator);
    776     }
    777 
    778     @Override
    779     public void showGpsOnScreenIndicator(boolean hasSignal) { }
    780 
    781     @Override
    782     public void hideGpsOnScreenIndicator() { }
    783 
    784     private void updateExposureOnScreenIndicator(int value) {
    785         if (mExposureIndicator == null) {
    786             return;
    787         }
    788         int id = 0;
    789         float step = mParameters.getExposureCompensationStep();
    790         value = (int) Math.round(value * step);
    791         switch(value) {
    792         case -3:
    793             id = R.drawable.ic_indicator_ev_n3;
    794             break;
    795         case -2:
    796             id = R.drawable.ic_indicator_ev_n2;
    797             break;
    798         case -1:
    799             id = R.drawable.ic_indicator_ev_n1;
    800             break;
    801         case 0:
    802             id = R.drawable.ic_indicator_ev_0;
    803             break;
    804         case 1:
    805             id = R.drawable.ic_indicator_ev_p1;
    806             break;
    807         case 2:
    808             id = R.drawable.ic_indicator_ev_p2;
    809             break;
    810         case 3:
    811             id = R.drawable.ic_indicator_ev_p3;
    812             break;
    813         }
    814         mExposureIndicator.setImageResource(id);
    815 
    816     }
    817 
    818     private void updateFlashOnScreenIndicator(String value) {
    819         if (mFlashIndicator == null) {
    820             return;
    821         }
    822         if (value == null || Parameters.FLASH_MODE_OFF.equals(value)) {
    823             mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_off);
    824         } else {
    825             if (Parameters.FLASH_MODE_AUTO.equals(value)) {
    826                 mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_auto);
    827             } else if (Parameters.FLASH_MODE_ON.equals(value)) {
    828                 mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_on);
    829             } else {
    830                 mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_off);
    831             }
    832         }
    833     }
    834 
    835     private void updateSceneOnScreenIndicator(String value) {
    836         if (mSceneIndicator == null) {
    837             return;
    838         }
    839         if ((value == null) || Parameters.SCENE_MODE_AUTO.equals(value)
    840                 || Parameters.SCENE_MODE_HDR.equals(value)) {
    841             mSceneIndicator.setImageResource(R.drawable.ic_indicator_sce_off);
    842         } else {
    843             mSceneIndicator.setImageResource(R.drawable.ic_indicator_sce_on);
    844         }
    845     }
    846 
    847     private void updateHdrOnScreenIndicator(String value) {
    848         if (mHdrIndicator == null) {
    849             return;
    850         }
    851         if ((value != null) && Parameters.SCENE_MODE_HDR.equals(value)) {
    852             mHdrIndicator.setImageResource(R.drawable.ic_indicator_hdr_on);
    853         } else {
    854             mHdrIndicator.setImageResource(R.drawable.ic_indicator_hdr_off);
    855         }
    856     }
    857 
    858     private void updateOnScreenIndicators() {
    859         updateSceneOnScreenIndicator(mParameters.getSceneMode());
    860         updateExposureOnScreenIndicator(CameraSettings.readExposure(mPreferences));
    861         updateFlashOnScreenIndicator(mParameters.getFlashMode());
    862         updateHdrOnScreenIndicator(mParameters.getSceneMode());
    863     }
    864 
    865     private final class ShutterCallback
    866             implements android.hardware.Camera.ShutterCallback {
    867         @Override
    868         public void onShutter() {
    869             mShutterCallbackTime = System.currentTimeMillis();
    870             mShutterLag = mShutterCallbackTime - mCaptureStartTime;
    871             Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
    872         }
    873     }
    874 
    875     private final class PostViewPictureCallback implements PictureCallback {
    876         @Override
    877         public void onPictureTaken(
    878                 byte [] data, android.hardware.Camera camera) {
    879             mPostViewPictureCallbackTime = System.currentTimeMillis();
    880             Log.v(TAG, "mShutterToPostViewCallbackTime = "
    881                     + (mPostViewPictureCallbackTime - mShutterCallbackTime)
    882                     + "ms");
    883         }
    884     }
    885 
    886     private final class RawPictureCallback implements PictureCallback {
    887         @Override
    888         public void onPictureTaken(
    889                 byte [] rawData, android.hardware.Camera camera) {
    890             mRawPictureCallbackTime = System.currentTimeMillis();
    891             Log.v(TAG, "mShutterToRawCallbackTime = "
    892                     + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
    893         }
    894     }
    895 
    896     private final class JpegPictureCallback implements PictureCallback {
    897         Location mLocation;
    898 
    899         public JpegPictureCallback(Location loc) {
    900             mLocation = loc;
    901         }
    902 
    903         @Override
    904         public void onPictureTaken(
    905                 final byte [] jpegData, final android.hardware.Camera camera) {
    906             if (mPaused) {
    907                 return;
    908             }
    909             if (mSceneMode == Util.SCENE_MODE_HDR) {
    910                 mActivity.showSwitcher();
    911                 mActivity.setSwipingEnabled(true);
    912             }
    913 
    914             mJpegPictureCallbackTime = System.currentTimeMillis();
    915             // If postview callback has arrived, the captured image is displayed
    916             // in postview callback. If not, the captured image is displayed in
    917             // raw picture callback.
    918             if (mPostViewPictureCallbackTime != 0) {
    919                 mShutterToPictureDisplayedTime =
    920                         mPostViewPictureCallbackTime - mShutterCallbackTime;
    921                 mPictureDisplayedToJpegCallbackTime =
    922                         mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
    923             } else {
    924                 mShutterToPictureDisplayedTime =
    925                         mRawPictureCallbackTime - mShutterCallbackTime;
    926                 mPictureDisplayedToJpegCallbackTime =
    927                         mJpegPictureCallbackTime - mRawPictureCallbackTime;
    928             }
    929             Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
    930                     + mPictureDisplayedToJpegCallbackTime + "ms");
    931 
    932             // Only animate when in full screen capture mode
    933             // i.e. If monkey/a user swipes to the gallery during picture taking,
    934             // don't show animation
    935             if (ApiHelper.HAS_SURFACE_TEXTURE && !mIsImageCaptureIntent
    936                     && mActivity.mShowCameraAppView) {
    937                 // Finish capture animation
    938                 ((CameraScreenNail) mActivity.mCameraScreenNail).animateSlide();
    939             }
    940             mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
    941             if (!mIsImageCaptureIntent) {
    942                 if (ApiHelper.CAN_START_PREVIEW_IN_JPEG_CALLBACK) {
    943                     setupPreview();
    944                 } else {
    945                     // Camera HAL of some devices have a bug. Starting preview
    946                     // immediately after taking a picture will fail. Wait some
    947                     // time before starting the preview.
    948                     mHandler.sendEmptyMessageDelayed(SETUP_PREVIEW, 300);
    949                 }
    950             }
    951 
    952             if (!mIsImageCaptureIntent) {
    953                 // Calculate the width and the height of the jpeg.
    954                 Size s = mParameters.getPictureSize();
    955                 int orientation = Exif.getOrientation(jpegData);
    956                 int width, height;
    957                 if ((mJpegRotation + orientation) % 180 == 0) {
    958                     width = s.width;
    959                     height = s.height;
    960                 } else {
    961                     width = s.height;
    962                     height = s.width;
    963                 }
    964                 Uri uri = mImageNamer.getUri();
    965                 mActivity.addSecureAlbumItemIfNeeded(false, uri);
    966                 String title = mImageNamer.getTitle();
    967                 mImageSaver.addImage(jpegData, uri, title, mLocation,
    968                         width, height, orientation);
    969             } else {
    970                 mJpegImageData = jpegData;
    971                 if (!mQuickCapture) {
    972                     showPostCaptureAlert();
    973                 } else {
    974                     doAttach();
    975                 }
    976             }
    977 
    978             // Check this in advance of each shot so we don't add to shutter
    979             // latency. It's true that someone else could write to the SD card in
    980             // the mean time and fill it, but that could have happened between the
    981             // shutter press and saving the JPEG too.
    982             mActivity.updateStorageSpaceAndHint();
    983 
    984             long now = System.currentTimeMillis();
    985             mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
    986             Log.v(TAG, "mJpegCallbackFinishTime = "
    987                     + mJpegCallbackFinishTime + "ms");
    988             mJpegPictureCallbackTime = 0;
    989         }
    990     }
    991 
    992     private final class AutoFocusCallback
    993             implements android.hardware.Camera.AutoFocusCallback {
    994         @Override
    995         public void onAutoFocus(
    996                 boolean focused, android.hardware.Camera camera) {
    997             if (mPaused) return;
    998 
    999             mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
   1000             Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms");
   1001             setCameraState(IDLE);
   1002             mFocusManager.onAutoFocus(focused, mShutterButton.isPressed());
   1003         }
   1004     }
   1005 
   1006     @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
   1007     private final class AutoFocusMoveCallback
   1008             implements android.hardware.Camera.AutoFocusMoveCallback {
   1009         @Override
   1010         public void onAutoFocusMoving(
   1011             boolean moving, android.hardware.Camera camera) {
   1012                 mFocusManager.onAutoFocusMoving(moving);
   1013         }
   1014     }
   1015 
   1016     // Each SaveRequest remembers the data needed to save an image.
   1017     private static class SaveRequest {
   1018         byte[] data;
   1019         Uri uri;
   1020         String title;
   1021         Location loc;
   1022         int width, height;
   1023         int orientation;
   1024     }
   1025 
   1026     // We use a queue to store the SaveRequests that have not been completed
   1027     // yet. The main thread puts the request into the queue. The saver thread
   1028     // gets it from the queue, does the work, and removes it from the queue.
   1029     //
   1030     // The main thread needs to wait for the saver thread to finish all the work
   1031     // in the queue, when the activity's onPause() is called, we need to finish
   1032     // all the work, so other programs (like Gallery) can see all the images.
   1033     //
   1034     // If the queue becomes too long, adding a new request will block the main
   1035     // thread until the queue length drops below the threshold (QUEUE_LIMIT).
   1036     // If we don't do this, we may face several problems: (1) We may OOM
   1037     // because we are holding all the jpeg data in memory. (2) We may ANR
   1038     // when we need to wait for saver thread finishing all the work (in
   1039     // onPause() or gotoGallery()) because the time to finishing a long queue
   1040     // of work may be too long.
   1041     private class ImageSaver extends Thread {
   1042         private static final int QUEUE_LIMIT = 3;
   1043 
   1044         private ArrayList<SaveRequest> mQueue;
   1045         private boolean mStop;
   1046 
   1047         // Runs in main thread
   1048         public ImageSaver() {
   1049             mQueue = new ArrayList<SaveRequest>();
   1050             start();
   1051         }
   1052 
   1053         // Runs in main thread
   1054         public void addImage(final byte[] data, Uri uri, String title,
   1055                 Location loc, int width, int height, int orientation) {
   1056             SaveRequest r = new SaveRequest();
   1057             r.data = data;
   1058             r.uri = uri;
   1059             r.title = title;
   1060             r.loc = (loc == null) ? null : new Location(loc);  // make a copy
   1061             r.width = width;
   1062             r.height = height;
   1063             r.orientation = orientation;
   1064             synchronized (this) {
   1065                 while (mQueue.size() >= QUEUE_LIMIT) {
   1066                     try {
   1067                         wait();
   1068                     } catch (InterruptedException ex) {
   1069                         // ignore.
   1070                     }
   1071                 }
   1072                 mQueue.add(r);
   1073                 notifyAll();  // Tell saver thread there is new work to do.
   1074             }
   1075         }
   1076 
   1077         // Runs in saver thread
   1078         @Override
   1079         public void run() {
   1080             while (true) {
   1081                 SaveRequest r;
   1082                 synchronized (this) {
   1083                     if (mQueue.isEmpty()) {
   1084                         notifyAll();  // notify main thread in waitDone
   1085 
   1086                         // Note that we can only stop after we saved all images
   1087                         // in the queue.
   1088                         if (mStop) break;
   1089 
   1090                         try {
   1091                             wait();
   1092                         } catch (InterruptedException ex) {
   1093                             // ignore.
   1094                         }
   1095                         continue;
   1096                     }
   1097                     r = mQueue.get(0);
   1098                 }
   1099                 storeImage(r.data, r.uri, r.title, r.loc, r.width, r.height,
   1100                         r.orientation);
   1101                 synchronized (this) {
   1102                     mQueue.remove(0);
   1103                     notifyAll();  // the main thread may wait in addImage
   1104                 }
   1105             }
   1106         }
   1107 
   1108         // Runs in main thread
   1109         public void waitDone() {
   1110             synchronized (this) {
   1111                 while (!mQueue.isEmpty()) {
   1112                     try {
   1113                         wait();
   1114                     } catch (InterruptedException ex) {
   1115                         // ignore.
   1116                     }
   1117                 }
   1118             }
   1119         }
   1120 
   1121         // Runs in main thread
   1122         public void finish() {
   1123             waitDone();
   1124             synchronized (this) {
   1125                 mStop = true;
   1126                 notifyAll();
   1127             }
   1128             try {
   1129                 join();
   1130             } catch (InterruptedException ex) {
   1131                 // ignore.
   1132             }
   1133         }
   1134 
   1135         // Runs in saver thread
   1136         private void storeImage(final byte[] data, Uri uri, String title,
   1137                 Location loc, int width, int height, int orientation) {
   1138             boolean ok = Storage.updateImage(mContentResolver, uri, title, loc,
   1139                     orientation, data, width, height);
   1140             if (ok) {
   1141                 Util.broadcastNewPicture(mActivity, uri);
   1142             }
   1143         }
   1144     }
   1145 
   1146     private static class ImageNamer extends Thread {
   1147         private boolean mRequestPending;
   1148         private ContentResolver mResolver;
   1149         private long mDateTaken;
   1150         private int mWidth, mHeight;
   1151         private boolean mStop;
   1152         private Uri mUri;
   1153         private String mTitle;
   1154 
   1155         // Runs in main thread
   1156         public ImageNamer() {
   1157             start();
   1158         }
   1159 
   1160         // Runs in main thread
   1161         public synchronized void prepareUri(ContentResolver resolver,
   1162                 long dateTaken, int width, int height, int rotation) {
   1163             if (rotation % 180 != 0) {
   1164                 int tmp = width;
   1165                 width = height;
   1166                 height = tmp;
   1167             }
   1168             mRequestPending = true;
   1169             mResolver = resolver;
   1170             mDateTaken = dateTaken;
   1171             mWidth = width;
   1172             mHeight = height;
   1173             notifyAll();
   1174         }
   1175 
   1176         // Runs in main thread
   1177         public synchronized Uri getUri() {
   1178             // wait until the request is done.
   1179             while (mRequestPending) {
   1180                 try {
   1181                     wait();
   1182                 } catch (InterruptedException ex) {
   1183                     // ignore.
   1184                 }
   1185             }
   1186 
   1187             // return the uri generated
   1188             Uri uri = mUri;
   1189             mUri = null;
   1190             return uri;
   1191         }
   1192 
   1193         // Runs in main thread, should be called after getUri().
   1194         public synchronized String getTitle() {
   1195             return mTitle;
   1196         }
   1197 
   1198         // Runs in namer thread
   1199         @Override
   1200         public synchronized void run() {
   1201             while (true) {
   1202                 if (mStop) break;
   1203                 if (!mRequestPending) {
   1204                     try {
   1205                         wait();
   1206                     } catch (InterruptedException ex) {
   1207                         // ignore.
   1208                     }
   1209                     continue;
   1210                 }
   1211                 cleanOldUri();
   1212                 generateUri();
   1213                 mRequestPending = false;
   1214                 notifyAll();
   1215             }
   1216             cleanOldUri();
   1217         }
   1218 
   1219         // Runs in main thread
   1220         public synchronized void finish() {
   1221             mStop = true;
   1222             notifyAll();
   1223         }
   1224 
   1225         // Runs in namer thread
   1226         private void generateUri() {
   1227             mTitle = Util.createJpegName(mDateTaken);
   1228             mUri = Storage.newImage(mResolver, mTitle, mDateTaken, mWidth, mHeight);
   1229         }
   1230 
   1231         // Runs in namer thread
   1232         private void cleanOldUri() {
   1233             if (mUri == null) return;
   1234             Storage.deleteImage(mResolver, mUri);
   1235             mUri = null;
   1236         }
   1237     }
   1238 
   1239     private void setCameraState(int state) {
   1240         mCameraState = state;
   1241         switch (state) {
   1242             case PREVIEW_STOPPED:
   1243             case SNAPSHOT_IN_PROGRESS:
   1244             case FOCUSING:
   1245             case SWITCHING_CAMERA:
   1246                 if (mGestures != null) mGestures.setEnabled(false);
   1247                 break;
   1248             case IDLE:
   1249                 if (mGestures != null && mActivity.mShowCameraAppView) {
   1250                     // Enable gestures only when the camera app view is visible
   1251                     mGestures.setEnabled(true);
   1252                 }
   1253                 break;
   1254         }
   1255     }
   1256 
   1257     private void animateFlash() {
   1258         // Only animate when in full screen capture mode
   1259         // i.e. If monkey/a user swipes to the gallery during picture taking,
   1260         // don't show animation
   1261         if (ApiHelper.HAS_SURFACE_TEXTURE && !mIsImageCaptureIntent
   1262                 && mActivity.mShowCameraAppView) {
   1263             // Start capture animation.
   1264             ((CameraScreenNail) mActivity.mCameraScreenNail).animateFlash(mDisplayRotation);
   1265         }
   1266     }
   1267 
   1268     @Override
   1269     public boolean capture() {
   1270         // If we are already in the middle of taking a snapshot then ignore.
   1271         if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
   1272                 || mCameraState == SWITCHING_CAMERA) {
   1273             return false;
   1274         }
   1275         mCaptureStartTime = System.currentTimeMillis();
   1276         mPostViewPictureCallbackTime = 0;
   1277         mJpegImageData = null;
   1278 
   1279         final boolean animateBefore = (mSceneMode == Util.SCENE_MODE_HDR);
   1280 
   1281         if (animateBefore) {
   1282             animateFlash();
   1283         }
   1284 
   1285         // Set rotation and gps data.
   1286         mJpegRotation = Util.getJpegRotation(mCameraId, mOrientation);
   1287         mParameters.setRotation(mJpegRotation);
   1288         Location loc = mLocationManager.getCurrentLocation();
   1289         Util.setGpsParameters(mParameters, loc);
   1290         mCameraDevice.setParameters(mParameters);
   1291 
   1292         mCameraDevice.takePicture2(mShutterCallback, mRawPictureCallback,
   1293                 mPostViewPictureCallback, new JpegPictureCallback(loc),
   1294                 mCameraState, mFocusManager.getFocusState());
   1295 
   1296         if (!animateBefore) {
   1297             animateFlash();
   1298         }
   1299 
   1300         Size size = mParameters.getPictureSize();
   1301         mImageNamer.prepareUri(mContentResolver, mCaptureStartTime,
   1302                 size.width, size.height, mJpegRotation);
   1303 
   1304         mFaceDetectionStarted = false;
   1305         setCameraState(SNAPSHOT_IN_PROGRESS);
   1306         return true;
   1307     }
   1308 
   1309     @Override
   1310     public void setFocusParameters() {
   1311         setCameraParameters(UPDATE_PARAM_PREFERENCE);
   1312     }
   1313 
   1314     private int getPreferredCameraId(ComboPreferences preferences) {
   1315         int intentCameraId = Util.getCameraFacingIntentExtras(mActivity);
   1316         if (intentCameraId != -1) {
   1317             // Testing purpose. Launch a specific camera through the intent
   1318             // extras.
   1319             return intentCameraId;
   1320         } else {
   1321             return CameraSettings.readPreferredCameraId(preferences);
   1322         }
   1323     }
   1324 
   1325     private void setShowMenu(boolean show) {
   1326         if (mOnScreenIndicators != null) {
   1327             mOnScreenIndicators.setVisibility(show ? View.VISIBLE : View.GONE);
   1328         }
   1329         if (mMenu != null) {
   1330             mMenu.setVisibility(show ? View.VISIBLE : View.GONE);
   1331         }
   1332     }
   1333 
   1334     @Override
   1335     public void onFullScreenChanged(boolean full) {
   1336         if (mFaceView != null) {
   1337             mFaceView.setBlockDraw(!full);
   1338         }
   1339         if (mPopup != null) {
   1340             dismissPopup(false, full);
   1341         }
   1342         if (mGestures != null) {
   1343             mGestures.setEnabled(full);
   1344         }
   1345         if (mRenderOverlay != null) {
   1346             // this can not happen in capture mode
   1347             mRenderOverlay.setVisibility(full ? View.VISIBLE : View.GONE);
   1348         }
   1349         if (mPieRenderer != null) {
   1350             mPieRenderer.setBlockFocus(!full);
   1351         }
   1352         setShowMenu(full);
   1353         if (mBlocker != null) {
   1354             mBlocker.setVisibility(full ? View.VISIBLE : View.GONE);
   1355         }
   1356         if (ApiHelper.HAS_SURFACE_TEXTURE) {
   1357             if (mActivity.mCameraScreenNail != null) {
   1358                 ((CameraScreenNail) mActivity.mCameraScreenNail).setFullScreen(full);
   1359             }
   1360             return;
   1361         }
   1362         if (full) {
   1363             mPreviewSurfaceView.expand();
   1364         } else {
   1365             mPreviewSurfaceView.shrink();
   1366         }
   1367     }
   1368 
   1369     @Override
   1370     public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
   1371         Log.v(TAG, "surfaceChanged:" + holder + " width=" + width + ". height="
   1372                 + height);
   1373     }
   1374 
   1375     @Override
   1376     public void surfaceCreated(SurfaceHolder holder) {
   1377         Log.v(TAG, "surfaceCreated: " + holder);
   1378         mCameraSurfaceHolder = holder;
   1379         // Do not access the camera if camera start up thread is not finished.
   1380         if (mCameraDevice == null || mCameraStartUpThread != null) return;
   1381 
   1382         mCameraDevice.setPreviewDisplayAsync(holder);
   1383         // This happens when onConfigurationChanged arrives, surface has been
   1384         // destroyed, and there is no onFullScreenChanged.
   1385         if (mCameraState == PREVIEW_STOPPED) {
   1386             setupPreview();
   1387         }
   1388     }
   1389 
   1390     @Override
   1391     public void surfaceDestroyed(SurfaceHolder holder) {
   1392         Log.v(TAG, "surfaceDestroyed: " + holder);
   1393         mCameraSurfaceHolder = null;
   1394         stopPreview();
   1395     }
   1396 
   1397     private void updateSceneModeUI() {
   1398         // If scene mode is set, we cannot set flash mode, white balance, and
   1399         // focus mode, instead, we read it from driver
   1400         if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
   1401             overrideCameraSettings(mParameters.getFlashMode(),
   1402                     mParameters.getWhiteBalance(), mParameters.getFocusMode());
   1403         } else {
   1404             overrideCameraSettings(null, null, null);
   1405         }
   1406     }
   1407 
   1408     private void overrideCameraSettings(final String flashMode,
   1409             final String whiteBalance, final String focusMode) {
   1410         if (mPhotoControl != null) {
   1411 //            mPieControl.enableFilter(true);
   1412             mPhotoControl.overrideSettings(
   1413                     CameraSettings.KEY_FLASH_MODE, flashMode,
   1414                     CameraSettings.KEY_WHITE_BALANCE, whiteBalance,
   1415                     CameraSettings.KEY_FOCUS_MODE, focusMode);
   1416 //            mPieControl.enableFilter(false);
   1417         }
   1418     }
   1419 
   1420     private void loadCameraPreferences() {
   1421         CameraSettings settings = new CameraSettings(mActivity, mInitialParams,
   1422                 mCameraId, CameraHolder.instance().getCameraInfo());
   1423         mPreferenceGroup = settings.getPreferenceGroup(R.xml.camera_preferences);
   1424     }
   1425 
   1426     @Override
   1427     public boolean collapseCameraControls() {
   1428         // Remove all the popups/dialog boxes
   1429         boolean ret = false;
   1430         if (mPopup != null) {
   1431             dismissPopup(false);
   1432             ret = true;
   1433         }
   1434         return ret;
   1435     }
   1436 
   1437     public boolean removeTopLevelPopup() {
   1438         // Remove the top level popup or dialog box and return true if there's any
   1439         if (mPopup != null) {
   1440             dismissPopup(true);
   1441             return true;
   1442         }
   1443         return false;
   1444     }
   1445 
   1446     @Override
   1447     public void onOrientationChanged(int orientation) {
   1448         // We keep the last known orientation. So if the user first orient
   1449         // the camera then point the camera to floor or sky, we still have
   1450         // the correct orientation.
   1451         if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) return;
   1452         mOrientation = Util.roundOrientation(orientation, mOrientation);
   1453         // When the screen is unlocked, display rotation may change. Always
   1454         // calculate the up-to-date orientationCompensation.
   1455         int orientationCompensation =
   1456                 (mOrientation + Util.getDisplayRotation(mActivity)) % 360;
   1457         if (mOrientationCompensation != orientationCompensation) {
   1458             mOrientationCompensation = orientationCompensation;
   1459             if (mFaceView != null) {
   1460                 mFaceView.setOrientation(mOrientationCompensation, true);
   1461             }
   1462             setDisplayOrientation();
   1463         }
   1464 
   1465         // Show the toast after getting the first orientation changed.
   1466         if (mHandler.hasMessages(SHOW_TAP_TO_FOCUS_TOAST)) {
   1467             mHandler.removeMessages(SHOW_TAP_TO_FOCUS_TOAST);
   1468             showTapToFocusToast();
   1469         }
   1470     }
   1471 
   1472     @Override
   1473     public void onStop() {
   1474         if (mMediaProviderClient != null) {
   1475             mMediaProviderClient.release();
   1476             mMediaProviderClient = null;
   1477         }
   1478     }
   1479 
   1480     // onClick handler for R.id.btn_done
   1481     @OnClickAttr
   1482     public void onReviewDoneClicked(View v) {
   1483         doAttach();
   1484     }
   1485 
   1486     // onClick handler for R.id.btn_cancel
   1487     @OnClickAttr
   1488     public void onReviewCancelClicked(View v) {
   1489         doCancel();
   1490     }
   1491 
   1492     // onClick handler for R.id.btn_retake
   1493     @OnClickAttr
   1494     public void onReviewRetakeClicked(View v) {
   1495         if (mPaused)
   1496             return;
   1497 
   1498         hidePostCaptureAlert();
   1499         setupPreview();
   1500     }
   1501 
   1502     private void doAttach() {
   1503         if (mPaused) {
   1504             return;
   1505         }
   1506 
   1507         byte[] data = mJpegImageData;
   1508 
   1509         if (mCropValue == null) {
   1510             // First handle the no crop case -- just return the value.  If the
   1511             // caller specifies a "save uri" then write the data to its
   1512             // stream. Otherwise, pass back a scaled down version of the bitmap
   1513             // directly in the extras.
   1514             if (mSaveUri != null) {
   1515                 OutputStream outputStream = null;
   1516                 try {
   1517                     outputStream = mContentResolver.openOutputStream(mSaveUri);
   1518                     outputStream.write(data);
   1519                     outputStream.close();
   1520 
   1521                     mActivity.setResultEx(Activity.RESULT_OK);
   1522                     mActivity.finish();
   1523                 } catch (IOException ex) {
   1524                     // ignore exception
   1525                 } finally {
   1526                     Util.closeSilently(outputStream);
   1527                 }
   1528             } else {
   1529                 int orientation = Exif.getOrientation(data);
   1530                 Bitmap bitmap = Util.makeBitmap(data, 50 * 1024);
   1531                 bitmap = Util.rotate(bitmap, orientation);
   1532                 mActivity.setResultEx(Activity.RESULT_OK,
   1533                         new Intent("inline-data").putExtra("data", bitmap));
   1534                 mActivity.finish();
   1535             }
   1536         } else {
   1537             // Save the image to a temp file and invoke the cropper
   1538             Uri tempUri = null;
   1539             FileOutputStream tempStream = null;
   1540             try {
   1541                 File path = mActivity.getFileStreamPath(sTempCropFilename);
   1542                 path.delete();
   1543                 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
   1544                 tempStream.write(data);
   1545                 tempStream.close();
   1546                 tempUri = Uri.fromFile(path);
   1547             } catch (FileNotFoundException ex) {
   1548                 mActivity.setResultEx(Activity.RESULT_CANCELED);
   1549                 mActivity.finish();
   1550                 return;
   1551             } catch (IOException ex) {
   1552                 mActivity.setResultEx(Activity.RESULT_CANCELED);
   1553                 mActivity.finish();
   1554                 return;
   1555             } finally {
   1556                 Util.closeSilently(tempStream);
   1557             }
   1558 
   1559             Bundle newExtras = new Bundle();
   1560             if (mCropValue.equals("circle")) {
   1561                 newExtras.putString("circleCrop", "true");
   1562             }
   1563             if (mSaveUri != null) {
   1564                 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
   1565             } else {
   1566                 newExtras.putBoolean("return-data", true);
   1567             }
   1568             if (mActivity.isSecureCamera()) {
   1569                 newExtras.putBoolean(CropImage.KEY_SHOW_WHEN_LOCKED, true);
   1570             }
   1571 
   1572             Intent cropIntent = new Intent("com.android.camera.action.CROP");
   1573 
   1574             cropIntent.setData(tempUri);
   1575             cropIntent.putExtras(newExtras);
   1576 
   1577             mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
   1578         }
   1579     }
   1580 
   1581     private void doCancel() {
   1582         mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
   1583         mActivity.finish();
   1584     }
   1585 
   1586     @Override
   1587     public void onShutterButtonFocus(boolean pressed) {
   1588         if (mPaused || collapseCameraControls()
   1589                 || (mCameraState == SNAPSHOT_IN_PROGRESS)
   1590                 || (mCameraState == PREVIEW_STOPPED)) return;
   1591 
   1592         // Do not do focus if there is not enough storage.
   1593         if (pressed && !canTakePicture()) return;
   1594 
   1595         if (pressed) {
   1596             if (mSceneMode == Util.SCENE_MODE_HDR) {
   1597                 mActivity.hideSwitcher();
   1598                 mActivity.setSwipingEnabled(false);
   1599             }
   1600             mFocusManager.onShutterDown();
   1601         } else {
   1602             mFocusManager.onShutterUp();
   1603         }
   1604     }
   1605 
   1606     @Override
   1607     public void onShutterButtonClick() {
   1608         if (mPaused || collapseCameraControls()
   1609                 || (mCameraState == SWITCHING_CAMERA)
   1610                 || (mCameraState == PREVIEW_STOPPED)) return;
   1611 
   1612         // Do not take the picture if there is not enough storage.
   1613         if (mActivity.getStorageSpace() <= Storage.LOW_STORAGE_THRESHOLD) {
   1614             Log.i(TAG, "Not enough space or storage not ready. remaining="
   1615                     + mActivity.getStorageSpace());
   1616             return;
   1617         }
   1618         Log.v(TAG, "onShutterButtonClick: mCameraState=" + mCameraState);
   1619 
   1620         // If the user wants to do a snapshot while the previous one is still
   1621         // in progress, remember the fact and do it after we finish the previous
   1622         // one and re-start the preview. Snapshot in progress also includes the
   1623         // state that autofocus is focusing and a picture will be taken when
   1624         // focus callback arrives.
   1625         if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)
   1626                 && !mIsImageCaptureIntent) {
   1627             mSnapshotOnIdle = true;
   1628             return;
   1629         }
   1630 
   1631         mSnapshotOnIdle = false;
   1632         mFocusManager.doSnap();
   1633     }
   1634 
   1635     @Override
   1636     public void installIntentFilter() {
   1637     }
   1638 
   1639     @Override
   1640     public boolean updateStorageHintOnResume() {
   1641         return mFirstTimeInitialized;
   1642     }
   1643 
   1644     @Override
   1645     public void updateCameraAppView() {
   1646     }
   1647 
   1648     @Override
   1649     public void onResumeBeforeSuper() {
   1650         mPaused = false;
   1651     }
   1652 
   1653     @Override
   1654     public void onResumeAfterSuper() {
   1655         if (mOpenCameraFail || mCameraDisabled) return;
   1656 
   1657         mJpegPictureCallbackTime = 0;
   1658         mZoomValue = 0;
   1659 
   1660         // Start the preview if it is not started.
   1661         if (mCameraState == PREVIEW_STOPPED && mCameraStartUpThread == null) {
   1662             resetExposureCompensation();
   1663             mCameraStartUpThread = new CameraStartUpThread();
   1664             mCameraStartUpThread.start();
   1665         }
   1666 
   1667         // If first time initialization is not finished, put it in the
   1668         // message queue.
   1669         if (!mFirstTimeInitialized) {
   1670             mHandler.sendEmptyMessage(FIRST_TIME_INIT);
   1671         } else {
   1672             initializeSecondTime();
   1673         }
   1674         keepScreenOnAwhile();
   1675 
   1676         // Dismiss open menu if exists.
   1677         PopupManager.getInstance(mActivity).notifyShowPopup(null);
   1678     }
   1679 
   1680     void waitCameraStartUpThread() {
   1681         try {
   1682             if (mCameraStartUpThread != null) {
   1683                 mCameraStartUpThread.cancel();
   1684                 mCameraStartUpThread.join();
   1685                 mCameraStartUpThread = null;
   1686                 setCameraState(IDLE);
   1687             }
   1688         } catch (InterruptedException e) {
   1689             // ignore
   1690         }
   1691     }
   1692 
   1693     @Override
   1694     public void onPauseBeforeSuper() {
   1695         mPaused = true;
   1696     }
   1697 
   1698     @Override
   1699     public void onPauseAfterSuper() {
   1700         // Wait the camera start up thread to finish.
   1701         waitCameraStartUpThread();
   1702 
   1703         // When camera is started from secure lock screen for the first time
   1704         // after screen on, the activity gets onCreate->onResume->onPause->onResume.
   1705         // To reduce the latency, keep the camera for a short time so it does
   1706         // not need to be opened again.
   1707         if (mCameraDevice != null && mActivity.isSecureCamera()
   1708                 && ActivityBase.isFirstStartAfterScreenOn()) {
   1709             ActivityBase.resetFirstStartAfterScreenOn();
   1710             CameraHolder.instance().keep(KEEP_CAMERA_TIMEOUT);
   1711         }
   1712         // Reset the focus first. Camera CTS does not guarantee that
   1713         // cancelAutoFocus is allowed after preview stops.
   1714         if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
   1715             mCameraDevice.cancelAutoFocus();
   1716         }
   1717         stopPreview();
   1718         // Close the camera now because other activities may need to use it.
   1719         closeCamera();
   1720         if (mSurfaceTexture != null) {
   1721             ((CameraScreenNail) mActivity.mCameraScreenNail).releaseSurfaceTexture();
   1722             mSurfaceTexture = null;
   1723         }
   1724         resetScreenOn();
   1725 
   1726         // Clear UI.
   1727         collapseCameraControls();
   1728         if (mFaceView != null) mFaceView.clear();
   1729 
   1730         if (mFirstTimeInitialized) {
   1731             if (mImageSaver != null) {
   1732                 mImageSaver.finish();
   1733                 mImageSaver = null;
   1734                 mImageNamer.finish();
   1735                 mImageNamer = null;
   1736             }
   1737         }
   1738 
   1739         if (mLocationManager != null) mLocationManager.recordLocation(false);
   1740 
   1741         // If we are in an image capture intent and has taken
   1742         // a picture, we just clear it in onPause.
   1743         mJpegImageData = null;
   1744 
   1745         // Remove the messages in the event queue.
   1746         mHandler.removeMessages(SETUP_PREVIEW);
   1747         mHandler.removeMessages(FIRST_TIME_INIT);
   1748         mHandler.removeMessages(CHECK_DISPLAY_ROTATION);
   1749         mHandler.removeMessages(SWITCH_CAMERA);
   1750         mHandler.removeMessages(SWITCH_CAMERA_START_ANIMATION);
   1751         mHandler.removeMessages(CAMERA_OPEN_DONE);
   1752         mHandler.removeMessages(START_PREVIEW_DONE);
   1753         mHandler.removeMessages(OPEN_CAMERA_FAIL);
   1754         mHandler.removeMessages(CAMERA_DISABLED);
   1755 
   1756         mPendingSwitchCameraId = -1;
   1757         if (mFocusManager != null) mFocusManager.removeMessages();
   1758     }
   1759 
   1760     private void initializeControlByIntent() {
   1761         mBlocker = mRootView.findViewById(R.id.blocker);
   1762         mMenu = mRootView.findViewById(R.id.menu);
   1763         mMenu.setOnClickListener(new OnClickListener() {
   1764             @Override
   1765             public void onClick(View v) {
   1766                 if (mPieRenderer != null) {
   1767                     mPieRenderer.showInCenter();
   1768                 }
   1769             }
   1770         });
   1771         if (mIsImageCaptureIntent) {
   1772 
   1773             mActivity.hideSwitcher();
   1774             // Cannot use RotateImageView for "done" and "cancel" button because
   1775             // the tablet layout uses RotateLayout, which cannot be cast to
   1776             // RotateImageView.
   1777             mReviewDoneButton = (Rotatable) mRootView.findViewById(R.id.btn_done);
   1778             mReviewCancelButton = (Rotatable) mRootView.findViewById(R.id.btn_cancel);
   1779             mReviewRetakeButton = mRootView.findViewById(R.id.btn_retake);
   1780             ((View) mReviewCancelButton).setVisibility(View.VISIBLE);
   1781 
   1782             ((View) mReviewDoneButton).setOnClickListener(new OnClickListener() {
   1783                 @Override
   1784                 public void onClick(View v) {
   1785                     onReviewDoneClicked(v);
   1786                 }
   1787             });
   1788             ((View) mReviewCancelButton).setOnClickListener(new OnClickListener() {
   1789                 @Override
   1790                 public void onClick(View v) {
   1791                     onReviewCancelClicked(v);
   1792                 }
   1793             });
   1794 
   1795             mReviewRetakeButton.setOnClickListener(new OnClickListener() {
   1796                 @Override
   1797                 public void onClick(View v) {
   1798                     onReviewRetakeClicked(v);
   1799                 }
   1800             });
   1801 
   1802             // Not grayed out upon disabled, to make the follow-up fade-out
   1803             // effect look smooth. Note that the review done button in tablet
   1804             // layout is not a TwoStateImageView.
   1805             if (mReviewDoneButton instanceof TwoStateImageView) {
   1806                 ((TwoStateImageView) mReviewDoneButton).enableFilter(false);
   1807             }
   1808 
   1809             setupCaptureParams();
   1810         }
   1811     }
   1812 
   1813     /**
   1814      * The focus manager is the first UI related element to get initialized,
   1815      * and it requires the RenderOverlay, so initialize it here
   1816      */
   1817     private void initializeFocusManager() {
   1818         // Create FocusManager object. startPreview needs it.
   1819         mRenderOverlay = (RenderOverlay) mRootView.findViewById(R.id.render_overlay);
   1820         // if mFocusManager not null, reuse it
   1821         // otherwise create a new instance
   1822         if (mFocusManager != null) {
   1823             mFocusManager.removeMessages();
   1824         } else {
   1825             CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
   1826             boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
   1827             String[] defaultFocusModes = mActivity.getResources().getStringArray(
   1828                     R.array.pref_camera_focusmode_default_array);
   1829             mFocusManager = new FocusOverlayManager(mPreferences, defaultFocusModes,
   1830                     mInitialParams, this, mirror,
   1831                     mActivity.getMainLooper());
   1832         }
   1833     }
   1834 
   1835     private void initializeMiscControls() {
   1836         // startPreview needs this.
   1837         mPreviewFrameLayout = (PreviewFrameLayout) mRootView.findViewById(R.id.frame);
   1838         // Set touch focus listener.
   1839         mActivity.setSingleTapUpListener(mPreviewFrameLayout);
   1840 
   1841         mFaceView = (FaceView) mRootView.findViewById(R.id.face_view);
   1842         mPreviewFrameLayout.setOnSizeChangedListener(this);
   1843         mPreviewFrameLayout.setOnLayoutChangeListener(mActivity);
   1844         if (!ApiHelper.HAS_SURFACE_TEXTURE) {
   1845             mPreviewSurfaceView =
   1846                     (PreviewSurfaceView) mRootView.findViewById(R.id.preview_surface_view);
   1847             mPreviewSurfaceView.setVisibility(View.VISIBLE);
   1848             mPreviewSurfaceView.getHolder().addCallback(this);
   1849         }
   1850     }
   1851 
   1852     @Override
   1853     public void onConfigurationChanged(Configuration newConfig) {
   1854         Log.v(TAG, "onConfigurationChanged");
   1855         setDisplayOrientation();
   1856 
   1857         ((ViewGroup) mRootView).removeAllViews();
   1858         LayoutInflater inflater = mActivity.getLayoutInflater();
   1859         inflater.inflate(R.layout.photo_module, (ViewGroup) mRootView);
   1860 
   1861         // from onCreate()
   1862         initializeControlByIntent();
   1863 
   1864         initializeFocusManager();
   1865         initializeMiscControls();
   1866         loadCameraPreferences();
   1867 
   1868         // from initializeFirstTime()
   1869         mShutterButton = mActivity.getShutterButton();
   1870         mShutterButton.setOnShutterButtonListener(this);
   1871         initializeZoom();
   1872         initOnScreenIndicator();
   1873         updateOnScreenIndicators();
   1874         if (mFaceView != null) {
   1875             mFaceView.clear();
   1876             mFaceView.setVisibility(View.VISIBLE);
   1877             mFaceView.setDisplayOrientation(mDisplayOrientation);
   1878             CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
   1879             mFaceView.setMirror(info.facing == CameraInfo.CAMERA_FACING_FRONT);
   1880             mFaceView.resume();
   1881             mFocusManager.setFaceView(mFaceView);
   1882         }
   1883         initializeRenderOverlay();
   1884         onFullScreenChanged(mActivity.isInCameraApp());
   1885         if (mJpegImageData != null) {  // Jpeg data found, picture has been taken.
   1886             showPostCaptureAlert();
   1887         }
   1888     }
   1889 
   1890     @Override
   1891     public void onActivityResult(
   1892             int requestCode, int resultCode, Intent data) {
   1893         switch (requestCode) {
   1894             case REQUEST_CROP: {
   1895                 Intent intent = new Intent();
   1896                 if (data != null) {
   1897                     Bundle extras = data.getExtras();
   1898                     if (extras != null) {
   1899                         intent.putExtras(extras);
   1900                     }
   1901                 }
   1902                 mActivity.setResultEx(resultCode, intent);
   1903                 mActivity.finish();
   1904 
   1905                 File path = mActivity.getFileStreamPath(sTempCropFilename);
   1906                 path.delete();
   1907 
   1908                 break;
   1909             }
   1910         }
   1911     }
   1912 
   1913     private boolean canTakePicture() {
   1914         return isCameraIdle() && (mActivity.getStorageSpace() > Storage.LOW_STORAGE_THRESHOLD);
   1915     }
   1916 
   1917     @Override
   1918     public void autoFocus() {
   1919         mFocusStartTime = System.currentTimeMillis();
   1920         mCameraDevice.autoFocus(mAutoFocusCallback);
   1921         setCameraState(FOCUSING);
   1922     }
   1923 
   1924     @Override
   1925     public void cancelAutoFocus() {
   1926         mCameraDevice.cancelAutoFocus();
   1927         setCameraState(IDLE);
   1928         setCameraParameters(UPDATE_PARAM_PREFERENCE);
   1929     }
   1930 
   1931     // Preview area is touched. Handle touch focus.
   1932     @Override
   1933     public void onSingleTapUp(View view, int x, int y) {
   1934         if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
   1935                 || mCameraState == SNAPSHOT_IN_PROGRESS
   1936                 || mCameraState == SWITCHING_CAMERA
   1937                 || mCameraState == PREVIEW_STOPPED) {
   1938             return;
   1939         }
   1940 
   1941         // Do not trigger touch focus if popup window is opened.
   1942         if (removeTopLevelPopup()) return;
   1943 
   1944         // Check if metering area or focus area is supported.
   1945         if (!mFocusAreaSupported && !mMeteringAreaSupported) return;
   1946         mFocusManager.onSingleTapUp(x, y);
   1947     }
   1948 
   1949     @Override
   1950     public boolean onBackPressed() {
   1951         if (mPieRenderer != null && mPieRenderer.showsItems()) {
   1952             mPieRenderer.hide();
   1953             return true;
   1954         }
   1955         // In image capture mode, back button should:
   1956         // 1) if there is any popup, dismiss them, 2) otherwise, get out of image capture
   1957         if (mIsImageCaptureIntent) {
   1958             if (!removeTopLevelPopup()) {
   1959                 // no popup to dismiss, cancel image capture
   1960                 doCancel();
   1961             }
   1962             return true;
   1963         } else if (!isCameraIdle()) {
   1964             // ignore backs while we're taking a picture
   1965             return true;
   1966         } else {
   1967             return removeTopLevelPopup();
   1968         }
   1969     }
   1970 
   1971     @Override
   1972     public boolean onKeyDown(int keyCode, KeyEvent event) {
   1973         switch (keyCode) {
   1974             case KeyEvent.KEYCODE_FOCUS:
   1975                 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
   1976                     onShutterButtonFocus(true);
   1977                 }
   1978                 return true;
   1979             case KeyEvent.KEYCODE_CAMERA:
   1980                 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
   1981                     onShutterButtonClick();
   1982                 }
   1983                 return true;
   1984             case KeyEvent.KEYCODE_DPAD_CENTER:
   1985                 // If we get a dpad center event without any focused view, move
   1986                 // the focus to the shutter button and press it.
   1987                 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
   1988                     // Start auto-focus immediately to reduce shutter lag. After
   1989                     // the shutter button gets the focus, onShutterButtonFocus()
   1990                     // will be called again but it is fine.
   1991                     if (removeTopLevelPopup()) return true;
   1992                     onShutterButtonFocus(true);
   1993                     if (mShutterButton.isInTouchMode()) {
   1994                         mShutterButton.requestFocusFromTouch();
   1995                     } else {
   1996                         mShutterButton.requestFocus();
   1997                     }
   1998                     mShutterButton.setPressed(true);
   1999                 }
   2000                 return true;
   2001         }
   2002         return false;
   2003     }
   2004 
   2005     @Override
   2006     public boolean onKeyUp(int keyCode, KeyEvent event) {
   2007         switch (keyCode) {
   2008             case KeyEvent.KEYCODE_FOCUS:
   2009                 if (mFirstTimeInitialized) {
   2010                     onShutterButtonFocus(false);
   2011                 }
   2012                 return true;
   2013         }
   2014         return false;
   2015     }
   2016 
   2017     @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
   2018     private void closeCamera() {
   2019         if (mCameraDevice != null) {
   2020             mCameraDevice.setZoomChangeListener(null);
   2021             if(ApiHelper.HAS_FACE_DETECTION) {
   2022                 mCameraDevice.setFaceDetectionListener(null);
   2023             }
   2024             mCameraDevice.setErrorCallback(null);
   2025             CameraHolder.instance().release();
   2026             mFaceDetectionStarted = false;
   2027             mCameraDevice = null;
   2028             setCameraState(PREVIEW_STOPPED);
   2029             mFocusManager.onCameraReleased();
   2030         }
   2031     }
   2032 
   2033     private void setDisplayOrientation() {
   2034         mDisplayRotation = Util.getDisplayRotation(mActivity);
   2035         mDisplayOrientation = Util.getDisplayOrientation(mDisplayRotation, mCameraId);
   2036         mCameraDisplayOrientation = Util.getDisplayOrientation(0, mCameraId);
   2037         if (mFaceView != null) {
   2038             mFaceView.setDisplayOrientation(mDisplayOrientation);
   2039         }
   2040         if (mFocusManager != null) {
   2041             mFocusManager.setDisplayOrientation(mDisplayOrientation);
   2042         }
   2043         // GLRoot also uses the DisplayRotation, and needs to be told to layout to update
   2044         mActivity.getGLRoot().requestLayoutContentPane();
   2045     }
   2046 
   2047     // Only called by UI thread.
   2048     private void setupPreview() {
   2049         mFocusManager.resetTouchFocus();
   2050         startPreview();
   2051         setCameraState(IDLE);
   2052         startFaceDetection();
   2053     }
   2054 
   2055     // This can be called by UI Thread or CameraStartUpThread. So this should
   2056     // not modify the views.
   2057     private void startPreview() {
   2058         mCameraDevice.setErrorCallback(mErrorCallback);
   2059 
   2060         // ICS camera frameworks has a bug. Face detection state is not cleared
   2061         // after taking a picture. Stop the preview to work around it. The bug
   2062         // was fixed in JB.
   2063         if (mCameraState != PREVIEW_STOPPED) stopPreview();
   2064 
   2065         setDisplayOrientation();
   2066 
   2067         if (!mSnapshotOnIdle) {
   2068             // If the focus mode is continuous autofocus, call cancelAutoFocus to
   2069             // resume it because it may have been paused by autoFocus call.
   2070             if (Util.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusManager.getFocusMode())) {
   2071                 mCameraDevice.cancelAutoFocus();
   2072             }
   2073             mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
   2074         }
   2075         setCameraParameters(UPDATE_PARAM_ALL);
   2076 
   2077         if (ApiHelper.HAS_SURFACE_TEXTURE) {
   2078             CameraScreenNail screenNail = (CameraScreenNail) mActivity.mCameraScreenNail;
   2079             if (mSurfaceTexture == null) {
   2080                 Size size = mParameters.getPreviewSize();
   2081                 if (mCameraDisplayOrientation % 180 == 0) {
   2082                     screenNail.setSize(size.width, size.height);
   2083                 } else {
   2084                     screenNail.setSize(size.height, size.width);
   2085                 }
   2086                 screenNail.enableAspectRatioClamping();
   2087                 mActivity.notifyScreenNailChanged();
   2088                 screenNail.acquireSurfaceTexture();
   2089                 mSurfaceTexture = screenNail.getSurfaceTexture();
   2090             }
   2091             mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);
   2092             mCameraDevice.setPreviewTextureAsync((SurfaceTexture) mSurfaceTexture);
   2093         } else {
   2094             mCameraDevice.setDisplayOrientation(mDisplayOrientation);
   2095             mCameraDevice.setPreviewDisplayAsync(mCameraSurfaceHolder);
   2096         }
   2097 
   2098         Log.v(TAG, "startPreview");
   2099         mCameraDevice.startPreviewAsync();
   2100 
   2101         mFocusManager.onPreviewStarted();
   2102 
   2103         if (mSnapshotOnIdle) {
   2104             mHandler.post(mDoSnapRunnable);
   2105         }
   2106     }
   2107 
   2108     private void stopPreview() {
   2109         if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
   2110             Log.v(TAG, "stopPreview");
   2111             mCameraDevice.stopPreview();
   2112             mFaceDetectionStarted = false;
   2113         }
   2114         setCameraState(PREVIEW_STOPPED);
   2115         if (mFocusManager != null) mFocusManager.onPreviewStopped();
   2116     }
   2117 
   2118     @SuppressWarnings("deprecation")
   2119     private void updateCameraParametersInitialize() {
   2120         // Reset preview frame rate to the maximum because it may be lowered by
   2121         // video camera application.
   2122         List<Integer> frameRates = mParameters.getSupportedPreviewFrameRates();
   2123         if (frameRates != null) {
   2124             Integer max = Collections.max(frameRates);
   2125             mParameters.setPreviewFrameRate(max);
   2126         }
   2127 
   2128         mParameters.set(Util.RECORDING_HINT, Util.FALSE);
   2129 
   2130         // Disable video stabilization. Convenience methods not available in API
   2131         // level <= 14
   2132         String vstabSupported = mParameters.get("video-stabilization-supported");
   2133         if ("true".equals(vstabSupported)) {
   2134             mParameters.set("video-stabilization", "false");
   2135         }
   2136     }
   2137 
   2138     private void updateCameraParametersZoom() {
   2139         // Set zoom.
   2140         if (mParameters.isZoomSupported()) {
   2141             mParameters.setZoom(mZoomValue);
   2142         }
   2143     }
   2144 
   2145     @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
   2146     private void setAutoExposureLockIfSupported() {
   2147         if (mAeLockSupported) {
   2148             mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock());
   2149         }
   2150     }
   2151 
   2152     @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
   2153     private void setAutoWhiteBalanceLockIfSupported() {
   2154         if (mAwbLockSupported) {
   2155             mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
   2156         }
   2157     }
   2158 
   2159     @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
   2160     private void setFocusAreasIfSupported() {
   2161         if (mFocusAreaSupported) {
   2162             mParameters.setFocusAreas(mFocusManager.getFocusAreas());
   2163         }
   2164     }
   2165 
   2166     @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
   2167     private void setMeteringAreasIfSupported() {
   2168         if (mMeteringAreaSupported) {
   2169             // Use the same area for focus and metering.
   2170             mParameters.setMeteringAreas(mFocusManager.getMeteringAreas());
   2171         }
   2172     }
   2173 
   2174     private void updateCameraParametersPreference() {
   2175         setAutoExposureLockIfSupported();
   2176         setAutoWhiteBalanceLockIfSupported();
   2177         setFocusAreasIfSupported();
   2178         setMeteringAreasIfSupported();
   2179 
   2180         // Set picture size.
   2181         String pictureSize = mPreferences.getString(
   2182                 CameraSettings.KEY_PICTURE_SIZE, null);
   2183         if (pictureSize == null) {
   2184             CameraSettings.initialCameraPictureSize(mActivity, mParameters);
   2185         } else {
   2186             List<Size> supported = mParameters.getSupportedPictureSizes();
   2187             CameraSettings.setCameraPictureSize(
   2188                     pictureSize, supported, mParameters);
   2189         }
   2190         Size size = mParameters.getPictureSize();
   2191 
   2192         // Set a preview size that is closest to the viewfinder height and has
   2193         // the right aspect ratio.
   2194         List<Size> sizes = mParameters.getSupportedPreviewSizes();
   2195         Size optimalSize = Util.getOptimalPreviewSize(mActivity, sizes,
   2196                 (double) size.width / size.height);
   2197         Size original = mParameters.getPreviewSize();
   2198         if (!original.equals(optimalSize)) {
   2199             mParameters.setPreviewSize(optimalSize.width, optimalSize.height);
   2200 
   2201             // Zoom related settings will be changed for different preview
   2202             // sizes, so set and read the parameters to get latest values
   2203             mCameraDevice.setParameters(mParameters);
   2204             mParameters = mCameraDevice.getParameters();
   2205         }
   2206         Log.v(TAG, "Preview size is " + optimalSize.width + "x" + optimalSize.height);
   2207 
   2208         // Since changing scene mode may change supported values, set scene mode
   2209         // first. HDR is a scene mode. To promote it in UI, it is stored in a
   2210         // separate preference.
   2211         String hdr = mPreferences.getString(CameraSettings.KEY_CAMERA_HDR,
   2212                 mActivity.getString(R.string.pref_camera_hdr_default));
   2213         if (mActivity.getString(R.string.setting_on_value).equals(hdr)) {
   2214             mSceneMode = Util.SCENE_MODE_HDR;
   2215         } else {
   2216             mSceneMode = mPreferences.getString(
   2217                 CameraSettings.KEY_SCENE_MODE,
   2218                 mActivity.getString(R.string.pref_camera_scenemode_default));
   2219         }
   2220         if (Util.isSupported(mSceneMode, mParameters.getSupportedSceneModes())) {
   2221             if (!mParameters.getSceneMode().equals(mSceneMode)) {
   2222                 mParameters.setSceneMode(mSceneMode);
   2223 
   2224                 // Setting scene mode will change the settings of flash mode,
   2225                 // white balance, and focus mode. Here we read back the
   2226                 // parameters, so we can know those settings.
   2227                 mCameraDevice.setParameters(mParameters);
   2228                 mParameters = mCameraDevice.getParameters();
   2229             }
   2230         } else {
   2231             mSceneMode = mParameters.getSceneMode();
   2232             if (mSceneMode == null) {
   2233                 mSceneMode = Parameters.SCENE_MODE_AUTO;
   2234             }
   2235         }
   2236 
   2237         // Set JPEG quality.
   2238         int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
   2239                 CameraProfile.QUALITY_HIGH);
   2240         mParameters.setJpegQuality(jpegQuality);
   2241 
   2242         // For the following settings, we need to check if the settings are
   2243         // still supported by latest driver, if not, ignore the settings.
   2244 
   2245         // Set exposure compensation
   2246         int value = CameraSettings.readExposure(mPreferences);
   2247         int max = mParameters.getMaxExposureCompensation();
   2248         int min = mParameters.getMinExposureCompensation();
   2249         if (value >= min && value <= max) {
   2250             mParameters.setExposureCompensation(value);
   2251         } else {
   2252             Log.w(TAG, "invalid exposure range: " + value);
   2253         }
   2254 
   2255         if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
   2256             // Set flash mode.
   2257             String flashMode = mPreferences.getString(
   2258                     CameraSettings.KEY_FLASH_MODE,
   2259                     mActivity.getString(R.string.pref_camera_flashmode_default));
   2260             List<String> supportedFlash = mParameters.getSupportedFlashModes();
   2261             if (Util.isSupported(flashMode, supportedFlash)) {
   2262                 mParameters.setFlashMode(flashMode);
   2263             } else {
   2264                 flashMode = mParameters.getFlashMode();
   2265                 if (flashMode == null) {
   2266                     flashMode = mActivity.getString(
   2267                             R.string.pref_camera_flashmode_no_flash);
   2268                 }
   2269             }
   2270 
   2271             // Set white balance parameter.
   2272             String whiteBalance = mPreferences.getString(
   2273                     CameraSettings.KEY_WHITE_BALANCE,
   2274                     mActivity.getString(R.string.pref_camera_whitebalance_default));
   2275             if (Util.isSupported(whiteBalance,
   2276                     mParameters.getSupportedWhiteBalance())) {
   2277                 mParameters.setWhiteBalance(whiteBalance);
   2278             } else {
   2279                 whiteBalance = mParameters.getWhiteBalance();
   2280                 if (whiteBalance == null) {
   2281                     whiteBalance = Parameters.WHITE_BALANCE_AUTO;
   2282                 }
   2283             }
   2284 
   2285             // Set focus mode.
   2286             mFocusManager.overrideFocusMode(null);
   2287             mParameters.setFocusMode(mFocusManager.getFocusMode());
   2288         } else {
   2289             mFocusManager.overrideFocusMode(mParameters.getFocusMode());
   2290         }
   2291 
   2292         if (mContinousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
   2293             updateAutoFocusMoveCallback();
   2294         }
   2295     }
   2296 
   2297     @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
   2298     private void updateAutoFocusMoveCallback() {
   2299         if (mParameters.getFocusMode().equals(Util.FOCUS_MODE_CONTINUOUS_PICTURE)) {
   2300             mCameraDevice.setAutoFocusMoveCallback(
   2301                 (AutoFocusMoveCallback) mAutoFocusMoveCallback);
   2302         } else {
   2303             mCameraDevice.setAutoFocusMoveCallback(null);
   2304         }
   2305     }
   2306 
   2307     // We separate the parameters into several subsets, so we can update only
   2308     // the subsets actually need updating. The PREFERENCE set needs extra
   2309     // locking because the preference can be changed from GLThread as well.
   2310     private void setCameraParameters(int updateSet) {
   2311         if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
   2312             updateCameraParametersInitialize();
   2313         }
   2314 
   2315         if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
   2316             updateCameraParametersZoom();
   2317         }
   2318 
   2319         if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
   2320             updateCameraParametersPreference();
   2321         }
   2322 
   2323         mCameraDevice.setParameters(mParameters);
   2324     }
   2325 
   2326     // If the Camera is idle, update the parameters immediately, otherwise
   2327     // accumulate them in mUpdateSet and update later.
   2328     private void setCameraParametersWhenIdle(int additionalUpdateSet) {
   2329         mUpdateSet |= additionalUpdateSet;
   2330         if (mCameraDevice == null) {
   2331             // We will update all the parameters when we open the device, so
   2332             // we don't need to do anything now.
   2333             mUpdateSet = 0;
   2334             return;
   2335         } else if (isCameraIdle()) {
   2336             setCameraParameters(mUpdateSet);
   2337             updateSceneModeUI();
   2338             mUpdateSet = 0;
   2339         } else {
   2340             if (!mHandler.hasMessages(SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
   2341                 mHandler.sendEmptyMessageDelayed(
   2342                         SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
   2343             }
   2344         }
   2345     }
   2346 
   2347     private boolean isCameraIdle() {
   2348         return (mCameraState == IDLE) ||
   2349                 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
   2350                         && (mCameraState != SWITCHING_CAMERA));
   2351     }
   2352 
   2353     private boolean isImageCaptureIntent() {
   2354         String action = mActivity.getIntent().getAction();
   2355         return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
   2356                 || ActivityBase.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
   2357     }
   2358 
   2359     private void setupCaptureParams() {
   2360         Bundle myExtras = mActivity.getIntent().getExtras();
   2361         if (myExtras != null) {
   2362             mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
   2363             mCropValue = myExtras.getString("crop");
   2364         }
   2365     }
   2366 
   2367     private void showPostCaptureAlert() {
   2368         if (mIsImageCaptureIntent) {
   2369             mOnScreenIndicators.setVisibility(View.GONE);
   2370             mMenu.setVisibility(View.GONE);
   2371             Util.fadeIn((View) mReviewDoneButton);
   2372             mShutterButton.setVisibility(View.INVISIBLE);
   2373             Util.fadeIn(mReviewRetakeButton);
   2374         }
   2375     }
   2376 
   2377     private void hidePostCaptureAlert() {
   2378         if (mIsImageCaptureIntent) {
   2379             mOnScreenIndicators.setVisibility(View.VISIBLE);
   2380             mMenu.setVisibility(View.VISIBLE);
   2381             Util.fadeOut((View) mReviewDoneButton);
   2382             mShutterButton.setVisibility(View.VISIBLE);
   2383             Util.fadeOut(mReviewRetakeButton);
   2384         }
   2385     }
   2386 
   2387     @Override
   2388     public void onSharedPreferenceChanged() {
   2389         // ignore the events after "onPause()"
   2390         if (mPaused) return;
   2391 
   2392         boolean recordLocation = RecordLocationPreference.get(
   2393                 mPreferences, mContentResolver);
   2394         mLocationManager.recordLocation(recordLocation);
   2395 
   2396         setCameraParametersWhenIdle(UPDATE_PARAM_PREFERENCE);
   2397         setPreviewFrameLayoutAspectRatio();
   2398         updateOnScreenIndicators();
   2399     }
   2400 
   2401     @Override
   2402     public void onCameraPickerClicked(int cameraId) {
   2403         if (mPaused || mPendingSwitchCameraId != -1) return;
   2404 
   2405         mPendingSwitchCameraId = cameraId;
   2406         if (ApiHelper.HAS_SURFACE_TEXTURE) {
   2407             Log.v(TAG, "Start to copy texture. cameraId=" + cameraId);
   2408             // We need to keep a preview frame for the animation before
   2409             // releasing the camera. This will trigger onPreviewTextureCopied.
   2410             ((CameraScreenNail) mActivity.mCameraScreenNail).copyTexture();
   2411             // Disable all camera controls.
   2412             setCameraState(SWITCHING_CAMERA);
   2413         } else {
   2414             switchCamera();
   2415         }
   2416     }
   2417 
   2418     private void switchCamera() {
   2419         if (mPaused) return;
   2420 
   2421         Log.v(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
   2422         mCameraId = mPendingSwitchCameraId;
   2423         mPendingSwitchCameraId = -1;
   2424         mPhotoControl.setCameraId(mCameraId);
   2425 
   2426         // from onPause
   2427         closeCamera();
   2428         collapseCameraControls();
   2429         if (mFaceView != null) mFaceView.clear();
   2430         if (mFocusManager != null) mFocusManager.removeMessages();
   2431 
   2432         // Restart the camera and initialize the UI. From onCreate.
   2433         mPreferences.setLocalId(mActivity, mCameraId);
   2434         CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
   2435         try {
   2436             mCameraDevice = Util.openCamera(mActivity, mCameraId);
   2437             mParameters = mCameraDevice.getParameters();
   2438         } catch (CameraHardwareException e) {
   2439             Util.showErrorAndFinish(mActivity, R.string.cannot_connect_camera);
   2440             return;
   2441         } catch (CameraDisabledException e) {
   2442             Util.showErrorAndFinish(mActivity, R.string.camera_disabled);
   2443             return;
   2444         }
   2445         initializeCapabilities();
   2446         CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
   2447         boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
   2448         mFocusManager.setMirror(mirror);
   2449         mFocusManager.setParameters(mInitialParams);
   2450         setupPreview();
   2451         loadCameraPreferences();
   2452         initializePhotoControl();
   2453 
   2454         // from initializeFirstTime
   2455         initializeZoom();
   2456         updateOnScreenIndicators();
   2457         showTapToFocusToastIfNeeded();
   2458 
   2459         if (ApiHelper.HAS_SURFACE_TEXTURE) {
   2460             // Start switch camera animation. Post a message because
   2461             // onFrameAvailable from the old camera may already exist.
   2462             mHandler.sendEmptyMessage(SWITCH_CAMERA_START_ANIMATION);
   2463         }
   2464     }
   2465 
   2466     @Override
   2467     public void onPieOpened(int centerX, int centerY) {
   2468         mActivity.cancelActivityTouchHandling();
   2469         mActivity.setSwipingEnabled(false);
   2470         if (mFaceView != null) {
   2471             mFaceView.setBlockDraw(true);
   2472         }
   2473     }
   2474 
   2475     @Override
   2476     public void onPieClosed() {
   2477         mActivity.setSwipingEnabled(true);
   2478         if (mFaceView != null) {
   2479             mFaceView.setBlockDraw(false);
   2480         }
   2481     }
   2482 
   2483     // Preview texture has been copied. Now camera can be released and the
   2484     // animation can be started.
   2485     @Override
   2486     public void onPreviewTextureCopied() {
   2487         mHandler.sendEmptyMessage(SWITCH_CAMERA);
   2488     }
   2489 
   2490     @Override
   2491     public void onCaptureTextureCopied() {
   2492     }
   2493 
   2494     @Override
   2495     public void onUserInteraction() {
   2496         if (!mActivity.isFinishing()) keepScreenOnAwhile();
   2497     }
   2498 
   2499     private void resetScreenOn() {
   2500         mHandler.removeMessages(CLEAR_SCREEN_DELAY);
   2501         mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
   2502     }
   2503 
   2504     private void keepScreenOnAwhile() {
   2505         mHandler.removeMessages(CLEAR_SCREEN_DELAY);
   2506         mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
   2507         mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY);
   2508     }
   2509 
   2510     // TODO: Delete this function after old camera code is removed
   2511     @Override
   2512     public void onRestorePreferencesClicked() {
   2513     }
   2514 
   2515     @Override
   2516     public void onOverriddenPreferencesClicked() {
   2517         if (mPaused) return;
   2518         if (mNotSelectableToast == null) {
   2519             String str = mActivity.getResources().getString(R.string.not_selectable_in_scene_mode);
   2520             mNotSelectableToast = Toast.makeText(mActivity, str, Toast.LENGTH_SHORT);
   2521         }
   2522         mNotSelectableToast.show();
   2523     }
   2524 
   2525     private void showTapToFocusToast() {
   2526         new RotateTextToast(mActivity, R.string.tap_to_focus, mOrientationCompensation).show();
   2527         // Clear the preference.
   2528         Editor editor = mPreferences.edit();
   2529         editor.putBoolean(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, false);
   2530         editor.apply();
   2531     }
   2532 
   2533     private void initializeCapabilities() {
   2534         mInitialParams = mCameraDevice.getParameters();
   2535         mFocusAreaSupported = Util.isFocusAreaSupported(mInitialParams);
   2536         mMeteringAreaSupported = Util.isMeteringAreaSupported(mInitialParams);
   2537         mAeLockSupported = Util.isAutoExposureLockSupported(mInitialParams);
   2538         mAwbLockSupported = Util.isAutoWhiteBalanceLockSupported(mInitialParams);
   2539         mContinousFocusSupported = mInitialParams.getSupportedFocusModes().contains(
   2540                 Util.FOCUS_MODE_CONTINUOUS_PICTURE);
   2541     }
   2542 
   2543     // PreviewFrameLayout size has changed.
   2544     @Override
   2545     public void onSizeChanged(int width, int height) {
   2546         if (mFocusManager != null) mFocusManager.setPreviewSize(width, height);
   2547     }
   2548 
   2549     void setPreviewFrameLayoutAspectRatio() {
   2550         // Set the preview frame aspect ratio according to the picture size.
   2551         Size size = mParameters.getPictureSize();
   2552         mPreviewFrameLayout.setAspectRatio((double) size.width / size.height);
   2553     }
   2554 
   2555     @Override
   2556     public boolean needsSwitcher() {
   2557         return !mIsImageCaptureIntent;
   2558     }
   2559 
   2560     public void showPopup(AbstractSettingPopup popup) {
   2561         mActivity.hideUI();
   2562         mBlocker.setVisibility(View.INVISIBLE);
   2563         setShowMenu(false);
   2564         mPopup = popup;
   2565         // Make sure popup is brought up with the right orientation
   2566         mPopup.setOrientation(mOrientationCompensation, false);
   2567         mPopup.setVisibility(View.VISIBLE);
   2568         FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
   2569                 LayoutParams.WRAP_CONTENT);
   2570         lp.gravity = Gravity.CENTER;
   2571         ((FrameLayout) mRootView).addView(mPopup, lp);
   2572     }
   2573 
   2574     public void dismissPopup(boolean topPopupOnly) {
   2575         dismissPopup(topPopupOnly, true);
   2576     }
   2577 
   2578     private void dismissPopup(boolean topOnly, boolean fullScreen) {
   2579         if (fullScreen) {
   2580             mActivity.showUI();
   2581             mBlocker.setVisibility(View.VISIBLE);
   2582         }
   2583         setShowMenu(fullScreen);
   2584         if (mPopup != null) {
   2585             ((FrameLayout) mRootView).removeView(mPopup);
   2586             mPopup = null;
   2587         }
   2588         mPhotoControl.popupDismissed(topOnly);
   2589     }
   2590 
   2591     @Override
   2592     public void onShowSwitcherPopup() {
   2593         if (mPieRenderer != null && mPieRenderer.showsItems()) {
   2594             mPieRenderer.hide();
   2595         }
   2596     }
   2597 
   2598 }
   2599