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