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