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