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