Camera.java revision 595d33f1308089ea4e656e247548a71b218d9fa5
1/*
2 * Copyright (C) 2007 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 com.android.camera.ui.CameraPicker;
20import com.android.camera.ui.FaceView;
21import com.android.camera.ui.IndicatorControlContainer;
22import com.android.camera.ui.RotateImageView;
23import com.android.camera.ui.RotateLayout;
24import com.android.camera.ui.SharePopup;
25import com.android.camera.ui.ZoomControl;
26
27import android.app.Activity;
28import android.content.BroadcastReceiver;
29import android.content.ContentProviderClient;
30import android.content.ContentResolver;
31import android.content.Context;
32import android.content.Intent;
33import android.content.IntentFilter;
34import android.content.SharedPreferences.Editor;
35import android.graphics.Bitmap;
36import android.hardware.Camera.CameraInfo;
37import android.hardware.Camera.Face;
38import android.hardware.Camera.FaceDetectionListener;
39import android.hardware.Camera.Parameters;
40import android.hardware.Camera.PictureCallback;
41import android.hardware.Camera.Size;
42import android.location.Location;
43import android.media.CameraProfile;
44import android.net.Uri;
45import android.os.Bundle;
46import android.os.Handler;
47import android.os.Looper;
48import android.os.Message;
49import android.os.MessageQueue;
50import android.os.SystemClock;
51import android.provider.MediaStore;
52import android.provider.Settings;
53import android.util.Log;
54import android.view.GestureDetector;
55import android.view.Gravity;
56import android.view.KeyEvent;
57import android.view.Menu;
58import android.view.MenuItem;
59import android.view.MenuItem.OnMenuItemClickListener;
60import android.view.MotionEvent;
61import android.view.OrientationEventListener;
62import android.view.SurfaceHolder;
63import android.view.SurfaceView;
64import android.view.View;
65import android.view.Window;
66import android.view.WindowManager;
67import android.view.animation.AnimationUtils;
68import android.widget.TextView;
69import android.widget.Toast;
70
71import java.io.File;
72import java.io.FileNotFoundException;
73import java.io.FileOutputStream;
74import java.io.IOException;
75import java.io.OutputStream;
76import java.util.ArrayList;
77import java.util.Collections;
78import java.util.Formatter;
79import java.util.List;
80
81/** The Camera activity which can preview and take pictures. */
82public class Camera extends ActivityBase implements FocusManager.Listener,
83        View.OnTouchListener, ShutterButton.OnShutterButtonListener,
84        SurfaceHolder.Callback, ModePicker.OnModeChangeListener,
85        FaceDetectionListener, CameraPreference.OnPreferenceChangedListener,
86        LocationManager.Listener {
87
88    private static final String TAG = "camera";
89
90    private static final int CROP_MSG = 1;
91    private static final int FIRST_TIME_INIT = 2;
92    private static final int RESTART_PREVIEW = 3;
93    private static final int CLEAR_SCREEN_DELAY = 4;
94    private static final int SET_CAMERA_PARAMETERS_WHEN_IDLE = 5;
95    private static final int CHECK_DISPLAY_ROTATION = 6;
96    private static final int SHOW_TAP_TO_FOCUS_TOAST = 7;
97    private static final int DISMISS_TAP_TO_FOCUS_TOAST = 8;
98
99    // The subset of parameters we need to update in setCameraParameters().
100    private static final int UPDATE_PARAM_INITIALIZE = 1;
101    private static final int UPDATE_PARAM_ZOOM = 2;
102    private static final int UPDATE_PARAM_PREFERENCE = 4;
103    private static final int UPDATE_PARAM_ALL = -1;
104
105    // When setCameraParametersWhenIdle() is called, we accumulate the subsets
106    // needed to be updated in mUpdateSet.
107    private int mUpdateSet;
108
109    // The brightness settings used when it is set to automatic in the system.
110    // The reason why it is set to 0.7 is just because 1.0 is too bright.
111    private static final float DEFAULT_CAMERA_BRIGHTNESS = 0.7f;
112
113    private static final int SCREEN_DELAY = 2 * 60 * 1000;
114
115    private static final int ZOOM_STOPPED = 0;
116    private static final int ZOOM_START = 1;
117    private static final int ZOOM_STOPPING = 2;
118
119    private int mZoomState = ZOOM_STOPPED;
120    private boolean mSmoothZoomSupported = false;
121    private int mZoomValue;  // The current zoom value.
122    private int mZoomMax;
123    private int mTargetZoomValue;
124    private ZoomControl mZoomControl;
125
126    private Parameters mParameters;
127    private Parameters mInitialParams;
128    private boolean mFocusAreaSupported;
129    private boolean mMeteringAreaSupported;
130
131    private MyOrientationEventListener mOrientationListener;
132    // The degrees of the device rotated clockwise from its natural orientation.
133    private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
134    // The orientation compensation for icons and thumbnails. Ex: if the value
135    // is 90, the UI components should be rotated 90 degrees counter-clockwise.
136    private int mOrientationCompensation = 0;
137    private ComboPreferences mPreferences;
138
139    private static final String sTempCropFilename = "crop-temp";
140
141    private android.hardware.Camera mCameraDevice;
142    private ContentProviderClient mMediaProviderClient;
143    private SurfaceHolder mSurfaceHolder = null;
144    private ShutterButton mShutterButton;
145    private GestureDetector mPopupGestureDetector;
146    private boolean mOpenCameraFail = false;
147    private boolean mCameraDisabled = false;
148
149    private PreviewFrameLayout mPreviewFrameLayout;
150    private View mPreviewFrame;  // Preview frame area.
151
152    // A popup window that contains a bigger thumbnail and a list of apps to share.
153    private SharePopup mSharePopup;
154    // The bitmap of the last captured picture thumbnail and the URI of the
155    // original picture.
156    private Thumbnail mThumbnail;
157    // An imageview showing showing the last captured picture thumbnail.
158    private RotateImageView mThumbnailView;
159    private ModePicker mModePicker;
160    private FaceView mFaceView;
161    private RotateLayout mFocusIndicator;
162
163    // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
164    private String mCropValue;
165    private Uri mSaveUri;
166
167    // On-screen indicator
168    private View mGpsNoSignalIndicator;
169    private View mGpsHasSignalIndicator;
170    private TextView mExposureIndicator;
171
172    private final StringBuilder mBuilder = new StringBuilder();
173    private final Formatter mFormatter = new Formatter(mBuilder);
174    private final Object[] mFormatterArgs = new Object[1];
175
176    /**
177     * An unpublished intent flag requesting to return as soon as capturing
178     * is completed.
179     *
180     * TODO: consider publishing by moving into MediaStore.
181     */
182    private final static String EXTRA_QUICK_CAPTURE =
183            "android.intent.extra.quickCapture";
184
185    // The display rotation in degrees. This is only valid when mCameraState is
186    // not PREVIEW_STOPPED.
187    private int mDisplayRotation;
188    // The value for android.hardware.Camera.setDisplayOrientation.
189    private int mDisplayOrientation;
190    private boolean mPausing;
191    private boolean mFirstTimeInitialized;
192    private boolean mIsImageCaptureIntent;
193
194    private static final int PREVIEW_STOPPED = 0;
195    private static final int IDLE = 1;  // preview is active
196    // Focus is in progress. The exact focus state is in Focus.java.
197    private static final int FOCUSING = 2;
198    private static final int SNAPSHOT_IN_PROGRESS = 3;
199    private int mCameraState = PREVIEW_STOPPED;
200
201    private ContentResolver mContentResolver;
202    private boolean mDidRegister = false;
203
204    private final ArrayList<MenuItem> mGalleryItems = new ArrayList<MenuItem>();
205
206    private LocationManager mLocationManager;
207
208    private final ShutterCallback mShutterCallback = new ShutterCallback();
209    private final PostViewPictureCallback mPostViewPictureCallback =
210            new PostViewPictureCallback();
211    private final RawPictureCallback mRawPictureCallback =
212            new RawPictureCallback();
213    private final AutoFocusCallback mAutoFocusCallback =
214            new AutoFocusCallback();
215    private final ZoomListener mZoomListener = new ZoomListener();
216    private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
217
218    private long mFocusStartTime;
219    private long mCaptureStartTime;
220    private long mShutterCallbackTime;
221    private long mPostViewPictureCallbackTime;
222    private long mRawPictureCallbackTime;
223    private long mJpegPictureCallbackTime;
224    private long mOnResumeTime;
225    private long mPicturesRemaining;
226    private byte[] mJpegImageData;
227
228    // These latency time are for the CameraLatency test.
229    public long mAutoFocusTime;
230    public long mShutterLag;
231    public long mShutterToPictureDisplayedTime;
232    public long mPictureDisplayedToJpegCallbackTime;
233    public long mJpegCallbackFinishTime;
234
235    // This handles everything about focus.
236    private FocusManager mFocusManager;
237    private String mSceneMode;
238    private Toast mNotSelectableToast;
239    private Toast mNoShareToast;
240
241    private final Handler mHandler = new MainHandler();
242    private IndicatorControlContainer mIndicatorControlContainer;
243    private PreferenceGroup mPreferenceGroup;
244
245    // multiple cameras support
246    private int mNumberOfCameras;
247    private int mCameraId;
248    private int mFrontCameraId;
249    private int mBackCameraId;
250
251    private boolean mQuickCapture;
252
253    /**
254     * This Handler is used to post message back onto the main thread of the
255     * application
256     */
257    private class MainHandler extends Handler {
258        @Override
259        public void handleMessage(Message msg) {
260            switch (msg.what) {
261                case RESTART_PREVIEW: {
262                    startPreview();
263                    if (mJpegPictureCallbackTime != 0) {
264                        long now = System.currentTimeMillis();
265                        mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
266                        Log.v(TAG, "mJpegCallbackFinishTime = "
267                                + mJpegCallbackFinishTime + "ms");
268                        mJpegPictureCallbackTime = 0;
269                    }
270                    break;
271                }
272
273                case CLEAR_SCREEN_DELAY: {
274                    getWindow().clearFlags(
275                            WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
276                    break;
277                }
278
279                case FIRST_TIME_INIT: {
280                    initializeFirstTime();
281                    break;
282                }
283
284                case SET_CAMERA_PARAMETERS_WHEN_IDLE: {
285                    setCameraParametersWhenIdle(0);
286                    break;
287                }
288
289                case CHECK_DISPLAY_ROTATION: {
290                    // Restart the preview if display rotation has changed.
291                    // Sometimes this happens when the device is held upside
292                    // down and camera app is opened. Rotation animation will
293                    // take some time and the rotation value we have got may be
294                    // wrong. Framework does not have a callback for this now.
295                    if (Util.getDisplayRotation(Camera.this) != mDisplayRotation
296                            && isCameraIdle()) {
297                        startPreview();
298                    }
299                    if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
300                        mHandler.sendEmptyMessageDelayed(CHECK_DISPLAY_ROTATION, 100);
301                    }
302                    break;
303                }
304
305                case SHOW_TAP_TO_FOCUS_TOAST: {
306                    showTapToFocusToast();
307                    break;
308                }
309
310                case DISMISS_TAP_TO_FOCUS_TOAST: {
311                    View v = findViewById(R.id.tap_to_focus_prompt);
312                    v.setVisibility(View.GONE);
313                    v.setAnimation(AnimationUtils.loadAnimation(Camera.this,
314                            R.anim.on_screen_hint_exit));
315                    break;
316                }
317            }
318        }
319    }
320
321    private void resetExposureCompensation() {
322        String value = mPreferences.getString(CameraSettings.KEY_EXPOSURE,
323                CameraSettings.EXPOSURE_DEFAULT_VALUE);
324        if (!CameraSettings.EXPOSURE_DEFAULT_VALUE.equals(value)) {
325            Editor editor = mPreferences.edit();
326            editor.putString(CameraSettings.KEY_EXPOSURE, "0");
327            editor.apply();
328            if (mIndicatorControlContainer != null) {
329                mIndicatorControlContainer.reloadPreferences();
330            }
331        }
332    }
333
334    private void keepMediaProviderInstance() {
335        // We want to keep a reference to MediaProvider in camera's lifecycle.
336        // TODO: Utilize mMediaProviderClient instance to replace
337        // ContentResolver calls.
338        if (mMediaProviderClient == null) {
339            mMediaProviderClient = getContentResolver()
340                    .acquireContentProviderClient(MediaStore.AUTHORITY);
341        }
342    }
343
344    // Snapshots can only be taken after this is called. It should be called
345    // once only. We could have done these things in onCreate() but we want to
346    // make preview screen appear as soon as possible.
347    private void initializeFirstTime() {
348        if (mFirstTimeInitialized) return;
349
350        // Create orientation listenter. This should be done first because it
351        // takes some time to get first orientation.
352        mOrientationListener = new MyOrientationEventListener(Camera.this);
353        mOrientationListener.enable();
354
355        // Initialize location sevice.
356        boolean recordLocation = RecordLocationPreference.get(
357                mPreferences, getContentResolver());
358        initOnScreenIndicator();
359        mLocationManager.recordLocation(recordLocation);
360
361        keepMediaProviderInstance();
362        checkStorage();
363
364        // Initialize last picture button.
365        mContentResolver = getContentResolver();
366        if (!mIsImageCaptureIntent) {  // no thumbnail in image capture intent
367            initThumbnailButton();
368        }
369
370        // Initialize shutter button.
371        mShutterButton = (ShutterButton) findViewById(R.id.shutter_button);
372        mShutterButton.setOnShutterButtonListener(this);
373        mShutterButton.setVisibility(View.VISIBLE);
374
375        // Initialize focus UI.
376        mPreviewFrame = findViewById(R.id.camera_preview);
377        mPreviewFrame.setOnTouchListener(this);
378        mFocusIndicator = (RotateLayout) findViewById(R.id.focus_indicator_rotate_layout);
379        mFocusManager.initialize(mFocusIndicator, mPreviewFrame, mFaceView, this);
380        mFocusManager.initializeToneGenerator();
381        initializeScreenBrightness();
382        installIntentFilter();
383        initializeZoom();
384        // Show the tap to focus toast if this is the first start.
385        if (mFocusAreaSupported &&
386                mPreferences.getBoolean(CameraSettings.KEY_TAP_TO_FOCUS_PROMPT_SHOWN, true)) {
387            // Delay the toast for one second to wait for orientation.
388            mHandler.sendEmptyMessageDelayed(SHOW_TAP_TO_FOCUS_TOAST, 1000);
389        }
390
391        mFirstTimeInitialized = true;
392        addIdleHandler();
393    }
394
395    private void addIdleHandler() {
396        MessageQueue queue = Looper.myQueue();
397        queue.addIdleHandler(new MessageQueue.IdleHandler() {
398            public boolean queueIdle() {
399                Storage.ensureOSXCompatible();
400                return false;
401            }
402        });
403    }
404
405    private void initThumbnailButton() {
406        // Load the thumbnail from the disk.
407        mThumbnail = Thumbnail.loadFrom(new File(getFilesDir(), Thumbnail.LAST_THUMB_FILENAME));
408        updateThumbnailButton();
409    }
410
411    private void updateThumbnailButton() {
412        // Update last image if URI is invalid and the storage is ready.
413        if ((mThumbnail == null || !Util.isUriValid(mThumbnail.getUri(), mContentResolver))
414                && mPicturesRemaining >= 0) {
415            mThumbnail = Thumbnail.getLastThumbnail(mContentResolver);
416        }
417        if (mThumbnail != null) {
418            mThumbnailView.setBitmap(mThumbnail.getBitmap());
419        } else {
420            mThumbnailView.setBitmap(null);
421        }
422    }
423
424    // If the activity is paused and resumed, this method will be called in
425    // onResume.
426    private void initializeSecondTime() {
427        // Start orientation listener as soon as possible because it takes
428        // some time to get first orientation.
429        mOrientationListener.enable();
430
431        // Start location update if needed.
432        boolean recordLocation = RecordLocationPreference.get(
433                mPreferences, getContentResolver());
434        mLocationManager.recordLocation(recordLocation);
435
436        installIntentFilter();
437        mFocusManager.initializeToneGenerator();
438        initializeZoom();
439        keepMediaProviderInstance();
440        checkStorage();
441
442        if (!mIsImageCaptureIntent) {
443            updateThumbnailButton();
444            mModePicker.setCurrentMode(ModePicker.MODE_CAMERA);
445        }
446    }
447
448    private class ZoomChangeListener implements ZoomControl.OnZoomChangedListener {
449        // only for immediate zoom
450        @Override
451        public void onZoomValueChanged(int index) {
452            Camera.this.onZoomValueChanged(index);
453        }
454
455        // only for smooth zoom
456        @Override
457        public void onZoomStateChanged(int state) {
458            if (mPausing) return;
459
460            Log.v(TAG, "zoom picker state=" + state);
461            if (state == ZoomControl.ZOOM_IN) {
462                Camera.this.onZoomValueChanged(mZoomMax);
463            } else if (state == ZoomControl.ZOOM_OUT) {
464                Camera.this.onZoomValueChanged(0);
465            } else {
466                mTargetZoomValue = -1;
467                if (mZoomState == ZOOM_START) {
468                    mZoomState = ZOOM_STOPPING;
469                    mCameraDevice.stopSmoothZoom();
470                }
471            }
472        }
473    }
474
475    private void initializeZoom() {
476        if (!mParameters.isZoomSupported()) return;
477        mZoomMax = mParameters.getMaxZoom();
478        mSmoothZoomSupported = mParameters.isSmoothZoomSupported();
479        mZoomControl.setZoomMax(mZoomMax);
480        mZoomControl.setZoomIndex(mParameters.getZoom());
481        mZoomControl.setSmoothZoomSupported(mSmoothZoomSupported);
482        mZoomControl.setOnZoomChangeListener(new ZoomChangeListener());
483        mCameraDevice.setZoomChangeListener(mZoomListener);
484    }
485
486    private void onZoomValueChanged(int index) {
487        // Not useful to change zoom value when the activity is paused.
488        if (mPausing) return;
489
490        if (mSmoothZoomSupported) {
491            if (mTargetZoomValue != index && mZoomState != ZOOM_STOPPED) {
492                mTargetZoomValue = index;
493                if (mZoomState == ZOOM_START) {
494                    mZoomState = ZOOM_STOPPING;
495                    mCameraDevice.stopSmoothZoom();
496                }
497            } else if (mZoomState == ZOOM_STOPPED && mZoomValue != index) {
498                mTargetZoomValue = index;
499                mCameraDevice.startSmoothZoom(index);
500                mZoomState = ZOOM_START;
501            }
502        } else {
503            mZoomValue = index;
504            setCameraParametersWhenIdle(UPDATE_PARAM_ZOOM);
505        }
506    }
507
508    @Override
509    public void startFaceDetection() {
510        if (mParameters.getMaxNumDetectedFaces() > 0) {
511            mFaceView = (FaceView) findViewById(R.id.face_view);
512            mFaceView.clear();
513            mFaceView.setVisibility(View.VISIBLE);
514            mFaceView.setDisplayOrientation(mDisplayOrientation);
515            CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
516            mFaceView.setMirror(info.facing == CameraInfo.CAMERA_FACING_FRONT);
517            mFaceView.resume();
518            mCameraDevice.setFaceDetectionListener(this);
519            mCameraDevice.startFaceDetection();
520        }
521    }
522
523    @Override
524    public void stopFaceDetection() {
525        if (mParameters.getMaxNumDetectedFaces() > 0) {
526            mCameraDevice.setFaceDetectionListener(null);
527            mCameraDevice.stopFaceDetection();
528            if (mFaceView != null) mFaceView.clear();
529        }
530    }
531
532    private class PopupGestureListener
533            extends GestureDetector.SimpleOnGestureListener {
534        @Override
535        public boolean onDown(MotionEvent e) {
536            // Check if the popup window is visible.
537            View popup = mIndicatorControlContainer.getActiveSettingPopup();
538            if (popup == null) return false;
539
540
541            // Let popup window, indicator control or preview frame handle the
542            // event by themselves. Dismiss the popup window if users touch on
543            // other areas.
544            if (!Util.pointInView(e.getX(), e.getY(), popup)
545                    && !Util.pointInView(e.getX(), e.getY(), mIndicatorControlContainer)
546                    && !Util.pointInView(e.getX(), e.getY(), mPreviewFrame)) {
547                mIndicatorControlContainer.dismissSettingPopup();
548                // Let event fall through.
549            }
550            return false;
551        }
552    }
553
554    @Override
555    public boolean dispatchTouchEvent(MotionEvent m) {
556        // Check if the popup window should be dismissed first.
557        if (mPopupGestureDetector != null && mPopupGestureDetector.onTouchEvent(m)) {
558            return true;
559        }
560
561        return super.dispatchTouchEvent(m);
562    }
563
564    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
565        @Override
566        public void onReceive(Context context, Intent intent) {
567            String action = intent.getAction();
568            Log.d(TAG, "Received intent action=" + action);
569            if (action.equals(Intent.ACTION_MEDIA_MOUNTED)
570                    || action.equals(Intent.ACTION_MEDIA_UNMOUNTED)
571                    || action.equals(Intent.ACTION_MEDIA_CHECKING)) {
572                checkStorage();
573            } else if (action.equals(Intent.ACTION_MEDIA_SCANNER_FINISHED)) {
574                checkStorage();
575                if (!mIsImageCaptureIntent) {
576                    updateThumbnailButton();
577                }
578            }
579        }
580    };
581
582    private void initOnScreenIndicator() {
583        mGpsNoSignalIndicator = findViewById(R.id.onscreen_gps_indicator_no_signal);
584        mGpsHasSignalIndicator = findViewById(R.id.onscreen_gps_indicator_on);
585        mExposureIndicator = (TextView) findViewById(R.id.onscreen_exposure_indicator);
586    }
587
588    @Override
589    public void showGpsOnScreenIndicator(boolean hasSignal) {
590        if (hasSignal) {
591            if (mGpsNoSignalIndicator != null) {
592                mGpsNoSignalIndicator.setVisibility(View.GONE);
593            }
594            if (mGpsHasSignalIndicator != null) {
595                mGpsHasSignalIndicator.setVisibility(View.VISIBLE);
596            }
597        } else {
598            if (mGpsNoSignalIndicator != null) {
599                mGpsNoSignalIndicator.setVisibility(View.VISIBLE);
600            }
601            if (mGpsHasSignalIndicator != null) {
602                mGpsHasSignalIndicator.setVisibility(View.GONE);
603            }
604        }
605    }
606
607    @Override
608    public void hideGpsOnScreenIndicator() {
609        if (mGpsNoSignalIndicator != null) mGpsNoSignalIndicator.setVisibility(View.GONE);
610        if (mGpsHasSignalIndicator != null) mGpsHasSignalIndicator.setVisibility(View.GONE);
611    }
612
613    private void updateExposureOnScreenIndicator(int value) {
614        if (mExposureIndicator == null) return;
615
616        if (value == 0) {
617            mExposureIndicator.setText("");
618            mExposureIndicator.setVisibility(View.GONE);
619        } else {
620            float step = mParameters.getExposureCompensationStep();
621            mFormatterArgs[0] = value * step;
622            mBuilder.delete(0, mBuilder.length());
623            mFormatter.format("%+1.1f", mFormatterArgs);
624            String exposure = mFormatter.toString();
625            mExposureIndicator.setText(exposure);
626            mExposureIndicator.setVisibility(View.VISIBLE);
627        }
628    }
629
630    private final class ShutterCallback
631            implements android.hardware.Camera.ShutterCallback {
632        public void onShutter() {
633            mShutterCallbackTime = System.currentTimeMillis();
634            mShutterLag = mShutterCallbackTime - mCaptureStartTime;
635            Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
636            mFocusManager.onShutter();
637        }
638    }
639
640    private final class PostViewPictureCallback implements PictureCallback {
641        public void onPictureTaken(
642                byte [] data, android.hardware.Camera camera) {
643            mPostViewPictureCallbackTime = System.currentTimeMillis();
644            Log.v(TAG, "mShutterToPostViewCallbackTime = "
645                    + (mPostViewPictureCallbackTime - mShutterCallbackTime)
646                    + "ms");
647        }
648    }
649
650    private final class RawPictureCallback implements PictureCallback {
651        public void onPictureTaken(
652                byte [] rawData, android.hardware.Camera camera) {
653            mRawPictureCallbackTime = System.currentTimeMillis();
654            Log.v(TAG, "mShutterToRawCallbackTime = "
655                    + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
656        }
657    }
658
659    private final class JpegPictureCallback implements PictureCallback {
660        Location mLocation;
661
662        public JpegPictureCallback(Location loc) {
663            mLocation = loc;
664        }
665
666        public void onPictureTaken(
667                final byte [] jpegData, final android.hardware.Camera camera) {
668            if (mPausing) {
669                return;
670            }
671
672            mJpegPictureCallbackTime = System.currentTimeMillis();
673            // If postview callback has arrived, the captured image is displayed
674            // in postview callback. If not, the captured image is displayed in
675            // raw picture callback.
676            if (mPostViewPictureCallbackTime != 0) {
677                mShutterToPictureDisplayedTime =
678                        mPostViewPictureCallbackTime - mShutterCallbackTime;
679                mPictureDisplayedToJpegCallbackTime =
680                        mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
681            } else {
682                mShutterToPictureDisplayedTime =
683                        mRawPictureCallbackTime - mShutterCallbackTime;
684                mPictureDisplayedToJpegCallbackTime =
685                        mJpegPictureCallbackTime - mRawPictureCallbackTime;
686            }
687            Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
688                    + mPictureDisplayedToJpegCallbackTime + "ms");
689
690            if (!mIsImageCaptureIntent) {
691                enableCameraControls(true);
692
693                // We want to show the taken picture for a while, so we wait
694                // for at least 0.5 second before restarting the preview.
695                long delay = 500 - mPictureDisplayedToJpegCallbackTime;
696                if (delay < 0) {
697                    startPreview();
698                } else {
699                    mHandler.sendEmptyMessageDelayed(RESTART_PREVIEW, delay);
700                }
701            }
702
703            if (!mIsImageCaptureIntent) {
704                storeImage(jpegData, mLocation);
705            } else {
706                mJpegImageData = jpegData;
707                if (!mQuickCapture) {
708                    showPostCaptureAlert();
709                } else {
710                    doAttach();
711                }
712            }
713
714            // Check this in advance of each shot so we don't add to shutter
715            // latency. It's true that someone else could write to the SD card in
716            // the mean time and fill it, but that could have happened between the
717            // shutter press and saving the JPEG too.
718            checkStorage();
719
720            if (!mHandler.hasMessages(RESTART_PREVIEW)) {
721                long now = System.currentTimeMillis();
722                mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
723                Log.v(TAG, "mJpegCallbackFinishTime = "
724                        + mJpegCallbackFinishTime + "ms");
725                mJpegPictureCallbackTime = 0;
726            }
727        }
728    }
729
730    private final class AutoFocusCallback
731            implements android.hardware.Camera.AutoFocusCallback {
732        public void onAutoFocus(
733                boolean focused, android.hardware.Camera camera) {
734            if (mPausing) return;
735
736            mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
737            Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms");
738            mFocusManager.onAutoFocus(focused);
739            // If focus completes and the snapshot is not started, enable the
740            // controls.
741            if (mFocusManager.isFocusCompleted()) {
742                enableCameraControls(true);
743            }
744        }
745    }
746
747    private final class ZoomListener
748            implements android.hardware.Camera.OnZoomChangeListener {
749        @Override
750        public void onZoomChange(
751                int value, boolean stopped, android.hardware.Camera camera) {
752            Log.v(TAG, "Zoom changed: value=" + value + ". stopped="+ stopped);
753            mZoomValue = value;
754
755            // Update the UI when we get zoom value.
756            mZoomControl.setZoomIndex(value);
757
758            // Keep mParameters up to date. We do not getParameter again in
759            // takePicture. If we do not do this, wrong zoom value will be set.
760            mParameters.setZoom(value);
761
762            if (stopped && mZoomState != ZOOM_STOPPED) {
763                if (mTargetZoomValue != -1 && value != mTargetZoomValue) {
764                    mCameraDevice.startSmoothZoom(mTargetZoomValue);
765                    mZoomState = ZOOM_START;
766                } else {
767                    mZoomState = ZOOM_STOPPED;
768                }
769            }
770        }
771    }
772
773    private void storeImage(final byte[] data, Location loc) {
774        long dateTaken = System.currentTimeMillis();
775        String title = Util.createJpegName(dateTaken);
776        int orientation = Exif.getOrientation(data);
777        Uri uri = Storage.addImage(mContentResolver, title, dateTaken,
778                loc, orientation, data);
779        if (uri != null) {
780            // Create a thumbnail whose width is equal or bigger than that of the preview.
781            int ratio = (int) Math.ceil((double) mParameters.getPictureSize().width
782                    / mPreviewFrameLayout.getWidth());
783            int inSampleSize = Integer.highestOneBit(ratio);
784            mThumbnail = Thumbnail.createThumbnail(data, orientation, inSampleSize, uri);
785            if (mThumbnail != null) {
786                mThumbnailView.setBitmap(mThumbnail.getBitmap());
787            }
788            Util.broadcastNewPicture(this, uri);
789        }
790    }
791
792    @Override
793    public boolean capture() {
794        // If we are already in the middle of taking a snapshot then ignore.
795        if (mCameraState == SNAPSHOT_IN_PROGRESS || mCameraDevice == null) {
796            return false;
797        }
798        mCaptureStartTime = System.currentTimeMillis();
799        mPostViewPictureCallbackTime = 0;
800        enableCameraControls(false);
801        mJpegImageData = null;
802
803        // Set rotation and gps data.
804        Util.setRotationParameter(mParameters, mCameraId, mOrientation);
805        Location loc = mLocationManager.getCurrentLocation();
806        Util.setGpsParameters(mParameters, loc);
807        mCameraDevice.setParameters(mParameters);
808
809        mCameraDevice.takePicture(mShutterCallback, mRawPictureCallback,
810                mPostViewPictureCallback, new JpegPictureCallback(loc));
811        mCameraState = SNAPSHOT_IN_PROGRESS;
812        return true;
813    }
814
815    @Override
816    public void setFocusParameters() {
817        setCameraParameters(UPDATE_PARAM_PREFERENCE);
818    }
819
820    private boolean saveDataToFile(String filePath, byte[] data) {
821        FileOutputStream f = null;
822        try {
823            f = new FileOutputStream(filePath);
824            f.write(data);
825        } catch (IOException e) {
826            return false;
827        } finally {
828            Util.closeSilently(f);
829        }
830        return true;
831    }
832
833    @Override
834    public void onCreate(Bundle icicle) {
835        super.onCreate(icicle);
836
837        mIsImageCaptureIntent = isImageCaptureIntent();
838        setContentView(R.layout.camera);
839        if (mIsImageCaptureIntent) {
840            findViewById(R.id.btn_cancel).setVisibility(View.VISIBLE);
841        } else {
842            mThumbnailView = (RotateImageView) findViewById(R.id.thumbnail);
843            mThumbnailView.setVisibility(View.VISIBLE);
844        }
845
846        mPreferences = new ComboPreferences(this);
847        CameraSettings.upgradeGlobalPreferences(mPreferences.getGlobal());
848        mFocusManager = new FocusManager(mPreferences,
849                getString(R.string.pref_camera_focusmode_default));
850
851        mCameraId = CameraSettings.readPreferredCameraId(mPreferences);
852
853        // Testing purpose. Launch a specific camera through the intent extras.
854        int intentCameraId = Util.getCameraFacingIntentExtras(this);
855        if (intentCameraId != -1) {
856            mCameraId = intentCameraId;
857        }
858
859        mPreferences.setLocalId(this, mCameraId);
860        CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
861
862        mNumberOfCameras = CameraHolder.instance().getNumberOfCameras();
863        mQuickCapture = getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
864
865        // we need to reset exposure for the preview
866        resetExposureCompensation();
867
868        /*
869         * To reduce startup time, we start the preview in another thread.
870         * We make sure the preview is started at the end of onCreate.
871         */
872        Thread startPreviewThread = new Thread(new Runnable() {
873            public void run() {
874                try {
875                    mCameraDevice = Util.openCamera(Camera.this, mCameraId);
876                    initializeCapabilities();
877                    startPreview();
878                } catch (CameraHardwareException e) {
879                    mOpenCameraFail = true;
880                } catch (CameraDisabledException e) {
881                    mCameraDisabled = true;
882                }
883            }
884        });
885        startPreviewThread.start();
886
887        Util.enterLightsOutMode(getWindow());
888
889        // don't set mSurfaceHolder here. We have it set ONLY within
890        // surfaceChanged / surfaceDestroyed, other parts of the code
891        // assume that when it is set, the surface is also set.
892        SurfaceView preview = (SurfaceView) findViewById(R.id.camera_preview);
893        SurfaceHolder holder = preview.getHolder();
894        holder.addCallback(this);
895        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
896
897        if (mIsImageCaptureIntent) {
898            setupCaptureParams();
899        } else {
900            mModePicker = (ModePicker) findViewById(R.id.mode_picker);
901            mModePicker.setVisibility(View.VISIBLE);
902            mModePicker.setOnModeChangeListener(this);
903            mModePicker.setCurrentMode(ModePicker.MODE_CAMERA);
904        }
905
906        mZoomControl = (ZoomControl) findViewById(R.id.zoom_control);
907        mLocationManager = new LocationManager(this, this);
908
909        // Make sure preview is started.
910        try {
911            startPreviewThread.join();
912            if (mOpenCameraFail) {
913                Util.showErrorAndFinish(this, R.string.cannot_connect_camera);
914                return;
915            } else if (mCameraDisabled) {
916                Util.showErrorAndFinish(this, R.string.camera_disabled);
917                return;
918            }
919        } catch (InterruptedException ex) {
920            // ignore
921        }
922
923        mBackCameraId = CameraHolder.instance().getBackCameraId();
924        mFrontCameraId = CameraHolder.instance().getFrontCameraId();
925
926        // Do this after starting preview because it depends on camera
927        // parameters.
928        initializeIndicatorControl();
929    }
930
931    private void overrideCameraSettings(final String flashMode,
932            final String whiteBalance, final String focusMode) {
933        if (mIndicatorControlContainer != null) {
934            mIndicatorControlContainer.overrideSettings(
935                    CameraSettings.KEY_FLASH_MODE, flashMode,
936                    CameraSettings.KEY_WHITE_BALANCE, whiteBalance,
937                    CameraSettings.KEY_FOCUS_MODE, focusMode);
938        }
939    }
940
941    private void updateSceneModeUI() {
942        // If scene mode is set, we cannot set flash mode, white balance, and
943        // focus mode, instead, we read it from driver
944        if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
945            overrideCameraSettings(mParameters.getFlashMode(),
946                    mParameters.getWhiteBalance(), mParameters.getFocusMode());
947        } else {
948            overrideCameraSettings(null, null, null);
949        }
950    }
951
952    private void loadCameraPreferences() {
953        CameraSettings settings = new CameraSettings(this, mInitialParams,
954                mCameraId, CameraHolder.instance().getCameraInfo());
955        mPreferenceGroup = settings.getPreferenceGroup(R.xml.camera_preferences);
956    }
957
958    private void initializeIndicatorControl() {
959        // setting the indicator buttons.
960        mIndicatorControlContainer =
961                (IndicatorControlContainer) findViewById(R.id.indicator_control);
962        if (mIndicatorControlContainer == null) return;
963        loadCameraPreferences();
964        final String[] SETTING_KEYS = {
965                CameraSettings.KEY_WHITE_BALANCE,
966                CameraSettings.KEY_SCENE_MODE};
967        final String[] OTHER_SETTING_KEYS = {
968                CameraSettings.KEY_RECORD_LOCATION,
969                CameraSettings.KEY_FOCUS_MODE,
970                CameraSettings.KEY_EXPOSURE,
971                CameraSettings.KEY_PICTURE_SIZE};
972
973        CameraPicker.setImageResourceId(R.drawable.ic_switch_photo_facing_holo_light);
974        mIndicatorControlContainer.initialize(this, mPreferenceGroup,
975                CameraSettings.KEY_FLASH_MODE, mParameters.isZoomSupported(),
976                SETTING_KEYS, OTHER_SETTING_KEYS);
977        mIndicatorControlContainer.setListener(this);
978    }
979
980    private boolean collapseCameraControls() {
981        if ((mIndicatorControlContainer != null)
982                && mIndicatorControlContainer.dismissSettingPopup()) {
983            return true;
984        }
985        return false;
986    }
987
988    private void enableCameraControls(boolean enable) {
989        if (mIndicatorControlContainer != null) {
990            mIndicatorControlContainer.setEnabled(enable);
991        }
992        if (mModePicker != null) mModePicker.setEnabled(enable);
993        if (mZoomControl != null) mZoomControl.setEnabled(enable);
994    }
995
996    public static int roundOrientation(int orientation) {
997        return ((orientation + 45) / 90 * 90) % 360;
998    }
999
1000    private class MyOrientationEventListener
1001            extends OrientationEventListener {
1002        public MyOrientationEventListener(Context context) {
1003            super(context);
1004        }
1005
1006        @Override
1007        public void onOrientationChanged(int orientation) {
1008            // We keep the last known orientation. So if the user first orient
1009            // the camera then point the camera to floor or sky, we still have
1010            // the correct orientation.
1011            if (orientation == ORIENTATION_UNKNOWN) return;
1012            mOrientation = roundOrientation(orientation);
1013            // When the screen is unlocked, display rotation may change. Always
1014            // calculate the up-to-date orientationCompensation.
1015            int orientationCompensation = mOrientation
1016                    + Util.getDisplayRotation(Camera.this);
1017            if (mOrientationCompensation != orientationCompensation) {
1018                mOrientationCompensation = orientationCompensation;
1019                setOrientationIndicator(mOrientationCompensation);
1020            }
1021
1022            // Show the toast after getting the first orientation changed.
1023            if (mHandler.hasMessages(SHOW_TAP_TO_FOCUS_TOAST)) {
1024                mHandler.removeMessages(SHOW_TAP_TO_FOCUS_TOAST);
1025                showTapToFocusToast();
1026            }
1027        }
1028    }
1029
1030    private void setOrientationIndicator(int degree) {
1031        if (mThumbnailView != null) mThumbnailView.setDegree(degree);
1032        if (mModePicker != null) mModePicker.setDegree(degree);
1033        if (mSharePopup != null) mSharePopup.setOrientation(degree);
1034        if (mIndicatorControlContainer != null) mIndicatorControlContainer.setDegree(degree);
1035        if (mZoomControl != null) mZoomControl.setDegree(degree);
1036        if (mFocusIndicator != null) mFocusIndicator.setOrientation(degree);
1037        if (mFaceView != null) mFaceView.setOrientation(degree);
1038    }
1039
1040    @Override
1041    public void onStop() {
1042        super.onStop();
1043        if (mMediaProviderClient != null) {
1044            mMediaProviderClient.release();
1045            mMediaProviderClient = null;
1046        }
1047    }
1048
1049    private void checkStorage() {
1050        mPicturesRemaining = Storage.getAvailableSpace();
1051        if (mPicturesRemaining > 0) {
1052            mPicturesRemaining /= 1500000;
1053        }
1054        updateStorageHint();
1055    }
1056
1057    @OnClickAttr
1058    public void onThumbnailClicked(View v) {
1059        if (isCameraIdle() && mThumbnail != null) {
1060            showSharePopup();
1061        }
1062    }
1063
1064    @OnClickAttr
1065    public void onRetakeButtonClicked(View v) {
1066        hidePostCaptureAlert();
1067        startPreview();
1068    }
1069
1070    @OnClickAttr
1071    public void onDoneButtonClicked(View v) {
1072        doAttach();
1073    }
1074
1075    @OnClickAttr
1076    public void onCancelButtonClicked(View v) {
1077        doCancel();
1078    }
1079
1080    private void doAttach() {
1081        if (mPausing) {
1082            return;
1083        }
1084
1085        byte[] data = mJpegImageData;
1086
1087        if (mCropValue == null) {
1088            // First handle the no crop case -- just return the value.  If the
1089            // caller specifies a "save uri" then write the data to it's
1090            // stream. Otherwise, pass back a scaled down version of the bitmap
1091            // directly in the extras.
1092            if (mSaveUri != null) {
1093                OutputStream outputStream = null;
1094                try {
1095                    outputStream = mContentResolver.openOutputStream(mSaveUri);
1096                    outputStream.write(data);
1097                    outputStream.close();
1098
1099                    setResultEx(RESULT_OK);
1100                    finish();
1101                } catch (IOException ex) {
1102                    // ignore exception
1103                } finally {
1104                    Util.closeSilently(outputStream);
1105                }
1106            } else {
1107                int orientation = Exif.getOrientation(data);
1108                Bitmap bitmap = Util.makeBitmap(data, 50 * 1024);
1109                bitmap = Util.rotate(bitmap, orientation);
1110                setResultEx(RESULT_OK,
1111                        new Intent("inline-data").putExtra("data", bitmap));
1112                finish();
1113            }
1114        } else {
1115            // Save the image to a temp file and invoke the cropper
1116            Uri tempUri = null;
1117            FileOutputStream tempStream = null;
1118            try {
1119                File path = getFileStreamPath(sTempCropFilename);
1120                path.delete();
1121                tempStream = openFileOutput(sTempCropFilename, 0);
1122                tempStream.write(data);
1123                tempStream.close();
1124                tempUri = Uri.fromFile(path);
1125            } catch (FileNotFoundException ex) {
1126                setResultEx(Activity.RESULT_CANCELED);
1127                finish();
1128                return;
1129            } catch (IOException ex) {
1130                setResultEx(Activity.RESULT_CANCELED);
1131                finish();
1132                return;
1133            } finally {
1134                Util.closeSilently(tempStream);
1135            }
1136
1137            Bundle newExtras = new Bundle();
1138            if (mCropValue.equals("circle")) {
1139                newExtras.putString("circleCrop", "true");
1140            }
1141            if (mSaveUri != null) {
1142                newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1143            } else {
1144                newExtras.putBoolean("return-data", true);
1145            }
1146
1147            Intent cropIntent = new Intent("com.android.camera.action.CROP");
1148
1149            cropIntent.setData(tempUri);
1150            cropIntent.putExtras(newExtras);
1151
1152            startActivityForResult(cropIntent, CROP_MSG);
1153        }
1154    }
1155
1156    private void doCancel() {
1157        setResultEx(RESULT_CANCELED, new Intent());
1158        finish();
1159    }
1160
1161    public void onShutterButtonFocus(ShutterButton button, boolean pressed) {
1162        switch (button.getId()) {
1163            case R.id.shutter_button:
1164                doFocus(pressed);
1165                break;
1166        }
1167    }
1168
1169    public void onShutterButtonClick(ShutterButton button) {
1170        switch (button.getId()) {
1171            case R.id.shutter_button:
1172                doSnap();
1173                break;
1174        }
1175    }
1176
1177    private OnScreenHint mStorageHint;
1178
1179    private void updateStorageHint() {
1180        String noStorageText = null;
1181
1182        if (mPicturesRemaining == Storage.UNAVAILABLE) {
1183            noStorageText = getString(R.string.no_storage);
1184        } else if (mPicturesRemaining == Storage.PREPARING) {
1185            noStorageText = getString(R.string.preparing_sd);
1186        } else if (mPicturesRemaining == Storage.UNKNOWN_SIZE) {
1187            noStorageText = getString(R.string.access_sd_fail);
1188        } else if (mPicturesRemaining < 1L) {
1189            noStorageText = getString(R.string.not_enough_space);
1190        }
1191
1192        if (noStorageText != null) {
1193            if (mStorageHint == null) {
1194                mStorageHint = OnScreenHint.makeText(this, noStorageText);
1195            } else {
1196                mStorageHint.setText(noStorageText);
1197            }
1198            mStorageHint.show();
1199        } else if (mStorageHint != null) {
1200            mStorageHint.cancel();
1201            mStorageHint = null;
1202        }
1203    }
1204
1205    private void installIntentFilter() {
1206        // install an intent filter to receive SD card related events.
1207        IntentFilter intentFilter =
1208                new IntentFilter(Intent.ACTION_MEDIA_MOUNTED);
1209        intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
1210        intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
1211        intentFilter.addAction(Intent.ACTION_MEDIA_CHECKING);
1212        intentFilter.addDataScheme("file");
1213        registerReceiver(mReceiver, intentFilter);
1214        mDidRegister = true;
1215    }
1216
1217    private void initializeScreenBrightness() {
1218        Window win = getWindow();
1219        // Overright the brightness settings if it is automatic
1220        int mode = Settings.System.getInt(
1221                getContentResolver(),
1222                Settings.System.SCREEN_BRIGHTNESS_MODE,
1223                Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
1224        if (mode == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC) {
1225            WindowManager.LayoutParams winParams = win.getAttributes();
1226            winParams.screenBrightness = DEFAULT_CAMERA_BRIGHTNESS;
1227            win.setAttributes(winParams);
1228        }
1229    }
1230
1231    @Override
1232    protected void onResume() {
1233        super.onResume();
1234        mPausing = false;
1235        if (mOpenCameraFail || mCameraDisabled) return;
1236
1237        mJpegPictureCallbackTime = 0;
1238        mZoomValue = 0;
1239
1240        // Start the preview if it is not started.
1241        if (mCameraState == PREVIEW_STOPPED) {
1242            try {
1243                mCameraDevice = Util.openCamera(this, mCameraId);
1244                initializeCapabilities();
1245                resetExposureCompensation();
1246                startPreview();
1247            } catch(CameraHardwareException e) {
1248                Util.showErrorAndFinish(this, R.string.cannot_connect_camera);
1249                return;
1250            } catch(CameraDisabledException e) {
1251                Util.showErrorAndFinish(this, R.string.camera_disabled);
1252                return;
1253            }
1254        }
1255
1256        if (mSurfaceHolder != null) {
1257            // If first time initialization is not finished, put it in the
1258            // message queue.
1259            if (!mFirstTimeInitialized) {
1260                mHandler.sendEmptyMessage(FIRST_TIME_INIT);
1261            } else {
1262                initializeSecondTime();
1263            }
1264        }
1265        keepScreenOnAwhile();
1266
1267        if (mCameraState == IDLE) {
1268            mOnResumeTime = SystemClock.uptimeMillis();
1269            mHandler.sendEmptyMessageDelayed(CHECK_DISPLAY_ROTATION, 100);
1270        }
1271    }
1272
1273    @Override
1274    protected void onPause() {
1275        mPausing = true;
1276        stopPreview();
1277        // Close the camera now because other activities may need to use it.
1278        closeCamera();
1279        resetScreenOn();
1280
1281        // Clear UI.
1282        collapseCameraControls();
1283        if (mSharePopup != null) mSharePopup.dismiss();
1284        if (mFaceView != null) mFaceView.clear();
1285
1286        if (mFirstTimeInitialized) {
1287            mOrientationListener.disable();
1288            if (!mIsImageCaptureIntent) {
1289                if (mThumbnail != null) {
1290                    mThumbnail.saveTo(new File(getFilesDir(), Thumbnail.LAST_THUMB_FILENAME));
1291                }
1292            }
1293            hidePostCaptureAlert();
1294        }
1295
1296        if (mDidRegister) {
1297            unregisterReceiver(mReceiver);
1298            mDidRegister = false;
1299        }
1300        mLocationManager.recordLocation(false);
1301        updateExposureOnScreenIndicator(0);
1302
1303        mFocusManager.releaseToneGenerator();
1304
1305        if (mStorageHint != null) {
1306            mStorageHint.cancel();
1307            mStorageHint = null;
1308        }
1309
1310        // If we are in an image capture intent and has taken
1311        // a picture, we just clear it in onPause.
1312        mJpegImageData = null;
1313
1314        // Remove the messages in the event queue.
1315        mHandler.removeMessages(RESTART_PREVIEW);
1316        mHandler.removeMessages(FIRST_TIME_INIT);
1317        mHandler.removeMessages(CHECK_DISPLAY_ROTATION);
1318        mFocusManager.removeMessages();
1319
1320        super.onPause();
1321    }
1322
1323    @Override
1324    protected void onActivityResult(
1325            int requestCode, int resultCode, Intent data) {
1326        switch (requestCode) {
1327            case CROP_MSG: {
1328                Intent intent = new Intent();
1329                if (data != null) {
1330                    Bundle extras = data.getExtras();
1331                    if (extras != null) {
1332                        intent.putExtras(extras);
1333                    }
1334                }
1335                setResultEx(resultCode, intent);
1336                finish();
1337
1338                File path = getFileStreamPath(sTempCropFilename);
1339                path.delete();
1340
1341                break;
1342            }
1343        }
1344    }
1345
1346    private boolean canTakePicture() {
1347        return isCameraIdle() && (mPicturesRemaining > 0);
1348    }
1349
1350    @Override
1351    public void autoFocus() {
1352        mFocusStartTime = System.currentTimeMillis();
1353        mCameraDevice.autoFocus(mAutoFocusCallback);
1354        mCameraState = FOCUSING;
1355        enableCameraControls(false);
1356    }
1357
1358    @Override
1359    public void cancelAutoFocus() {
1360        mCameraDevice.cancelAutoFocus();
1361        mCameraState = IDLE;
1362        enableCameraControls(true);
1363        setCameraParameters(UPDATE_PARAM_PREFERENCE);
1364    }
1365
1366    // Preview area is touched. Handle touch focus.
1367    @Override
1368    public boolean onTouch(View v, MotionEvent e) {
1369        if (mPausing || !mFirstTimeInitialized || mCameraState == SNAPSHOT_IN_PROGRESS) {
1370            return false;
1371        }
1372
1373        // Do not trigger touch focus if popup window is opened.
1374        if (collapseCameraControls()) return false;
1375
1376        // Check if metering area or focus area is supported.
1377        if (!mFocusAreaSupported && !mMeteringAreaSupported) return false;
1378
1379        return mFocusManager.onTouch(e);
1380    }
1381
1382    @Override
1383    public void onBackPressed() {
1384        if (!isCameraIdle()) {
1385            // ignore backs while we're taking a picture
1386            return;
1387        } else if (!collapseCameraControls()) {
1388            super.onBackPressed();
1389        }
1390    }
1391
1392    @Override
1393    public boolean onKeyDown(int keyCode, KeyEvent event) {
1394        switch (keyCode) {
1395            case KeyEvent.KEYCODE_FOCUS:
1396                if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1397                    doFocus(true);
1398                }
1399                return true;
1400            case KeyEvent.KEYCODE_CAMERA:
1401                if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1402                    doSnap();
1403                }
1404                return true;
1405            case KeyEvent.KEYCODE_DPAD_CENTER:
1406                // If we get a dpad center event without any focused view, move
1407                // the focus to the shutter button and press it.
1408                if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1409                    // Start auto-focus immediately to reduce shutter lag. After
1410                    // the shutter button gets the focus, doFocus() will be
1411                    // called again but it is fine.
1412                    if (collapseCameraControls()) return true;
1413                    doFocus(true);
1414                    if (mShutterButton.isInTouchMode()) {
1415                        mShutterButton.requestFocusFromTouch();
1416                    } else {
1417                        mShutterButton.requestFocus();
1418                    }
1419                    mShutterButton.setPressed(true);
1420                }
1421                return true;
1422        }
1423
1424        return super.onKeyDown(keyCode, event);
1425    }
1426
1427    @Override
1428    public boolean onKeyUp(int keyCode, KeyEvent event) {
1429        switch (keyCode) {
1430            case KeyEvent.KEYCODE_FOCUS:
1431                if (mFirstTimeInitialized) {
1432                    doFocus(false);
1433                }
1434                return true;
1435        }
1436        return super.onKeyUp(keyCode, event);
1437    }
1438
1439    private void doSnap() {
1440        if (mPausing || collapseCameraControls()) return;
1441
1442        // Do not take the picture if there is not enough storage.
1443        if (mPicturesRemaining <= 0) {
1444            Log.i(TAG, "Not enough space or storage not ready. remaining=" + mPicturesRemaining);
1445            return;
1446        }
1447
1448        Log.v(TAG, "doSnap: mCameraState=" + mCameraState);
1449        mFocusManager.doSnap();
1450    }
1451
1452    private void doFocus(boolean pressed) {
1453        if (mPausing || collapseCameraControls()) return;
1454
1455        // Do not do focus if there is not enough storage.
1456        if (pressed && !canTakePicture()) return;
1457
1458        mFocusManager.doFocus(pressed);
1459    }
1460
1461    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
1462        // Make sure we have a surface in the holder before proceeding.
1463        if (holder.getSurface() == null) {
1464            Log.d(TAG, "holder.getSurface() == null");
1465            return;
1466        }
1467
1468        Log.v(TAG, "surfaceChanged. w=" + w + ". h=" + h);
1469
1470        // We need to save the holder for later use, even when the mCameraDevice
1471        // is null. This could happen if onResume() is invoked after this
1472        // function.
1473        mSurfaceHolder = holder;
1474
1475        // The mCameraDevice will be null if it fails to connect to the camera
1476        // hardware. In this case we will show a dialog and then finish the
1477        // activity, so it's OK to ignore it.
1478        if (mCameraDevice == null) return;
1479
1480        // Sometimes surfaceChanged is called after onPause or before onResume.
1481        // Ignore it.
1482        if (mPausing || isFinishing()) return;
1483
1484        // Set preview display if the surface is being created. Preview was
1485        // already started. Also restart the preview if display rotation has
1486        // changed. Sometimes this happens when the device is held in portrait
1487        // and camera app is opened. Rotation animation takes some time and
1488        // display rotation in onCreate may not be what we want.
1489        if (mCameraState != PREVIEW_STOPPED
1490                && (Util.getDisplayRotation(this) == mDisplayRotation)
1491                && holder.isCreating()) {
1492            // Set preview display if the surface is being created and preview
1493            // was already started. That means preview display was set to null
1494            // and we need to set it now.
1495            setPreviewDisplay(holder);
1496        } else {
1497            // 1. Restart the preview if the size of surface was changed. The
1498            // framework may not support changing preview display on the fly.
1499            // 2. Start the preview now if surface was destroyed and preview
1500            // stopped.
1501            startPreview();
1502        }
1503
1504        // If first time initialization is not finished, send a message to do
1505        // it later. We want to finish surfaceChanged as soon as possible to let
1506        // user see preview first.
1507        if (!mFirstTimeInitialized) {
1508            mHandler.sendEmptyMessage(FIRST_TIME_INIT);
1509        } else {
1510            initializeSecondTime();
1511        }
1512    }
1513
1514    public void surfaceCreated(SurfaceHolder holder) {
1515    }
1516
1517    public void surfaceDestroyed(SurfaceHolder holder) {
1518        stopPreview();
1519        mSurfaceHolder = null;
1520    }
1521
1522    private void closeCamera() {
1523        if (mCameraDevice != null) {
1524            CameraHolder.instance().release();
1525            mCameraDevice.setZoomChangeListener(null);
1526            mCameraDevice.setFaceDetectionListener(null);
1527            mCameraDevice = null;
1528            mCameraState = PREVIEW_STOPPED;
1529            mFocusManager.onCameraReleased();
1530        }
1531    }
1532
1533    private void setPreviewDisplay(SurfaceHolder holder) {
1534        try {
1535            mCameraDevice.setPreviewDisplay(holder);
1536        } catch (Throwable ex) {
1537            closeCamera();
1538            throw new RuntimeException("setPreviewDisplay failed", ex);
1539        }
1540    }
1541
1542    private void startPreview() {
1543        if (mPausing || isFinishing()) return;
1544
1545        mFocusManager.resetTouchFocus();
1546
1547        mCameraDevice.setErrorCallback(mErrorCallback);
1548
1549        // If we're previewing already, stop the preview first (this will blank
1550        // the screen).
1551        if (mCameraState != PREVIEW_STOPPED) stopPreview();
1552
1553        setPreviewDisplay(mSurfaceHolder);
1554        mDisplayRotation = Util.getDisplayRotation(this);
1555        mDisplayOrientation = Util.getDisplayOrientation(mDisplayRotation, mCameraId);
1556        mCameraDevice.setDisplayOrientation(mDisplayOrientation);
1557        if (mFaceView != null) {
1558            mFaceView.setDisplayOrientation(mDisplayOrientation);
1559        }
1560        setCameraParameters(UPDATE_PARAM_ALL);
1561
1562        try {
1563            Log.v(TAG, "startPreview");
1564            mCameraDevice.startPreview();
1565        } catch (Throwable ex) {
1566            closeCamera();
1567            throw new RuntimeException("startPreview failed", ex);
1568        }
1569
1570        startFaceDetection();
1571        mZoomState = ZOOM_STOPPED;
1572        mCameraState = IDLE;
1573        mFocusManager.onPreviewStarted();
1574    }
1575
1576    private void stopPreview() {
1577        if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1578            Log.v(TAG, "stopPreview");
1579            mCameraDevice.stopPreview();
1580        }
1581        mCameraState = PREVIEW_STOPPED;
1582        mFocusManager.onPreviewStopped();
1583    }
1584
1585    private static boolean isSupported(String value, List<String> supported) {
1586        return supported == null ? false : supported.indexOf(value) >= 0;
1587    }
1588
1589    private void updateCameraParametersInitialize() {
1590        // Reset preview frame rate to the maximum because it may be lowered by
1591        // video camera application.
1592        List<Integer> frameRates = mParameters.getSupportedPreviewFrameRates();
1593        if (frameRates != null) {
1594            Integer max = Collections.max(frameRates);
1595            mParameters.setPreviewFrameRate(max);
1596        }
1597
1598        mParameters.setRecordingHint(false);
1599    }
1600
1601    private void updateCameraParametersZoom() {
1602        // Set zoom.
1603        if (mParameters.isZoomSupported()) {
1604            mParameters.setZoom(mZoomValue);
1605        }
1606    }
1607
1608    private void updateCameraParametersPreference() {
1609        if (mFocusAreaSupported) {
1610            mParameters.setFocusAreas(mFocusManager.getTapArea());
1611        }
1612
1613        if (mMeteringAreaSupported) {
1614            // Use the same area for focus and metering.
1615            mParameters.setMeteringAreas(mFocusManager.getTapArea());
1616        }
1617
1618        // Set picture size.
1619        String pictureSize = mPreferences.getString(
1620                CameraSettings.KEY_PICTURE_SIZE, null);
1621        if (pictureSize == null) {
1622            CameraSettings.initialCameraPictureSize(this, mParameters);
1623        } else {
1624            List<Size> supported = mParameters.getSupportedPictureSizes();
1625            CameraSettings.setCameraPictureSize(
1626                    pictureSize, supported, mParameters);
1627        }
1628
1629        // Set the preview frame aspect ratio according to the picture size.
1630        Size size = mParameters.getPictureSize();
1631
1632        mPreviewFrameLayout = (PreviewFrameLayout) findViewById(R.id.frame_layout);
1633        mPreviewFrameLayout.setAspectRatio((double) size.width / size.height);
1634
1635        // Set a preview size that is closest to the viewfinder height and has
1636        // the right aspect ratio.
1637        List<Size> sizes = mParameters.getSupportedPreviewSizes();
1638        Size optimalSize = Util.getOptimalPreviewSize(this,
1639                sizes, (double) size.width / size.height);
1640        Size original = mParameters.getPreviewSize();
1641        if (!original.equals(optimalSize)) {
1642            mParameters.setPreviewSize(optimalSize.width, optimalSize.height);
1643
1644            // Zoom related settings will be changed for different preview
1645            // sizes, so set and read the parameters to get lastest values
1646            mCameraDevice.setParameters(mParameters);
1647            mParameters = mCameraDevice.getParameters();
1648        }
1649        Log.v(TAG, "Preview size is " + optimalSize.width + "x" + optimalSize.height);
1650
1651        // Since change scene mode may change supported values,
1652        // Set scene mode first,
1653        mSceneMode = mPreferences.getString(
1654                CameraSettings.KEY_SCENE_MODE,
1655                getString(R.string.pref_camera_scenemode_default));
1656        if (isSupported(mSceneMode, mParameters.getSupportedSceneModes())) {
1657            if (!mParameters.getSceneMode().equals(mSceneMode)) {
1658                mParameters.setSceneMode(mSceneMode);
1659                mCameraDevice.setParameters(mParameters);
1660
1661                // Setting scene mode will change the settings of flash mode,
1662                // white balance, and focus mode. Here we read back the
1663                // parameters, so we can know those settings.
1664                mParameters = mCameraDevice.getParameters();
1665            }
1666        } else {
1667            mSceneMode = mParameters.getSceneMode();
1668            if (mSceneMode == null) {
1669                mSceneMode = Parameters.SCENE_MODE_AUTO;
1670            }
1671        }
1672
1673        // Set JPEG quality.
1674        int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
1675                CameraProfile.QUALITY_HIGH);
1676        mParameters.setJpegQuality(jpegQuality);
1677
1678        // For the following settings, we need to check if the settings are
1679        // still supported by latest driver, if not, ignore the settings.
1680
1681        // Set exposure compensation
1682        int value = CameraSettings.readExposure(mPreferences);
1683        int max = mParameters.getMaxExposureCompensation();
1684        int min = mParameters.getMinExposureCompensation();
1685        if (value >= min && value <= max) {
1686            mParameters.setExposureCompensation(value);
1687        } else {
1688            Log.w(TAG, "invalid exposure range: " + value);
1689        }
1690
1691        if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
1692            // Set flash mode.
1693            String flashMode = mPreferences.getString(
1694                    CameraSettings.KEY_FLASH_MODE,
1695                    getString(R.string.pref_camera_flashmode_default));
1696            List<String> supportedFlash = mParameters.getSupportedFlashModes();
1697            if (isSupported(flashMode, supportedFlash)) {
1698                mParameters.setFlashMode(flashMode);
1699            } else {
1700                flashMode = mParameters.getFlashMode();
1701                if (flashMode == null) {
1702                    flashMode = getString(
1703                            R.string.pref_camera_flashmode_no_flash);
1704                }
1705            }
1706
1707            // Set white balance parameter.
1708            String whiteBalance = mPreferences.getString(
1709                    CameraSettings.KEY_WHITE_BALANCE,
1710                    getString(R.string.pref_camera_whitebalance_default));
1711            if (isSupported(whiteBalance,
1712                    mParameters.getSupportedWhiteBalance())) {
1713                mParameters.setWhiteBalance(whiteBalance);
1714            } else {
1715                whiteBalance = mParameters.getWhiteBalance();
1716                if (whiteBalance == null) {
1717                    whiteBalance = Parameters.WHITE_BALANCE_AUTO;
1718                }
1719            }
1720
1721            // Set focus mode.
1722            mFocusManager.overrideFocusMode(null);
1723            mParameters.setFocusMode(mFocusManager.getFocusMode());
1724        } else {
1725            mFocusManager.overrideFocusMode(mParameters.getFocusMode());
1726        }
1727    }
1728
1729    // We separate the parameters into several subsets, so we can update only
1730    // the subsets actually need updating. The PREFERENCE set needs extra
1731    // locking because the preference can be changed from GLThread as well.
1732    private void setCameraParameters(int updateSet) {
1733        mParameters = mCameraDevice.getParameters();
1734
1735        if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
1736            updateCameraParametersInitialize();
1737        }
1738
1739        if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
1740            updateCameraParametersZoom();
1741        }
1742
1743        if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
1744            updateCameraParametersPreference();
1745        }
1746
1747        mCameraDevice.setParameters(mParameters);
1748    }
1749
1750    // If the Camera is idle, update the parameters immediately, otherwise
1751    // accumulate them in mUpdateSet and update later.
1752    private void setCameraParametersWhenIdle(int additionalUpdateSet) {
1753        mUpdateSet |= additionalUpdateSet;
1754        if (mCameraDevice == null) {
1755            // We will update all the parameters when we open the device, so
1756            // we don't need to do anything now.
1757            mUpdateSet = 0;
1758            return;
1759        } else if (isCameraIdle()) {
1760            setCameraParameters(mUpdateSet);
1761            updateSceneModeUI();
1762            mUpdateSet = 0;
1763        } else {
1764            if (!mHandler.hasMessages(SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
1765                mHandler.sendEmptyMessageDelayed(
1766                        SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
1767            }
1768        }
1769    }
1770
1771    private void gotoGallery() {
1772        MenuHelper.gotoCameraImageGallery(this);
1773    }
1774
1775    private boolean isCameraIdle() {
1776        return (mCameraState == IDLE) || (mFocusManager.isFocusCompleted());
1777    }
1778
1779    private boolean isImageCaptureIntent() {
1780        String action = getIntent().getAction();
1781        return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action));
1782    }
1783
1784    private void setupCaptureParams() {
1785        Bundle myExtras = getIntent().getExtras();
1786        if (myExtras != null) {
1787            mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
1788            mCropValue = myExtras.getString("crop");
1789        }
1790    }
1791
1792    private void showPostCaptureAlert() {
1793        if (mIsImageCaptureIntent) {
1794            mShutterButton.setVisibility(View.GONE);
1795            mIndicatorControlContainer.setVisibility(View.GONE);
1796
1797            int[] pickIds = {R.id.btn_retake, R.id.btn_done};
1798            for (int id : pickIds) {
1799                Util.fadeIn(findViewById(id));
1800            }
1801        }
1802    }
1803
1804    private void hidePostCaptureAlert() {
1805        if (mIsImageCaptureIntent) {
1806            enableCameraControls(true);
1807
1808            int[] pickIds = {R.id.btn_retake, R.id.btn_done};
1809            for (int id : pickIds) {
1810                (findViewById(id)).setVisibility(View.GONE);
1811            }
1812
1813            Util.fadeIn(mShutterButton);
1814            Util.fadeIn(mIndicatorControlContainer);
1815        }
1816    }
1817
1818    @Override
1819    public boolean onPrepareOptionsMenu(Menu menu) {
1820        super.onPrepareOptionsMenu(menu);
1821        // Only show the menu when camera is idle.
1822        for (int i = 0; i < menu.size(); i++) {
1823            menu.getItem(i).setVisible(isCameraIdle());
1824        }
1825
1826        return true;
1827    }
1828
1829    @Override
1830    public boolean onCreateOptionsMenu(Menu menu) {
1831        super.onCreateOptionsMenu(menu);
1832
1833        if (mIsImageCaptureIntent) {
1834            // No options menu for attach mode.
1835            return false;
1836        } else {
1837            addBaseMenuItems(menu);
1838        }
1839        return true;
1840    }
1841
1842    private void addBaseMenuItems(Menu menu) {
1843        MenuHelper.addSwitchModeMenuItem(menu, ModePicker.MODE_VIDEO, new Runnable() {
1844            public void run() {
1845                switchToOtherMode(ModePicker.MODE_VIDEO);
1846            }
1847        });
1848        MenuHelper.addSwitchModeMenuItem(menu, ModePicker.MODE_PANORAMA, new Runnable() {
1849            public void run() {
1850                switchToOtherMode(ModePicker.MODE_PANORAMA);
1851            }
1852        });
1853        MenuItem gallery = menu.add(R.string.camera_gallery_photos_text)
1854                .setOnMenuItemClickListener(new OnMenuItemClickListener() {
1855            public boolean onMenuItemClick(MenuItem item) {
1856                gotoGallery();
1857                return true;
1858            }
1859        });
1860        gallery.setIcon(android.R.drawable.ic_menu_gallery);
1861        mGalleryItems.add(gallery);
1862
1863        if (mNumberOfCameras > 1) {
1864            menu.add(R.string.switch_camera_id)
1865                    .setOnMenuItemClickListener(new OnMenuItemClickListener() {
1866                public boolean onMenuItemClick(MenuItem item) {
1867                    CameraSettings.writePreferredCameraId(mPreferences,
1868                            ((mCameraId == mFrontCameraId)
1869                            ? mBackCameraId : mFrontCameraId));
1870                    onSharedPreferenceChanged();
1871                    return true;
1872                }
1873            }).setIcon(android.R.drawable.ic_menu_camera);
1874        }
1875    }
1876
1877    private boolean switchToOtherMode(int mode) {
1878        if (isFinishing() || !isCameraIdle()) return false;
1879        MenuHelper.gotoMode(mode, Camera.this);
1880        mHandler.removeMessages(FIRST_TIME_INIT);
1881        finish();
1882        return true;
1883    }
1884
1885    public boolean onModeChanged(int mode) {
1886        if (mode != ModePicker.MODE_CAMERA) {
1887            return switchToOtherMode(mode);
1888        } else {
1889            return true;
1890        }
1891    }
1892
1893    public void onSharedPreferenceChanged() {
1894        // ignore the events after "onPause()"
1895        if (mPausing) return;
1896
1897        boolean recordLocation = RecordLocationPreference.get(
1898                mPreferences, getContentResolver());
1899        mLocationManager.recordLocation(recordLocation);
1900
1901        int cameraId = CameraSettings.readPreferredCameraId(mPreferences);
1902        if (mCameraId != cameraId) {
1903            // Restart the activity to have a crossfade animation.
1904            // TODO: Use SurfaceTexture to implement a better and faster
1905            // animation.
1906            if (mIsImageCaptureIntent) {
1907                // If the intent is camera capture, stay in camera capture mode.
1908                MenuHelper.gotoCameraMode(this, getIntent());
1909            } else {
1910                MenuHelper.gotoCameraMode(this);
1911            }
1912
1913            finish();
1914        } else {
1915            setCameraParametersWhenIdle(UPDATE_PARAM_PREFERENCE);
1916        }
1917
1918        int exposureValue = CameraSettings.readExposure(mPreferences);
1919        updateExposureOnScreenIndicator(exposureValue);
1920    }
1921
1922    @Override
1923    public void onUserInteraction() {
1924        super.onUserInteraction();
1925        keepScreenOnAwhile();
1926    }
1927
1928    private void resetScreenOn() {
1929        mHandler.removeMessages(CLEAR_SCREEN_DELAY);
1930        getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1931    }
1932
1933    private void keepScreenOnAwhile() {
1934        mHandler.removeMessages(CLEAR_SCREEN_DELAY);
1935        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1936        mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY);
1937    }
1938
1939    public void onRestorePreferencesClicked() {
1940        if (mPausing) return;
1941        Runnable runnable = new Runnable() {
1942            public void run() {
1943                restorePreferences();
1944            }
1945        };
1946        MenuHelper.confirmAction(this,
1947                getString(R.string.confirm_restore_title),
1948                getString(R.string.confirm_restore_message),
1949                runnable);
1950    }
1951
1952    private void restorePreferences() {
1953        // Reset the zoom. Zoom value is not stored in preference.
1954        if (mParameters.isZoomSupported()) {
1955            mZoomValue = 0;
1956            setCameraParametersWhenIdle(UPDATE_PARAM_ZOOM);
1957            mZoomControl.setZoomIndex(0);
1958        }
1959        if (mIndicatorControlContainer != null) {
1960            mIndicatorControlContainer.dismissSettingPopup();
1961            CameraSettings.restorePreferences(Camera.this, mPreferences,
1962                    mParameters);
1963            mIndicatorControlContainer.reloadPreferences();
1964            onSharedPreferenceChanged();
1965        }
1966    }
1967
1968    public void onOverriddenPreferencesClicked() {
1969        if (mPausing) return;
1970        if (mNotSelectableToast == null) {
1971            String str = getResources().getString(R.string.not_selectable_in_scene_mode);
1972            mNotSelectableToast = Toast.makeText(Camera.this, str, Toast.LENGTH_SHORT);
1973        }
1974        mNotSelectableToast.show();
1975    }
1976
1977    private void showSharePopup() {
1978        Uri uri = mThumbnail.getUri();
1979        if (mSharePopup == null || !uri.equals(mSharePopup.getUri())) {
1980            mSharePopup = new SharePopup(this, uri, mThumbnail.getBitmap(),
1981                    mOrientationCompensation, mPreviewFrameLayout);
1982        }
1983        mSharePopup.showAtLocation(mThumbnailView, Gravity.NO_GRAVITY, 0, 0);
1984    }
1985
1986    @Override
1987    public void onFaceDetection(Face[] faces, android.hardware.Camera camera) {
1988        mFaceView.setFaces(faces);
1989    }
1990
1991    private void showTapToFocusToast() {
1992        // Show the toast.
1993        RotateLayout v = (RotateLayout) findViewById(R.id.tap_to_focus_prompt);
1994        v.setOrientation(mOrientationCompensation);
1995        v.startAnimation(AnimationUtils.loadAnimation(this, R.anim.on_screen_hint_enter));
1996        v.setVisibility(View.VISIBLE);
1997        mHandler.sendEmptyMessageDelayed(DISMISS_TAP_TO_FOCUS_TOAST, 5000);
1998        // Clear the preference.
1999        Editor editor = mPreferences.edit();
2000        editor.putBoolean(CameraSettings.KEY_TAP_TO_FOCUS_PROMPT_SHOWN, false);
2001        editor.apply();
2002    }
2003
2004    private void initializeCapabilities() {
2005        mInitialParams = mCameraDevice.getParameters();
2006        mFocusManager.initializeParameters(mInitialParams);
2007        mFocusAreaSupported = (mInitialParams.getMaxNumFocusAreas() > 0
2008                && isSupported(Parameters.FOCUS_MODE_AUTO,
2009                        mInitialParams.getSupportedFocusModes()));
2010        mMeteringAreaSupported = (mInitialParams.getMaxNumMeteringAreas() > 0);
2011    }
2012}
2013