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