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