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