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