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