PhotoModule.java revision a47397fbc04c90de4b47f98040d558821b369a01
1/*
2 * Copyright (C) 2012 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 android.annotation.TargetApi;
20import android.app.Activity;
21import android.content.ContentResolver;
22import android.content.Context;
23import android.content.Intent;
24import android.graphics.Bitmap;
25import android.graphics.BitmapFactory;
26import android.graphics.SurfaceTexture;
27import android.hardware.Camera.CameraInfo;
28import android.hardware.Camera.Parameters;
29import android.hardware.Sensor;
30import android.hardware.SensorEvent;
31import android.hardware.SensorEventListener;
32import android.hardware.SensorManager;
33import android.location.Location;
34import android.media.AudioManager;
35import android.media.CameraProfile;
36import android.media.SoundPool;
37import android.net.Uri;
38import android.os.AsyncTask;
39import android.os.Build;
40import android.os.Bundle;
41import android.os.Handler;
42import android.os.Looper;
43import android.os.Message;
44import android.os.MessageQueue;
45import android.os.SystemClock;
46import android.provider.MediaStore;
47import android.view.KeyEvent;
48import android.view.OrientationEventListener;
49import android.view.View;
50
51import com.android.camera.PhotoModule.NamedImages.NamedEntity;
52import com.android.camera.app.AppController;
53import com.android.camera.app.CameraAppUI;
54import com.android.camera.app.CameraProvider;
55import com.android.camera.app.LocationManager;
56import com.android.camera.app.MediaSaver;
57import com.android.camera.app.MemoryManager;
58import com.android.camera.app.MemoryManager.MemoryListener;
59import com.android.camera.cameradevice.CameraCapabilities;
60import com.android.camera.cameradevice.CameraManager;
61import com.android.camera.cameradevice.CameraManager.CameraAFCallback;
62import com.android.camera.cameradevice.CameraManager.CameraAFMoveCallback;
63import com.android.camera.cameradevice.CameraManager.CameraPictureCallback;
64import com.android.camera.cameradevice.CameraManager.CameraProxy;
65import com.android.camera.cameradevice.CameraManager.CameraShutterCallback;
66import com.android.camera.debug.Log;
67import com.android.camera.exif.ExifInterface;
68import com.android.camera.exif.ExifTag;
69import com.android.camera.exif.Rational;
70import com.android.camera.hardware.HardwareSpec;
71import com.android.camera.hardware.HardwareSpecImpl;
72import com.android.camera.module.ModuleController;
73import com.android.camera.remote.RemoteCameraModule;
74import com.android.camera.settings.ResolutionUtil;
75import com.android.camera.settings.SettingsManager;
76import com.android.camera.settings.SettingsUtil;
77import com.android.camera.ui.CountDownView;
78import com.android.camera.util.ApiHelper;
79import com.android.camera.util.CameraUtil;
80import com.android.camera.util.GcamHelper;
81import com.android.camera.util.SessionStatsCollector;
82import com.android.camera.util.Size;
83import com.android.camera.util.UsageStatistics;
84import com.android.camera.widget.AspectRatioSelector;
85import com.android.camera2.R;
86import com.google.common.logging.eventprotos;
87
88import java.io.ByteArrayOutputStream;
89import java.io.File;
90import java.io.FileNotFoundException;
91import java.io.FileOutputStream;
92import java.io.IOException;
93import java.io.OutputStream;
94import java.lang.ref.WeakReference;
95import java.text.DecimalFormat;
96import java.util.List;
97import java.util.Vector;
98
99public class PhotoModule
100        extends CameraModule
101        implements PhotoController,
102        ModuleController,
103        MemoryListener,
104        FocusOverlayManager.Listener,
105        SensorEventListener,
106        SettingsManager.OnSettingChangedListener,
107        RemoteCameraModule,
108        CountDownView.OnCountDownStatusListener {
109
110    private static final Log.Tag TAG = new Log.Tag("PhotoModule");
111
112    // We number the request code from 1000 to avoid collision with Gallery.
113    private static final int REQUEST_CROP = 1000;
114
115    // Messages defined for the UI thread handler.
116    private static final int MSG_FIRST_TIME_INIT = 1;
117    private static final int MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE = 2;
118
119    // The subset of parameters we need to update in setCameraParameters().
120    private static final int UPDATE_PARAM_INITIALIZE = 1;
121    private static final int UPDATE_PARAM_ZOOM = 2;
122    private static final int UPDATE_PARAM_PREFERENCE = 4;
123    private static final int UPDATE_PARAM_ALL = -1;
124
125    // This is the delay before we execute onResume tasks when coming
126    // from the lock screen, to allow time for onPause to execute.
127    private static final int ON_RESUME_TASKS_DELAY_MSEC = 20;
128
129    private static final String DEBUG_IMAGE_PREFIX = "DEBUG_";
130
131    private static DecimalFormat sMegaPixelFormat = new DecimalFormat("##0.0");
132
133    private CameraActivity mActivity;
134    private CameraProxy mCameraDevice;
135    private int mCameraId;
136    private CameraCapabilities mCameraCapabilities;
137    private Parameters mParameters;
138    private boolean mPaused;
139    private boolean mShouldSetPreviewCallbacks = ApiHelper.SHOULD_HARD_RESET_PREVIEW_CALLBACK;
140
141    private PhotoUI mUI;
142
143    // The activity is going to switch to the specified camera id. This is
144    // needed because texture copy is done in GL thread. -1 means camera is not
145    // switching.
146    protected int mPendingSwitchCameraId = -1;
147
148    // When setCameraParametersWhenIdle() is called, we accumulate the subsets
149    // needed to be updated in mUpdateSet.
150    private int mUpdateSet;
151
152    private static final int SCREEN_DELAY = 2 * 60 * 1000;
153
154    private int mZoomValue; // The current zoom value.
155    private int mTimerDuration;
156
157    private Parameters mInitialParams;
158    private boolean mFocusAreaSupported;
159    private boolean mMeteringAreaSupported;
160    private boolean mAeLockSupported;
161    private boolean mAwbLockSupported;
162    private boolean mContinuousFocusSupported;
163
164    // The degrees of the device rotated clockwise from its natural orientation.
165    private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
166
167    private static final String sTempCropFilename = "crop-temp";
168
169    private boolean mFaceDetectionStarted = false;
170
171    // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
172    private String mCropValue;
173    private Uri mSaveUri;
174
175    private Uri mDebugUri;
176
177    // We use a queue to generated names of the images to be used later
178    // when the image is ready to be saved.
179    private NamedImages mNamedImages;
180
181    private final Runnable mDoSnapRunnable = new Runnable() {
182        @Override
183        public void run() {
184            onShutterButtonClick();
185        }
186    };
187
188    /**
189     * An unpublished intent flag requesting to return as soon as capturing is
190     * completed. TODO: consider publishing by moving into MediaStore.
191     */
192    private static final String EXTRA_QUICK_CAPTURE =
193            "android.intent.extra.quickCapture";
194
195    // The display rotation in degrees. This is only valid when mCameraState is
196    // not PREVIEW_STOPPED.
197    private int mDisplayRotation;
198    // The value for android.hardware.Camera.setDisplayOrientation.
199    private int mCameraDisplayOrientation;
200    // The value for UI components like indicators.
201    private int mDisplayOrientation;
202    // The value for android.hardware.Camera.Parameters.setRotation.
203    private int mJpegRotation;
204    // Indicates whether we are using front camera
205    private boolean mMirror;
206    private boolean mFirstTimeInitialized;
207    private boolean mIsImageCaptureIntent;
208
209    private int mCameraState = PREVIEW_STOPPED;
210    private boolean mSnapshotOnIdle = false;
211
212    private ContentResolver mContentResolver;
213
214    private LocationManager mLocationManager;
215    private AppController mAppController;
216
217    private final PostViewPictureCallback mPostViewPictureCallback =
218            new PostViewPictureCallback();
219    private final RawPictureCallback mRawPictureCallback =
220            new RawPictureCallback();
221    private final AutoFocusCallback mAutoFocusCallback =
222            new AutoFocusCallback();
223    private final Object mAutoFocusMoveCallback =
224            ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
225                    ? new AutoFocusMoveCallback()
226                    : null;
227
228    private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
229
230    private long mFocusStartTime;
231    private long mShutterCallbackTime;
232    private long mPostViewPictureCallbackTime;
233    private long mRawPictureCallbackTime;
234    private long mJpegPictureCallbackTime;
235    private long mOnResumeTime;
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    public long mCaptureStartTime;
245
246    // This handles everything about focus.
247    private FocusOverlayManager mFocusManager;
248
249    private final int mGcamModeIndex;
250    private final CountdownSoundPlayer mCountdownSoundPlayer = new CountdownSoundPlayer();
251
252    private String mSceneMode;
253
254    private final Handler mHandler = new MainHandler(this);
255
256    private boolean mQuickCapture;
257    private SensorManager mSensorManager;
258    private final float[] mGData = new float[3];
259    private final float[] mMData = new float[3];
260    private final float[] mR = new float[16];
261    private int mHeading = -1;
262
263    /** True if all the parameters needed to start preview is ready. */
264    private boolean mCameraPreviewParamsReady = false;
265
266    private final MediaSaver.OnMediaSavedListener mOnMediaSavedListener =
267            new MediaSaver.OnMediaSavedListener() {
268                @Override
269                public void onMediaSaved(Uri uri) {
270                    if (uri != null) {
271                        mActivity.notifyNewMedia(uri);
272                    }
273                }
274            };
275    private boolean mShouldResizeTo16x9 = false;
276
277    private final Runnable mResumeTaskRunnable = new Runnable() {
278        @Override
279        public void run() {
280            onResumeTasks();
281        }
282    };
283
284    /**
285     * This callback gets called when user select whether or not to
286     * turn on geo-tagging.
287     */
288    public interface LocationDialogCallback {
289        /**
290         * Gets called after user selected/unselected geo-tagging feature.
291         *
292         * @param selected whether or not geo-tagging feature is selected
293         */
294        public void onLocationTaggingSelected(boolean selected);
295    }
296
297    /**
298     * This callback defines the text that is shown in the aspect ratio selection
299     * dialog, provides the current aspect ratio, and gets notified when user changes
300     * aspect ratio selection in the dialog.
301     */
302    public interface AspectRatioDialogCallback {
303        /**
304         * Returns current aspect ratio that is being used to set as default.
305         */
306        public AspectRatioSelector.AspectRatio getCurrentAspectRatio();
307
308        /**
309         * Gets notified when user has made the aspect ratio selection.
310         *
311         * @param newAspectRatio aspect ratio that user has selected
312         * @param dialogHandlingFinishedRunnable runnable to run when the operations
313         *                                       needed to handle changes from dialog
314         *                                       are finished.
315         */
316        public void onAspectRatioSelected(AspectRatioSelector.AspectRatio newAspectRatio,
317                Runnable dialogHandlingFinishedRunnable);
318    }
319
320    private void checkDisplayRotation() {
321        // Set the display orientation if display rotation has changed.
322        // Sometimes this happens when the device is held upside
323        // down and camera app is opened. Rotation animation will
324        // take some time and the rotation value we have got may be
325        // wrong. Framework does not have a callback for this now.
326        if (CameraUtil.getDisplayRotation(mActivity) != mDisplayRotation) {
327            setDisplayOrientation();
328        }
329        if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
330            mHandler.postDelayed(new Runnable() {
331                @Override
332                public void run() {
333                    checkDisplayRotation();
334                }
335            }, 100);
336        }
337    }
338
339    /**
340     * This Handler is used to post message back onto the main thread of the
341     * application
342     */
343    private static class MainHandler extends Handler {
344        private final WeakReference<PhotoModule> mModule;
345
346        public MainHandler(PhotoModule module) {
347            super(Looper.getMainLooper());
348            mModule = new WeakReference<PhotoModule>(module);
349        }
350
351        @Override
352        public void handleMessage(Message msg) {
353            PhotoModule module = mModule.get();
354            if (module == null) {
355                return;
356            }
357            switch (msg.what) {
358                case MSG_FIRST_TIME_INIT: {
359                    module.initializeFirstTime();
360                    break;
361                }
362
363                case MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE: {
364                    module.setCameraParametersWhenIdle(0);
365                    break;
366                }
367            }
368        }
369    }
370
371    private void switchToGcamCapture() {
372        if (mActivity != null && mGcamModeIndex != 0) {
373            SettingsManager settingsManager = mActivity.getSettingsManager();
374            settingsManager.set(SettingsManager.SETTING_CAMERA_HDR_PLUS,
375                SettingsManager.VALUE_ON);
376
377            // Disable the HDR+ button to prevent callbacks from being
378            // queued before the correct callback is attached to the button
379            // in the new module.  The new module will set the enabled/disabled
380            // of this button when the module's preferred camera becomes available.
381            ButtonManager buttonManager = mActivity.getButtonManager();
382            buttonManager.disableButton(ButtonManager.BUTTON_HDR_PLUS);
383
384            // Do not post this to avoid this module switch getting interleaved with
385            // other button callbacks.
386            mActivity.onModeSelected(mGcamModeIndex);
387        }
388    }
389
390    /**
391     * Constructs a new photo module.
392     */
393    public PhotoModule(AppController app) {
394        super(app);
395        mGcamModeIndex = app.getAndroidContext().getResources()
396                .getInteger(R.integer.camera_mode_gcam);
397    }
398
399    @Override
400    public void init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent) {
401        mActivity = activity;
402        // TODO: Need to look at the controller interface to see if we can get
403        // rid of passing in the activity directly.
404        mAppController = mActivity;
405
406        mUI = new PhotoUI(mActivity, this, mActivity.getModuleLayoutRoot());
407        mActivity.setPreviewStatusListener(mUI);
408
409        SettingsManager settingsManager = mActivity.getSettingsManager();
410        mCameraId = Integer.parseInt(settingsManager.get(SettingsManager.SETTING_CAMERA_ID));
411
412        // TODO: Move this to SettingsManager as a part of upgrade procedure.
413        if (!settingsManager.getBoolean(SettingsManager.SETTING_USER_SELECTED_ASPECT_RATIO)) {
414            // Switch to back camera to set aspect ratio.
415            mCameraId = Integer.parseInt(settingsManager
416                    .getDefaultCameraIdSetting(activity).getDefault());
417        }
418
419        mContentResolver = mActivity.getContentResolver();
420
421        // Surface texture is from camera screen nail and startPreview needs it.
422        // This must be done before startPreview.
423        mIsImageCaptureIntent = isImageCaptureIntent();
424
425        mActivity.getCameraProvider().requestCamera(mCameraId);
426
427        mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
428        mLocationManager = mActivity.getLocationManager();
429        mSensorManager = (SensorManager) (mActivity.getSystemService(Context.SENSOR_SERVICE));
430        mUI.setCountdownFinishedListener(this);
431
432        // TODO: Make this a part of app controller API.
433        View cancelButton = mActivity.findViewById(R.id.shutter_cancel_button);
434        cancelButton.setOnClickListener(new View.OnClickListener() {
435            @Override
436            public void onClick(View view) {
437                cancelCountDown();
438            }
439        });
440    }
441
442    private void cancelCountDown() {
443        if (mUI.isCountingDown()) {
444            // Cancel on-going countdown.
445            mUI.cancelCountDown();
446        }
447        mAppController.getCameraAppUI().transitionToCapture();
448        mAppController.getCameraAppUI().showModeOptions();
449    }
450
451    @Override
452    public boolean isUsingBottomBar() {
453        return true;
454    }
455
456    private void initializeControlByIntent() {
457        if (mIsImageCaptureIntent) {
458            mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
459            setupCaptureParams();
460        }
461    }
462
463    private void onPreviewStarted() {
464        mAppController.onPreviewStarted();
465        setCameraState(IDLE);
466        startFaceDetection();
467        settingsFirstRun();
468    }
469
470    /**
471     * Prompt the user to pick to record location and choose aspect ratio for the
472     * very first run of camera only.
473     */
474    private void settingsFirstRun() {
475        final SettingsManager settingsManager = mActivity.getSettingsManager();
476
477        if (mActivity.isSecureCamera() || isImageCaptureIntent()) {
478            return;
479        }
480
481        boolean locationPrompt = !settingsManager.isSet(SettingsManager.SETTING_RECORD_LOCATION);
482        boolean aspectRatioPrompt = !settingsManager.getBoolean(
483                SettingsManager.SETTING_USER_SELECTED_ASPECT_RATIO);
484        if (!locationPrompt && !aspectRatioPrompt) {
485            return;
486        }
487
488        // Check if the back camera exists
489        int backCameraId = mAppController.getCameraProvider().getFirstBackCameraId();
490        if (backCameraId == -1) {
491            // If there is no back camera, do not show the prompt.
492            return;
493        }
494
495        if (locationPrompt) {
496            // Show both location and aspect ratio selection dialog.
497            mUI.showLocationAndAspectRatioDialog(new LocationDialogCallback(){
498                @Override
499                public void onLocationTaggingSelected(boolean selected) {
500                    settingsManager.setLocation(selected, mActivity.getLocationManager());
501                }
502            }, createAspectRatioDialogCallback());
503        } else {
504            // App upgrade. Only show aspect ratio selection.
505            mUI.showAspectRatioDialog(createAspectRatioDialogCallback());
506        }
507    }
508
509    private AspectRatioDialogCallback createAspectRatioDialogCallback() {
510        Size currentSize = new Size(mParameters.getPictureSize());
511        float aspectRatio = (float) currentSize.width() / (float) currentSize.height();
512        if (aspectRatio < 1f) {
513            aspectRatio = 1 / aspectRatio;
514        }
515        final AspectRatioSelector.AspectRatio currentAspectRatio;
516        if (Math.abs(aspectRatio - 4f / 3f) <= 0.1f) {
517            currentAspectRatio = AspectRatioSelector.AspectRatio.ASPECT_RATIO_4x3;
518        } else if (Math.abs(aspectRatio - 16f / 9f) <= 0.1f) {
519            currentAspectRatio = AspectRatioSelector.AspectRatio.ASPECT_RATIO_16x9;
520        } else {
521            // TODO: Log error and not show dialog.
522            return null;
523        }
524
525        List<Size> sizes = Size.buildListFromCameraSizes(mParameters.getSupportedPictureSizes());
526        List<Size> pictureSizes = ResolutionUtil
527                .getDisplayableSizesFromSupported(sizes, true);
528
529        // This logic below finds the largest resolution for each aspect ratio.
530        // TODO: Move this somewhere that can be shared with SettingsActivity
531        int aspectRatio4x3Resolution = 0;
532        int aspectRatio16x9Resolution = 0;
533        Size largestSize4x3 = new Size(0, 0);
534        Size largestSize16x9 = new Size(0, 0);
535        for (Size size : pictureSizes) {
536            float pictureAspectRatio = (float) size.width() / (float) size.height();
537            pictureAspectRatio = pictureAspectRatio < 1 ?
538                    1f / pictureAspectRatio : pictureAspectRatio;
539            int resolution = size.width() * size.height();
540            if (Math.abs(pictureAspectRatio - 4f / 3f) < 0.1f) {
541                if (resolution > aspectRatio4x3Resolution) {
542                    aspectRatio4x3Resolution = resolution;
543                    largestSize4x3 = size;
544                }
545            } else if (Math.abs(pictureAspectRatio - 16f / 9f) < 0.1f) {
546                if (resolution > aspectRatio16x9Resolution) {
547                    aspectRatio16x9Resolution = resolution;
548                    largestSize16x9 = size;
549                }
550            }
551        }
552
553        // Use the largest 4x3 and 16x9 sizes as candidates for picture size selection.
554        final Size size4x3ToSelect = largestSize4x3;
555        final Size size16x9ToSelect = largestSize16x9;
556
557        AspectRatioDialogCallback callback = new AspectRatioDialogCallback() {
558
559            @Override
560            public AspectRatioSelector.AspectRatio getCurrentAspectRatio() {
561                return currentAspectRatio;
562            }
563
564            @Override
565            public void onAspectRatioSelected(AspectRatioSelector.AspectRatio newAspectRatio,
566                    Runnable dialogHandlingFinishedRunnable) {
567                if (newAspectRatio == AspectRatioSelector.AspectRatio.ASPECT_RATIO_4x3) {
568                    String largestSize4x3Text = SettingsUtil.sizeToSetting(size4x3ToSelect);
569                    mActivity.getSettingsManager().set(SettingsManager.SETTING_PICTURE_SIZE_BACK,
570                            largestSize4x3Text);
571                } else if (newAspectRatio == AspectRatioSelector.AspectRatio.ASPECT_RATIO_16x9) {
572                    String largestSize16x9Text = SettingsUtil.sizeToSetting(size16x9ToSelect);
573                    mActivity.getSettingsManager().set(SettingsManager.SETTING_PICTURE_SIZE_BACK,
574                            largestSize16x9Text);
575                }
576                mActivity.getSettingsManager().setBoolean(
577                        SettingsManager.SETTING_USER_SELECTED_ASPECT_RATIO, true);
578                if (newAspectRatio != currentAspectRatio) {
579                    stopPreview();
580                    startPreview();
581                    mUI.setRunnableForNextFrame(dialogHandlingFinishedRunnable);
582                } else {
583                    mHandler.post(dialogHandlingFinishedRunnable);
584                }
585            }
586        };
587        return callback;
588    }
589
590    @Override
591    public void onPreviewUIReady() {
592        startPreview();
593    }
594
595    @Override
596    public void onPreviewUIDestroyed() {
597        if (mCameraDevice == null) {
598            return;
599        }
600        mCameraDevice.setPreviewTexture(null);
601        stopPreview();
602    }
603
604    @Override
605    public void startPreCaptureAnimation() {
606        mAppController.startPreCaptureAnimation();
607    }
608
609    private void onCameraOpened() {
610        openCameraCommon();
611        initializeControlByIntent();
612    }
613
614    private void switchCamera() {
615        if (mPaused) {
616            return;
617        }
618        cancelCountDown();
619        SettingsManager settingsManager = mActivity.getSettingsManager();
620
621        Log.i(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
622        closeCamera();
623        mCameraId = mPendingSwitchCameraId;
624        settingsManager.set(SettingsManager.SETTING_CAMERA_ID, "" + mCameraId);
625        mActivity.getCameraProvider().requestCamera(mCameraId);
626        mUI.clearFaces();
627        if (mFocusManager != null) {
628            mFocusManager.removeMessages();
629        }
630
631        mMirror = isCameraFrontFacing();
632        mFocusManager.setMirror(mMirror);
633        // Start switch camera animation. Post a message because
634        // onFrameAvailable from the old camera may already exist.
635    }
636
637    private final ButtonManager.ButtonCallback mCameraCallback =
638            new ButtonManager.ButtonCallback() {
639                @Override
640                public void onStateChanged(int state) {
641                    // At the time this callback is fired, the camera id
642                    // has be set to the desired camera.
643
644                    if (mPaused || mAppController.getCameraProvider().waitingForCamera()) {
645                        return;
646                    }
647                    // If switching to back camera, and HDR+ is still on,
648                    // switch back to gcam, otherwise handle callback normally.
649                    SettingsManager settingsManager = mActivity.getSettingsManager();
650                    if (settingsManager.isCameraBackFacing()) {
651                        if (settingsManager.requestsReturnToHdrPlus()) {
652                            switchToGcamCapture();
653                            return;
654                        }
655                    }
656
657                    mPendingSwitchCameraId = state;
658
659                    Log.d(TAG, "Start to switch camera. cameraId=" + state);
660                    // We need to keep a preview frame for the animation before
661                    // releasing the camera. This will trigger
662                    // onPreviewTextureCopied.
663                    // TODO: Need to animate the camera switch
664                    switchCamera();
665                }
666            };
667
668    private final ButtonManager.ButtonCallback mHdrPlusCallback =
669            new ButtonManager.ButtonCallback() {
670                @Override
671                public void onStateChanged(int state) {
672                    SettingsManager settingsManager = mActivity.getSettingsManager();
673                    if (GcamHelper.hasGcamCapture()) {
674                        // Set the camera setting to default backfacing.
675                        settingsManager.setDefault(SettingsManager.SETTING_CAMERA_ID);
676                        switchToGcamCapture();
677                    } else {
678                        if (settingsManager.isHdrOn()) {
679                            settingsManager.set(SettingsManager.SETTING_SCENE_MODE,
680                                    CameraUtil.SCENE_MODE_HDR);
681                        } else {
682                            settingsManager.set(SettingsManager.SETTING_SCENE_MODE,
683                                    Parameters.SCENE_MODE_AUTO);
684                        }
685                        updateParametersSceneMode();
686                        mCameraDevice.setParameters(mParameters);
687                        updateSceneMode();
688                    }
689                }
690            };
691
692    private final View.OnClickListener mCancelCallback = new View.OnClickListener() {
693        @Override
694        public void onClick(View v) {
695            onCaptureCancelled();
696        }
697    };
698
699    private final View.OnClickListener mDoneCallback = new View.OnClickListener() {
700        @Override
701        public void onClick(View v) {
702            onCaptureDone();
703        }
704    };
705
706    private final View.OnClickListener mRetakeCallback = new View.OnClickListener() {
707        @Override
708        public void onClick(View v) {
709            mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
710            onCaptureRetake();
711        }
712    };
713
714    @Override
715    public void hardResetSettings(SettingsManager settingsManager) {
716        // PhotoModule should hard reset HDR+ to off.
717        settingsManager.set(SettingsManager.SETTING_CAMERA_HDR_PLUS, SettingsManager.VALUE_OFF);
718    }
719
720    @Override
721    public HardwareSpec getHardwareSpec() {
722        return (mParameters != null ? new HardwareSpecImpl(mParameters) : null);
723    }
724
725    @Override
726    public CameraAppUI.BottomBarUISpec getBottomBarSpec() {
727        CameraAppUI.BottomBarUISpec bottomBarSpec = new CameraAppUI.BottomBarUISpec();
728
729        bottomBarSpec.enableCamera = true;
730        bottomBarSpec.cameraCallback = mCameraCallback;
731        bottomBarSpec.enableFlash = true;
732        bottomBarSpec.enableHdr = true;
733        bottomBarSpec.hdrCallback = mHdrPlusCallback;
734        bottomBarSpec.enableGridLines = true;
735        if (mCameraCapabilities != null) {
736            bottomBarSpec.enableExposureCompensation = true;
737            bottomBarSpec.exposureCompensationSetCallback =
738                new CameraAppUI.BottomBarUISpec.ExposureCompensationSetCallback() {
739                @Override
740                public void setExposure(int value) {
741                    setExposureCompensation(value);
742                }
743            };
744            bottomBarSpec.minExposureCompensation =
745                mCameraCapabilities.getMinExposureCompensation();
746            bottomBarSpec.maxExposureCompensation =
747                mCameraCapabilities.getMaxExposureCompensation();
748            bottomBarSpec.exposureCompensationStep =
749                mCameraCapabilities.getExposureCompensationStep();
750        }
751
752        bottomBarSpec.enableSelfTimer = true;
753        bottomBarSpec.showSelfTimer = true;
754
755        if (isImageCaptureIntent()) {
756            bottomBarSpec.showCancel = true;
757            bottomBarSpec.cancelCallback = mCancelCallback;
758            bottomBarSpec.showDone = true;
759            bottomBarSpec.doneCallback = mDoneCallback;
760            bottomBarSpec.showRetake = true;
761            bottomBarSpec.retakeCallback = mRetakeCallback;
762        }
763
764        return bottomBarSpec;
765    }
766
767    // either open a new camera or switch cameras
768    private void openCameraCommon() {
769        mUI.onCameraOpened(mParameters);
770        if (mIsImageCaptureIntent) {
771            // Set hdr plus to default: off.
772            SettingsManager settingsManager = mActivity.getSettingsManager();
773            settingsManager.setDefault(SettingsManager.SETTING_CAMERA_HDR_PLUS);
774        }
775        updateSceneMode();
776    }
777
778    @Override
779    public void updatePreviewAspectRatio(float aspectRatio) {
780        mAppController.updatePreviewAspectRatio(aspectRatio);
781    }
782
783    private void resetExposureCompensation() {
784        SettingsManager settingsManager = mActivity.getSettingsManager();
785        if (settingsManager == null) {
786            Log.e(TAG, "Settings manager is null!");
787            return;
788        }
789        settingsManager.setDefault(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE);
790    }
791
792    // Snapshots can only be taken after this is called. It should be called
793    // once only. We could have done these things in onCreate() but we want to
794    // make preview screen appear as soon as possible.
795    private void initializeFirstTime() {
796        if (mFirstTimeInitialized || mPaused) {
797            return;
798        }
799
800        mUI.initializeFirstTime();
801
802        // We set the listener only when both service and shutterbutton
803        // are initialized.
804        getServices().getMemoryManager().addListener(this);
805
806        mNamedImages = new NamedImages();
807
808        mFirstTimeInitialized = true;
809        addIdleHandler();
810
811        mActivity.updateStorageSpaceAndHint(null);
812    }
813
814    // If the activity is paused and resumed, this method will be called in
815    // onResume.
816    private void initializeSecondTime() {
817        getServices().getMemoryManager().addListener(this);
818        mNamedImages = new NamedImages();
819        mUI.initializeSecondTime(mParameters);
820    }
821
822    private void addIdleHandler() {
823        MessageQueue queue = Looper.myQueue();
824        queue.addIdleHandler(new MessageQueue.IdleHandler() {
825            @Override
826            public boolean queueIdle() {
827                Storage.ensureOSXCompatible();
828                return false;
829            }
830        });
831    }
832
833    @Override
834    public void startFaceDetection() {
835        if (mFaceDetectionStarted) {
836            return;
837        }
838        if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
839            mFaceDetectionStarted = true;
840            mUI.onStartFaceDetection(mDisplayOrientation, isCameraFrontFacing());
841            mCameraDevice.setFaceDetectionCallback(mHandler, mUI);
842            mCameraDevice.startFaceDetection();
843            SessionStatsCollector.instance().faceScanActive(true);
844        }
845    }
846
847    @Override
848    public void stopFaceDetection() {
849        if (!mFaceDetectionStarted) {
850            return;
851        }
852        if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
853            mFaceDetectionStarted = false;
854            mCameraDevice.setFaceDetectionCallback(null, null);
855            mCameraDevice.stopFaceDetection();
856            mUI.clearFaces();
857            SessionStatsCollector.instance().faceScanActive(false);
858        }
859    }
860
861    private final class ShutterCallback
862            implements CameraShutterCallback {
863
864        private final boolean mNeedsAnimation;
865
866        public ShutterCallback(boolean needsAnimation) {
867            mNeedsAnimation = needsAnimation;
868        }
869
870        @Override
871        public void onShutter(CameraProxy camera) {
872            mShutterCallbackTime = System.currentTimeMillis();
873            mShutterLag = mShutterCallbackTime - mCaptureStartTime;
874            Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
875            if (mNeedsAnimation) {
876                mActivity.runOnUiThread(new Runnable() {
877                    @Override
878                    public void run() {
879                        animateAfterShutter();
880                    }
881                });
882            }
883        }
884    }
885
886    private final class PostViewPictureCallback
887            implements CameraPictureCallback {
888        @Override
889        public void onPictureTaken(byte[] data, CameraProxy camera) {
890            mPostViewPictureCallbackTime = System.currentTimeMillis();
891            Log.v(TAG, "mShutterToPostViewCallbackTime = "
892                    + (mPostViewPictureCallbackTime - mShutterCallbackTime)
893                    + "ms");
894        }
895    }
896
897    private final class RawPictureCallback
898            implements CameraPictureCallback {
899        @Override
900        public void onPictureTaken(byte[] rawData, CameraProxy camera) {
901            mRawPictureCallbackTime = System.currentTimeMillis();
902            Log.v(TAG, "mShutterToRawCallbackTime = "
903                    + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
904        }
905    }
906
907    private static class ResizeBundle {
908        byte[] jpegData;
909        float targetAspectRatio;
910        ExifInterface exif;
911    }
912
913    /**
914     * @return Cropped image if the target aspect ratio is larger than the jpeg
915     *         aspect ratio on the long axis. The original jpeg otherwise.
916     */
917    private ResizeBundle cropJpegDataToAspectRatio(ResizeBundle dataBundle) {
918
919        final byte[] jpegData = dataBundle.jpegData;
920        final ExifInterface exif = dataBundle.exif;
921        float targetAspectRatio = dataBundle.targetAspectRatio;
922
923        Bitmap original = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length);
924        int originalWidth = original.getWidth();
925        int originalHeight = original.getHeight();
926        int newWidth;
927        int newHeight;
928
929        if (originalWidth > originalHeight) {
930            newHeight = (int) (originalWidth / targetAspectRatio);
931            newWidth = originalWidth;
932        } else {
933            newWidth = (int) (originalHeight / targetAspectRatio);
934            newHeight = originalHeight;
935        }
936        int xOffset = (originalWidth - newWidth)/2;
937        int yOffset = (originalHeight - newHeight)/2;
938
939        if (xOffset < 0 || yOffset < 0) {
940            return dataBundle;
941        }
942
943        Bitmap resized = Bitmap.createBitmap(original,xOffset,yOffset,newWidth, newHeight);
944        exif.setTagValue(ExifInterface.TAG_PIXEL_X_DIMENSION, new Integer(newWidth));
945        exif.setTagValue(ExifInterface.TAG_PIXEL_Y_DIMENSION, new Integer(newHeight));
946
947        ByteArrayOutputStream stream = new ByteArrayOutputStream();
948
949        resized.compress(Bitmap.CompressFormat.JPEG, 90, stream);
950        dataBundle.jpegData = stream.toByteArray();
951        return dataBundle;
952    }
953
954    private final class JpegPictureCallback
955            implements CameraPictureCallback {
956        Location mLocation;
957
958        public JpegPictureCallback(Location loc) {
959            mLocation = loc;
960        }
961
962        @Override
963        public void onPictureTaken(final byte[] originalJpegData, final CameraProxy camera) {
964            mAppController.setShutterEnabled(true);
965            if (mPaused) {
966                return;
967            }
968            if (mIsImageCaptureIntent) {
969                stopPreview();
970            }
971            if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {
972                mUI.setSwipingEnabled(true);
973            }
974
975            mJpegPictureCallbackTime = System.currentTimeMillis();
976            // If postview callback has arrived, the captured image is displayed
977            // in postview callback. If not, the captured image is displayed in
978            // raw picture callback.
979            if (mPostViewPictureCallbackTime != 0) {
980                mShutterToPictureDisplayedTime =
981                        mPostViewPictureCallbackTime - mShutterCallbackTime;
982                mPictureDisplayedToJpegCallbackTime =
983                        mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
984            } else {
985                mShutterToPictureDisplayedTime =
986                        mRawPictureCallbackTime - mShutterCallbackTime;
987                mPictureDisplayedToJpegCallbackTime =
988                        mJpegPictureCallbackTime - mRawPictureCallbackTime;
989            }
990            Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
991                    + mPictureDisplayedToJpegCallbackTime + "ms");
992
993            mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
994            if (!mIsImageCaptureIntent) {
995                setupPreview();
996            }
997
998            long now = System.currentTimeMillis();
999            mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
1000            Log.v(TAG, "mJpegCallbackFinishTime = " + mJpegCallbackFinishTime + "ms");
1001            mJpegPictureCallbackTime = 0;
1002
1003            final ExifInterface exif = Exif.getExif(originalJpegData);
1004
1005            if (mShouldResizeTo16x9) {
1006                final ResizeBundle dataBundle = new ResizeBundle();
1007                dataBundle.jpegData = originalJpegData;
1008                dataBundle.targetAspectRatio = ResolutionUtil.NEXUS_5_LARGE_16_BY_9_ASPECT_RATIO;
1009                dataBundle.exif = exif;
1010                new AsyncTask<ResizeBundle, Void, ResizeBundle>() {
1011
1012                    @Override
1013                    protected ResizeBundle doInBackground(ResizeBundle... resizeBundles) {
1014                        return cropJpegDataToAspectRatio(resizeBundles[0]);
1015                    }
1016
1017                    @Override
1018                    protected void onPostExecute(ResizeBundle result) {
1019                        saveFinalPhoto(result.jpegData, result.exif, camera);
1020                    }
1021                }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, dataBundle);
1022
1023            } else {
1024                saveFinalPhoto(originalJpegData, exif, camera);
1025            }
1026        }
1027
1028        void saveFinalPhoto(final byte[] jpegData, final ExifInterface exif, CameraProxy camera) {
1029
1030            int orientation = Exif.getOrientation(exif);
1031
1032            float zoomValue = 0f;
1033            if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
1034                int zoomIndex = mParameters.getZoom();
1035                List<Integer> zoomRatios = mParameters.getZoomRatios();
1036                if (zoomRatios != null && zoomIndex < zoomRatios.size()) {
1037                    zoomValue = 0.01f * zoomRatios.get(zoomIndex);
1038                }
1039            }
1040
1041            boolean hdrOn = CameraUtil.SCENE_MODE_HDR.equals(mSceneMode);
1042            String flashSetting =
1043                    mActivity.getSettingsManager().get(SettingsManager.SETTING_FLASH_MODE);
1044            boolean gridLinesOn = mActivity.getSettingsManager().areGridLinesOn();
1045            UsageStatistics.instance().photoCaptureDoneEvent(
1046                    eventprotos.NavigationChange.Mode.PHOTO_CAPTURE,
1047                    mNamedImages.mQueue.lastElement().title + ".jpg", exif,
1048                    isCameraFrontFacing(), hdrOn, zoomValue, flashSetting, gridLinesOn,
1049                    (float) mTimerDuration);
1050
1051            if (!mIsImageCaptureIntent) {
1052                // Calculate the width and the height of the jpeg.
1053                Integer exifWidth = exif.getTagIntValue(ExifInterface.TAG_PIXEL_X_DIMENSION);
1054                Integer exifHeight = exif.getTagIntValue(ExifInterface.TAG_PIXEL_Y_DIMENSION);
1055                int width, height;
1056                if (mShouldResizeTo16x9 && exifWidth != null && exifHeight != null) {
1057                    width = exifWidth;
1058                    height = exifHeight;
1059                } else {
1060                    Size s;
1061                    s = new Size(mParameters.getPictureSize());
1062                    if ((mJpegRotation + orientation) % 180 == 0) {
1063                        width = s.width();
1064                        height = s.height();
1065                    } else {
1066                        width = s.height();
1067                        height = s.width();
1068                    }
1069                }
1070                NamedEntity name = mNamedImages.getNextNameEntity();
1071                String title = (name == null) ? null : name.title;
1072                long date = (name == null) ? -1 : name.date;
1073
1074                // Handle debug mode outputs
1075                if (mDebugUri != null) {
1076                    // If using a debug uri, save jpeg there.
1077                    saveToDebugUri(jpegData);
1078
1079                    // Adjust the title of the debug image shown in mediastore.
1080                    if (title != null) {
1081                        title = DEBUG_IMAGE_PREFIX + title;
1082                    }
1083                }
1084
1085                if (title == null) {
1086                    Log.e(TAG, "Unbalanced name/data pair");
1087                } else {
1088                    if (date == -1) {
1089                        date = mCaptureStartTime;
1090                    }
1091                    if (mHeading >= 0) {
1092                        // heading direction has been updated by the sensor.
1093                        ExifTag directionRefTag = exif.buildTag(
1094                                ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
1095                                ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
1096                        ExifTag directionTag = exif.buildTag(
1097                                ExifInterface.TAG_GPS_IMG_DIRECTION,
1098                                new Rational(mHeading, 1));
1099                        exif.setTag(directionRefTag);
1100                        exif.setTag(directionTag);
1101                    }
1102                    getServices().getMediaSaver().addImage(
1103                            jpegData, title, date, mLocation, width, height,
1104                            orientation, exif, mOnMediaSavedListener, mContentResolver);
1105                }
1106                // Animate capture with real jpeg data instead of a preview
1107                // frame.
1108                mUI.animateCapture(jpegData, orientation, mMirror);
1109            } else {
1110                mJpegImageData = jpegData;
1111                if (!mQuickCapture) {
1112                    mUI.showCapturedImageForReview(jpegData, orientation, mMirror);
1113                } else {
1114                    onCaptureDone();
1115                }
1116            }
1117
1118            // Send the taken photo to remote shutter listeners, if any are
1119            // registered.
1120            AsyncTask.SERIAL_EXECUTOR.execute(new Runnable() {
1121                @Override
1122                public void run() {
1123                    getServices().getRemoteShutterListener().onPictureTaken(jpegData);
1124                }
1125            });
1126
1127            // Check this in advance of each shot so we don't add to shutter
1128            // latency. It's true that someone else could write to the SD card
1129            // in the mean time and fill it, but that could have happened
1130            // between the shutter press and saving the JPEG too.
1131            mActivity.updateStorageSpaceAndHint(null);
1132        }
1133    }
1134
1135    private final class AutoFocusCallback implements CameraAFCallback {
1136        @Override
1137        public void onAutoFocus(boolean focused, CameraProxy camera) {
1138            SessionStatsCollector.instance().autofocusResult(focused);
1139            if (mPaused) {
1140                return;
1141            }
1142
1143            mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
1144            Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms   focused = "+focused);
1145            setCameraState(IDLE);
1146            mFocusManager.onAutoFocus(focused, false);
1147        }
1148    }
1149
1150    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
1151    private final class AutoFocusMoveCallback
1152            implements CameraAFMoveCallback {
1153        @Override
1154        public void onAutoFocusMoving(
1155                boolean moving, CameraProxy camera) {
1156            mFocusManager.onAutoFocusMoving(moving);
1157            SessionStatsCollector.instance().autofocusMoving(moving);
1158        }
1159    }
1160
1161    /**
1162     * This class is just a thread-safe queue for name,date holder objects.
1163     */
1164    public static class NamedImages {
1165        private final Vector<NamedEntity> mQueue;
1166
1167        public NamedImages() {
1168            mQueue = new Vector<NamedEntity>();
1169        }
1170
1171        public void nameNewImage(long date) {
1172            NamedEntity r = new NamedEntity();
1173            r.title = CameraUtil.createJpegName(date);
1174            r.date = date;
1175            mQueue.add(r);
1176        }
1177
1178        public NamedEntity getNextNameEntity() {
1179            synchronized (mQueue) {
1180                if (!mQueue.isEmpty()) {
1181                    return mQueue.remove(0);
1182                }
1183            }
1184            return null;
1185        }
1186
1187        public static class NamedEntity {
1188            public String title;
1189            public long date;
1190        }
1191    }
1192
1193    private void setCameraState(int state) {
1194        mCameraState = state;
1195        switch (state) {
1196            case PREVIEW_STOPPED:
1197            case SNAPSHOT_IN_PROGRESS:
1198            case SWITCHING_CAMERA:
1199                // TODO: Tell app UI to disable swipe
1200                break;
1201            case PhotoController.IDLE:
1202                // TODO: Tell app UI to enable swipe
1203                break;
1204        }
1205    }
1206
1207    private void animateAfterShutter() {
1208        // Only animate when in full screen capture mode
1209        // i.e. If monkey/a user swipes to the gallery during picture taking,
1210        // don't show animation
1211        if (!mIsImageCaptureIntent) {
1212            mUI.animateFlash();
1213        }
1214    }
1215
1216    @Override
1217    public boolean capture() {
1218        // If we are already in the middle of taking a snapshot or the image
1219        // save request is full then ignore.
1220        if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
1221                || mCameraState == SWITCHING_CAMERA || !mAppController.isShutterEnabled()) {
1222            return false;
1223        }
1224        mCaptureStartTime = System.currentTimeMillis();
1225
1226        mPostViewPictureCallbackTime = 0;
1227        mJpegImageData = null;
1228
1229        final boolean animateBefore = (mSceneMode == CameraUtil.SCENE_MODE_HDR);
1230
1231        if (animateBefore) {
1232            animateAfterShutter();
1233        }
1234
1235        // Set rotation and gps data.
1236        int orientation;
1237
1238        // We need to be consistent with the framework orientation (i.e. the
1239        // orientation of the UI.) when the auto-rotate screen setting is on.
1240        if (mActivity.isAutoRotateScreen()) {
1241            orientation = (360 - mDisplayRotation) % 360;
1242        } else {
1243            orientation = mOrientation;
1244        }
1245        CameraInfo info = mActivity.getCameraProvider().getCameraInfo()[mCameraId];
1246        mJpegRotation = CameraUtil.getJpegRotation(info, orientation);
1247        mParameters.setRotation(mJpegRotation);
1248        Location loc = mActivity.getLocationManager().getCurrentLocation();
1249        CameraUtil.setGpsParameters(mParameters, loc);
1250        mCameraDevice.setParameters(mParameters);
1251
1252        // We don't want user to press the button again while taking a
1253        // multi-second HDR photo.
1254        mAppController.setShutterEnabled(false);
1255        mCameraDevice.takePicture(mHandler,
1256                new ShutterCallback(!animateBefore),
1257                mRawPictureCallback, mPostViewPictureCallback,
1258                new JpegPictureCallback(loc));
1259
1260        mNamedImages.nameNewImage(mCaptureStartTime);
1261
1262        mFaceDetectionStarted = false;
1263        setCameraState(SNAPSHOT_IN_PROGRESS);
1264        return true;
1265    }
1266
1267    @Override
1268    public void setFocusParameters() {
1269        setCameraParameters(UPDATE_PARAM_PREFERENCE);
1270    }
1271
1272    private void updateSceneMode() {
1273        // If scene mode is set, we cannot set flash mode, white balance, and
1274        // focus mode, instead, we read it from driver
1275        if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
1276            overrideCameraSettings(mParameters.getFlashMode(), mParameters.getFocusMode());
1277        }
1278    }
1279
1280    private void overrideCameraSettings(final String flashMode, final String focusMode) {
1281        SettingsManager settingsManager = mActivity.getSettingsManager();
1282        settingsManager.set(SettingsManager.SETTING_FLASH_MODE, flashMode);
1283        settingsManager.set(SettingsManager.SETTING_FOCUS_MODE, focusMode);
1284    }
1285
1286    @Override
1287    public void onOrientationChanged(int orientation) {
1288        // We keep the last known orientation. So if the user first orient
1289        // the camera then point the camera to floor or sky, we still have
1290        // the correct orientation.
1291        if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
1292            return;
1293        }
1294        mOrientation = CameraUtil.roundOrientation(orientation, mOrientation);
1295    }
1296
1297    @Override
1298    public void onCameraAvailable(CameraProxy cameraProxy) {
1299        if (mPaused) {
1300            return;
1301        }
1302        mCameraDevice = cameraProxy;
1303
1304        initializeCapabilities();
1305
1306        // Reset zoom value index.
1307        mZoomValue = 0;
1308        if (mFocusManager == null) {
1309            initializeFocusManager();
1310        }
1311        mFocusManager.setParameters(mInitialParams, mCameraCapabilities);
1312
1313        // Do camera parameter dependent initialization.
1314        mParameters = mCameraDevice.getParameters();
1315        setCameraParameters(UPDATE_PARAM_ALL);
1316        // Set a listener which updates camera parameters based
1317        // on changed settings.
1318        SettingsManager settingsManager = mActivity.getSettingsManager();
1319        settingsManager.addListener(this);
1320        mCameraPreviewParamsReady = true;
1321
1322        startPreview();
1323
1324        onCameraOpened();
1325    }
1326
1327    @Override
1328    public void onCaptureCancelled() {
1329        mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
1330        mActivity.finish();
1331    }
1332
1333    @Override
1334    public void onCaptureRetake() {
1335        if (mPaused) {
1336            return;
1337        }
1338        mUI.hidePostCaptureAlert();
1339        setupPreview();
1340    }
1341
1342    @Override
1343    public void onCaptureDone() {
1344        if (mPaused) {
1345            return;
1346        }
1347
1348        byte[] data = mJpegImageData;
1349
1350        if (mCropValue == null) {
1351            // First handle the no crop case -- just return the value. If the
1352            // caller specifies a "save uri" then write the data to its
1353            // stream. Otherwise, pass back a scaled down version of the bitmap
1354            // directly in the extras.
1355            if (mSaveUri != null) {
1356                OutputStream outputStream = null;
1357                try {
1358                    outputStream = mContentResolver.openOutputStream(mSaveUri);
1359                    outputStream.write(data);
1360                    outputStream.close();
1361
1362                    mActivity.setResultEx(Activity.RESULT_OK);
1363                    mActivity.finish();
1364                } catch (IOException ex) {
1365                    // ignore exception
1366                } finally {
1367                    CameraUtil.closeSilently(outputStream);
1368                }
1369            } else {
1370                ExifInterface exif = Exif.getExif(data);
1371                int orientation = Exif.getOrientation(exif);
1372                Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024);
1373                bitmap = CameraUtil.rotate(bitmap, orientation);
1374                mActivity.setResultEx(Activity.RESULT_OK,
1375                        new Intent("inline-data").putExtra("data", bitmap));
1376                mActivity.finish();
1377            }
1378        } else {
1379            // Save the image to a temp file and invoke the cropper
1380            Uri tempUri = null;
1381            FileOutputStream tempStream = null;
1382            try {
1383                File path = mActivity.getFileStreamPath(sTempCropFilename);
1384                path.delete();
1385                tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1386                tempStream.write(data);
1387                tempStream.close();
1388                tempUri = Uri.fromFile(path);
1389            } catch (FileNotFoundException ex) {
1390                mActivity.setResultEx(Activity.RESULT_CANCELED);
1391                mActivity.finish();
1392                return;
1393            } catch (IOException ex) {
1394                mActivity.setResultEx(Activity.RESULT_CANCELED);
1395                mActivity.finish();
1396                return;
1397            } finally {
1398                CameraUtil.closeSilently(tempStream);
1399            }
1400
1401            Bundle newExtras = new Bundle();
1402            if (mCropValue.equals("circle")) {
1403                newExtras.putString("circleCrop", "true");
1404            }
1405            if (mSaveUri != null) {
1406                newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1407            } else {
1408                newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true);
1409            }
1410            if (mActivity.isSecureCamera()) {
1411                newExtras.putBoolean(CameraUtil.KEY_SHOW_WHEN_LOCKED, true);
1412            }
1413
1414            // TODO: Share this constant.
1415            final String CROP_ACTION = "com.android.camera.action.CROP";
1416            Intent cropIntent = new Intent(CROP_ACTION);
1417
1418            cropIntent.setData(tempUri);
1419            cropIntent.putExtras(newExtras);
1420
1421            mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1422        }
1423    }
1424
1425    @Override
1426    public void onShutterButtonFocus(boolean pressed) {
1427        // Do nothing. We don't support half-press to focus anymore.
1428    }
1429
1430    @Override
1431    public void onShutterButtonClick() {
1432        if (mPaused || (mCameraState == SWITCHING_CAMERA)
1433                || (mCameraState == PREVIEW_STOPPED)) {
1434            return;
1435        }
1436
1437        // Do not take the picture if there is not enough storage.
1438        if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
1439            Log.i(TAG, "Not enough space or storage not ready. remaining="
1440                    + mActivity.getStorageSpaceBytes());
1441            return;
1442        }
1443        Log.d(TAG, "onShutterButtonClick: mCameraState=" + mCameraState);
1444
1445        int countDownDuration = Integer.parseInt(mActivity.getSettingsManager()
1446                .get(SettingsManager.SETTING_COUNTDOWN_DURATION));
1447        mTimerDuration = countDownDuration;
1448        if (countDownDuration > 0) {
1449            // Start count down.
1450            mAppController.getCameraAppUI().transitionToCancel();
1451            mAppController.getCameraAppUI().hideModeOptions();
1452            mUI.startCountdown(countDownDuration);
1453            return;
1454        } else {
1455            focusAndCapture();
1456        }
1457    }
1458
1459    private void focusAndCapture() {
1460        if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {
1461            mUI.setSwipingEnabled(false);
1462        }
1463        // If the user wants to do a snapshot while the previous one is still
1464        // in progress, remember the fact and do it after we finish the previous
1465        // one and re-start the preview. Snapshot in progress also includes the
1466        // state that autofocus is focusing and a picture will be taken when
1467        // focus callback arrives.
1468        if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)) {
1469            if (!mIsImageCaptureIntent) {
1470                mSnapshotOnIdle = true;
1471            }
1472            return;
1473        }
1474
1475        mSnapshotOnIdle = false;
1476        mFocusManager.focusAndCapture();
1477    }
1478
1479    @Override
1480    public void onRemainingSecondsChanged(int remainingSeconds) {
1481        mCountdownSoundPlayer.onRemainingSecondsChanged(remainingSeconds);
1482    }
1483
1484    @Override
1485    public void onCountDownFinished() {
1486        mAppController.getCameraAppUI().transitionToCapture();
1487        mAppController.getCameraAppUI().showModeOptions();
1488        if (mPaused) {
1489            return;
1490        }
1491        focusAndCapture();
1492    }
1493
1494    private void onResumeTasks() {
1495        if (mPaused) {
1496            return;
1497        }
1498        Log.v(TAG, "Executing onResumeTasks.");
1499        CameraProvider camProvider = mActivity.getCameraProvider();
1500        if (camProvider == null) {
1501            // No camera provider, the Activity is destroyed already.
1502            return;
1503        }
1504        camProvider.requestCamera(mCameraId);
1505
1506        mJpegPictureCallbackTime = 0;
1507        mZoomValue = 0;
1508
1509        mOnResumeTime = SystemClock.uptimeMillis();
1510        checkDisplayRotation();
1511
1512        // If first time initialization is not finished, put it in the
1513        // message queue.
1514        if (!mFirstTimeInitialized) {
1515            mHandler.sendEmptyMessage(MSG_FIRST_TIME_INIT);
1516        } else {
1517            initializeSecondTime();
1518        }
1519
1520        Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1521        if (gsensor != null) {
1522            mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL);
1523        }
1524
1525        Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1526        if (msensor != null) {
1527            mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL);
1528        }
1529    }
1530
1531    /**
1532     * @return Whether the currently active camera is front-facing.
1533     */
1534    private boolean isCameraFrontFacing() {
1535        CameraInfo info = mAppController.getCameraProvider().getCameraInfo()[mCameraId];
1536        return info.facing == CameraInfo.CAMERA_FACING_FRONT;
1537    }
1538
1539    /**
1540     * The focus manager is the first UI related element to get initialized, and
1541     * it requires the RenderOverlay, so initialize it here
1542     */
1543    private void initializeFocusManager() {
1544        // Create FocusManager object. startPreview needs it.
1545        // if mFocusManager not null, reuse it
1546        // otherwise create a new instance
1547        if (mFocusManager != null) {
1548            mFocusManager.removeMessages();
1549        } else {
1550            mMirror = isCameraFrontFacing();
1551            String[] defaultFocusModes = mActivity.getResources().getStringArray(
1552                    R.array.pref_camera_focusmode_default_array);
1553            mFocusManager = new FocusOverlayManager(mActivity.getSettingsManager(),
1554                    defaultFocusModes,
1555                    mInitialParams, this, mMirror,
1556                    mActivity.getMainLooper(), mUI.getFocusUI());
1557        }
1558        mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
1559    }
1560
1561    @Override
1562    public void resume() {
1563        mPaused = false;
1564        mCountdownSoundPlayer.loadSounds();
1565        if (mFocusManager != null) {
1566            // If camera is not open when resume is called, focus manager will
1567            // not
1568            // be initialized yet, in which case it will start listening to
1569            // preview area size change later in the initialization.
1570            mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
1571        }
1572        mAppController.addPreviewAreaSizeChangedListener(mUI);
1573
1574        // Add delay on resume from lock screen only, in order to to speed up
1575        // the onResume --> onPause --> onResume cycle from lock screen.
1576        // Don't do always because letting go of thread can cause delay.
1577        String action = mActivity.getIntent().getAction();
1578        if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action)
1579                || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)) {
1580            Log.v(TAG, "On resume, from lock screen.");
1581            // Note: onPauseAfterSuper() will delete this runnable, so we will
1582            // at most have 1 copy queued up.
1583            mHandler.postDelayed(mResumeTaskRunnable, ON_RESUME_TASKS_DELAY_MSEC);
1584        } else {
1585            Log.v(TAG, "On resume.");
1586            onResumeTasks();
1587        }
1588        getServices().getRemoteShutterListener().onModuleReady(this);
1589        SessionStatsCollector.instance().sessionActive(true);
1590    }
1591
1592    @Override
1593    public void pause() {
1594        getServices().getRemoteShutterListener().onModuleExit();
1595        mPaused = true;
1596        mHandler.removeCallbacks(mResumeTaskRunnable);
1597        SessionStatsCollector.instance().sessionActive(false);
1598
1599        Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1600        if (gsensor != null) {
1601            mSensorManager.unregisterListener(this, gsensor);
1602        }
1603
1604        Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1605        if (msensor != null) {
1606            mSensorManager.unregisterListener(this, msensor);
1607        }
1608
1609        // Reset the focus first. Camera CTS does not guarantee that
1610        // cancelAutoFocus is allowed after preview stops.
1611        if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1612            mCameraDevice.cancelAutoFocus();
1613        }
1614
1615        // If the camera has not been opened asynchronously yet,
1616        // and startPreview hasn't been called, then this is a no-op.
1617        // (e.g. onResume -> onPause -> onResume).
1618        stopPreview();
1619        cancelCountDown();
1620        mCountdownSoundPlayer.release();
1621
1622        mNamedImages = null;
1623        // If we are in an image capture intent and has taken
1624        // a picture, we just clear it in onPause.
1625        mJpegImageData = null;
1626
1627        // Remove the messages and runnables in the queue.
1628        mHandler.removeCallbacksAndMessages(null);
1629
1630        closeCamera();
1631        mActivity.enableKeepScreenOn(false);
1632        mUI.onPause();
1633
1634        mPendingSwitchCameraId = -1;
1635        if (mFocusManager != null) {
1636            mFocusManager.removeMessages();
1637        }
1638        getServices().getMemoryManager().removeListener(this);
1639        mAppController.removePreviewAreaSizeChangedListener(mFocusManager);
1640        mAppController.removePreviewAreaSizeChangedListener(mUI);
1641
1642        SettingsManager settingsManager = mActivity.getSettingsManager();
1643        settingsManager.removeListener(this);
1644    }
1645
1646    @Override
1647    public void destroy() {
1648        // TODO: implement this.
1649    }
1650
1651    @Override
1652    public void onLayoutOrientationChanged(boolean isLandscape) {
1653        setDisplayOrientation();
1654    }
1655
1656    @Override
1657    public void updateCameraOrientation() {
1658        if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) {
1659            setDisplayOrientation();
1660        }
1661    }
1662
1663    private boolean canTakePicture() {
1664        return isCameraIdle()
1665                && (mActivity.getStorageSpaceBytes() > Storage.LOW_STORAGE_THRESHOLD_BYTES);
1666    }
1667
1668    @Override
1669    public void autoFocus() {
1670        Log.v(TAG,"Starting auto focus");
1671        mFocusStartTime = System.currentTimeMillis();
1672        mCameraDevice.autoFocus(mHandler, mAutoFocusCallback);
1673        SessionStatsCollector.instance().autofocusManualTrigger();
1674        setCameraState(FOCUSING);
1675    }
1676
1677    @Override
1678    public void cancelAutoFocus() {
1679        mCameraDevice.cancelAutoFocus();
1680        setCameraState(IDLE);
1681        setCameraParameters(UPDATE_PARAM_PREFERENCE);
1682    }
1683
1684    @Override
1685    public void onSingleTapUp(View view, int x, int y) {
1686        if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1687                || mCameraState == SNAPSHOT_IN_PROGRESS
1688                || mCameraState == SWITCHING_CAMERA
1689                || mCameraState == PREVIEW_STOPPED) {
1690            return;
1691        }
1692
1693        // Check if metering area or focus area is supported.
1694        if (!mFocusAreaSupported && !mMeteringAreaSupported) {
1695            return;
1696        }
1697        mFocusManager.onSingleTapUp(x, y);
1698    }
1699
1700    @Override
1701    public boolean onBackPressed() {
1702        return mUI.onBackPressed();
1703    }
1704
1705    @Override
1706    public boolean onKeyDown(int keyCode, KeyEvent event) {
1707        switch (keyCode) {
1708            case KeyEvent.KEYCODE_VOLUME_UP:
1709            case KeyEvent.KEYCODE_VOLUME_DOWN:
1710            case KeyEvent.KEYCODE_FOCUS:
1711                if (/* TODO: mActivity.isInCameraApp() && */mFirstTimeInitialized) {
1712                    if (event.getRepeatCount() == 0) {
1713                        onShutterButtonFocus(true);
1714                    }
1715                    return true;
1716                }
1717                return false;
1718            case KeyEvent.KEYCODE_CAMERA:
1719                if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1720                    onShutterButtonClick();
1721                }
1722                return true;
1723            case KeyEvent.KEYCODE_DPAD_CENTER:
1724                // If we get a dpad center event without any focused view, move
1725                // the focus to the shutter button and press it.
1726                if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1727                    // Start auto-focus immediately to reduce shutter lag. After
1728                    // the shutter button gets the focus, onShutterButtonFocus()
1729                    // will be called again but it is fine.
1730                    onShutterButtonFocus(true);
1731                }
1732                return true;
1733        }
1734        return false;
1735    }
1736
1737    @Override
1738    public boolean onKeyUp(int keyCode, KeyEvent event) {
1739        switch (keyCode) {
1740            case KeyEvent.KEYCODE_VOLUME_UP:
1741            case KeyEvent.KEYCODE_VOLUME_DOWN:
1742                if (/* mActivity.isInCameraApp() && */mFirstTimeInitialized) {
1743                    onShutterButtonClick();
1744                    return true;
1745                }
1746                return false;
1747            case KeyEvent.KEYCODE_FOCUS:
1748                if (mFirstTimeInitialized) {
1749                    onShutterButtonFocus(false);
1750                }
1751                return true;
1752        }
1753        return false;
1754    }
1755
1756    private void closeCamera() {
1757        if (mCameraDevice != null) {
1758            stopFaceDetection();
1759            mCameraDevice.setZoomChangeListener(null);
1760            mCameraDevice.setFaceDetectionCallback(null, null);
1761            mCameraDevice.setErrorCallback(null, null);
1762
1763            mFaceDetectionStarted = false;
1764            mActivity.getCameraProvider().releaseCamera(mCameraDevice.getCameraId());
1765            mCameraDevice = null;
1766            setCameraState(PREVIEW_STOPPED);
1767            mFocusManager.onCameraReleased();
1768        }
1769    }
1770
1771    private void setDisplayOrientation() {
1772        mDisplayRotation = CameraUtil.getDisplayRotation(mActivity);
1773        mDisplayOrientation = CameraUtil.getDisplayOrientation(mDisplayRotation, mCameraId);
1774        mCameraDisplayOrientation = mDisplayOrientation;
1775        mUI.setDisplayOrientation(mDisplayOrientation);
1776        if (mFocusManager != null) {
1777            mFocusManager.setDisplayOrientation(mDisplayOrientation);
1778        }
1779        // Change the camera display orientation
1780        if (mCameraDevice != null) {
1781            mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);
1782        }
1783    }
1784
1785    /** Only called by UI thread. */
1786    private void setupPreview() {
1787        mFocusManager.resetTouchFocus();
1788        startPreview();
1789    }
1790
1791    /**
1792     * Returns whether we can/should start the preview or not.
1793     */
1794    private boolean checkPreviewPreconditions() {
1795        if (mPaused) {
1796            return false;
1797        }
1798
1799        if (mCameraDevice == null) {
1800            Log.w(TAG, "startPreview: camera device not ready yet.");
1801            return false;
1802        }
1803
1804        SurfaceTexture st = mActivity.getCameraAppUI().getSurfaceTexture();
1805        if (st == null) {
1806            Log.w(TAG, "startPreview: surfaceTexture is not ready.");
1807            return false;
1808        }
1809
1810        if (!mCameraPreviewParamsReady) {
1811            Log.w(TAG, "startPreview: parameters for preview is not ready.");
1812            return false;
1813        }
1814        return true;
1815    }
1816
1817    /**
1818     * The start/stop preview should only run on the UI thread.
1819     */
1820    private void startPreview() {
1821        if (!checkPreviewPreconditions()) {
1822            return;
1823        }
1824
1825        mCameraDevice.setErrorCallback(mHandler, mErrorCallback);
1826        setDisplayOrientation();
1827
1828        if (!mSnapshotOnIdle) {
1829            // If the focus mode is continuous autofocus, call cancelAutoFocus
1830            // to resume it because it may have been paused by autoFocus call.
1831            String focusMode = mFocusManager.getFocusMode();
1832            if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(focusMode)) {
1833                mCameraDevice.cancelAutoFocus();
1834            }
1835            mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1836        }
1837        setCameraParameters(UPDATE_PARAM_ALL);
1838
1839        // Workaround for KitKat and KitKat MR1 which leave configured preview
1840        // callback streams lingering around when they should have been removed.
1841        // These preview callback streams are the cause for distorted preview.
1842        // For more details, see b/12210027
1843        if (mShouldSetPreviewCallbacks) {
1844            mShouldSetPreviewCallbacks = false;
1845            Size previewSize = new Size(mCameraDevice.getParameters().getPreviewSize());
1846            mCameraDevice.setPreviewDataCallbackWithBuffer(mHandler,
1847                    new CameraManager.CameraPreviewDataCallback() {
1848                @Override
1849                public void onPreviewFrame(byte[] data, CameraProxy camera) {
1850                    // Remove callback after the first frame comes in.
1851                    mCameraDevice.setPreviewDataCallbackWithBuffer(null, null);
1852                }
1853            });
1854            mCameraDevice.addCallbackBuffer(new byte[previewSize.width() * previewSize.height()]);
1855        }
1856        mCameraDevice.setPreviewTexture(mActivity.getCameraAppUI().getSurfaceTexture());
1857
1858        Log.i(TAG, "startPreview");
1859        mCameraDevice.startPreview();
1860
1861        mFocusManager.onPreviewStarted();
1862        onPreviewStarted();
1863        SessionStatsCollector.instance().previewActive(true);
1864        if (mSnapshotOnIdle) {
1865            mHandler.post(mDoSnapRunnable);
1866        }
1867    }
1868
1869    @Override
1870    public void stopPreview() {
1871        if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1872            Log.i(TAG, "stopPreview");
1873            mCameraDevice.stopPreview();
1874            mFaceDetectionStarted = false;
1875        }
1876        setCameraState(PREVIEW_STOPPED);
1877        if (mFocusManager != null) {
1878            mFocusManager.onPreviewStopped();
1879        }
1880        SessionStatsCollector.instance().previewActive(false);
1881    }
1882
1883    @Override
1884    public void onSettingChanged(SettingsManager settingsManager, int id) {
1885        switch (id) {
1886            case SettingsManager.SETTING_FLASH_MODE: {
1887                updateParametersFlashMode();
1888                break;
1889            }
1890            default: {
1891                // Do nothing.
1892            }
1893        }
1894
1895        if (mCameraDevice != null) {
1896            mCameraDevice.setParameters(mParameters);
1897        }
1898    }
1899
1900    private void updateCameraParametersInitialize() {
1901        // Reset preview frame rate to the maximum because it may be lowered by
1902        // video camera application.
1903        int[] fpsRange = CameraUtil.getPhotoPreviewFpsRange(mCameraCapabilities);
1904        if (fpsRange != null && fpsRange.length > 0) {
1905            mParameters.setPreviewFpsRange(
1906                    fpsRange[Parameters.PREVIEW_FPS_MIN_INDEX],
1907                    fpsRange[Parameters.PREVIEW_FPS_MAX_INDEX]);
1908        }
1909
1910        mParameters.set(CameraUtil.RECORDING_HINT, CameraUtil.FALSE);
1911
1912        // Disable video stabilization. Convenience methods not available in API
1913        // level <= 14
1914        String vstabSupported = mParameters.get("video-stabilization-supported");
1915        if ("true".equals(vstabSupported)) {
1916            mParameters.set("video-stabilization", "false");
1917        }
1918    }
1919
1920    private void updateCameraParametersZoom() {
1921        // Set zoom.
1922        if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
1923            mParameters.setZoom(mZoomValue);
1924        }
1925    }
1926
1927    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
1928    private void setAutoExposureLockIfSupported() {
1929        if (mAeLockSupported) {
1930            mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock());
1931        }
1932    }
1933
1934    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
1935    private void setAutoWhiteBalanceLockIfSupported() {
1936        if (mAwbLockSupported) {
1937            mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
1938        }
1939    }
1940
1941    private void setFocusAreasIfSupported() {
1942        if (mFocusAreaSupported) {
1943            mParameters.setFocusAreas(mFocusManager.getFocusAreas());
1944        }
1945    }
1946
1947    private void setMeteringAreasIfSupported() {
1948        if (mMeteringAreaSupported) {
1949            mParameters.setMeteringAreas(mFocusManager.getMeteringAreas());
1950        }
1951    }
1952
1953    private void updateCameraParametersPreference() {
1954        setAutoExposureLockIfSupported();
1955        setAutoWhiteBalanceLockIfSupported();
1956        setFocusAreasIfSupported();
1957        setMeteringAreasIfSupported();
1958
1959        // Initialize focus mode.
1960        mFocusManager.overrideFocusMode(null);
1961        mParameters.setFocusMode(mFocusManager.getFocusMode());
1962        SessionStatsCollector.instance().autofocusActive(
1963                mFocusManager.getFocusMode() == CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE);
1964
1965        // Set picture size.
1966        updateParametersPictureSize();
1967
1968        // Set JPEG quality.
1969        updateParametersPictureQuality();
1970
1971        // For the following settings, we need to check if the settings are
1972        // still supported by latest driver, if not, ignore the settings.
1973
1974        // Set exposure compensation
1975        updateParametersExposureCompensation();
1976
1977        // Set the scene mode: also sets flash and white balance.
1978        updateParametersSceneMode();
1979
1980        if (mContinuousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
1981            updateAutoFocusMoveCallback();
1982        }
1983    }
1984
1985    private void updateParametersPictureSize() {
1986        SettingsManager settingsManager = mActivity.getSettingsManager();
1987        String pictureSize = settingsManager
1988                .get(isCameraFrontFacing() ? SettingsManager.SETTING_PICTURE_SIZE_FRONT
1989                        : SettingsManager.SETTING_PICTURE_SIZE_BACK);
1990
1991        List<Size> supported = Size.buildListFromCameraSizes(mParameters.getSupportedPictureSizes());
1992        SettingsUtil.setCameraPictureSize(pictureSize, supported, mParameters,
1993                mCameraDevice.getCameraId());
1994
1995        Size size = SettingsUtil.getPhotoSize(pictureSize, supported,
1996                mCameraDevice.getCameraId());
1997        if (ApiHelper.IS_NEXUS_5) {
1998            if (ResolutionUtil.NEXUS_5_LARGE_16_BY_9.equals(pictureSize)) {
1999                mShouldResizeTo16x9 = true;
2000            } else {
2001                mShouldResizeTo16x9 = false;
2002            }
2003        }
2004
2005        // Set a preview size that is closest to the viewfinder height and has
2006        // the right aspect ratio.
2007        List<Size> sizes = Size.buildListFromCameraSizes(mParameters.getSupportedPreviewSizes());
2008        Size optimalSize = CameraUtil.getOptimalPreviewSize(mActivity, sizes,
2009                (double) size.width() / size.height());
2010        Size original = new Size(mParameters.getPreviewSize());
2011        if (!original.equals(optimalSize)) {
2012            if (ApiHelper.SHOULD_HARD_RESET_PREVIEW_CALLBACK) {
2013                // Compare the aspect ratio.
2014                if ((original.width() * optimalSize.height())
2015                        != (original.height() * optimalSize.width())) {
2016                    // If aspect ratio has changed, set preview callback again, so
2017                    // that the old preview callback stream will be forced to update.
2018                    // This is a workaround for b/12210027, which was fixed in Kitkat MR2.
2019                    mShouldSetPreviewCallbacks = true;
2020                }
2021            }
2022            mParameters.setPreviewSize(optimalSize.width(), optimalSize.height());
2023
2024            // Zoom related settings will be changed for different preview
2025            // sizes, so set and read the parameters to get latest values
2026            if (mHandler.getLooper() == Looper.myLooper()) {
2027                // On UI thread only, not when camera starts up
2028                setupPreview();
2029            } else {
2030                mCameraDevice.setParameters(mParameters);
2031            }
2032            mParameters = mCameraDevice.getParameters();
2033        }
2034
2035        if (optimalSize.width() != 0 && optimalSize.height() != 0) {
2036            mUI.updatePreviewAspectRatio((float) optimalSize.width()
2037                    / (float) optimalSize.height());
2038        }
2039        Log.i(TAG, "Preview size is " + optimalSize);
2040    }
2041
2042    private void updateParametersPictureQuality() {
2043        int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
2044                CameraProfile.QUALITY_HIGH);
2045        mParameters.setJpegQuality(jpegQuality);
2046    }
2047
2048    private void updateParametersExposureCompensation() {
2049        SettingsManager settingsManager = mActivity.getSettingsManager();
2050        if (settingsManager.getBoolean(SettingsManager.SETTING_EXPOSURE_COMPENSATION_ENABLED)) {
2051            int value = Integer.parseInt(settingsManager.get(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE));
2052            int max = mCameraCapabilities.getMaxExposureCompensation();
2053            int min = mCameraCapabilities.getMinExposureCompensation();
2054            if (value >= min && value <= max) {
2055                mParameters.setExposureCompensation(value);
2056            } else {
2057                Log.w(TAG, "invalid exposure range: " + value);
2058            }
2059        } else {
2060            // If exposure compensation is not enabled, reset the exposure compensation value.
2061            setExposureCompensation(0);
2062        }
2063
2064    }
2065
2066    private void updateParametersSceneMode() {
2067        SettingsManager settingsManager = mActivity.getSettingsManager();
2068
2069        mSceneMode = settingsManager.get(SettingsManager.SETTING_SCENE_MODE);
2070        if (mCameraCapabilities
2071                .supports(mCameraCapabilities.getStringifier().sceneModeFromString(mSceneMode))) {
2072            if (!mParameters.getSceneMode().equals(mSceneMode)) {
2073                mParameters.setSceneMode(mSceneMode);
2074
2075                // Setting scene mode will change the settings of flash mode,
2076                // white balance, and focus mode. Here we read back the
2077                // parameters, so we can know those settings.
2078                mCameraDevice.setParameters(mParameters);
2079                mParameters = mCameraDevice.getParameters();
2080            }
2081        } else {
2082            mSceneMode = mParameters.getSceneMode();
2083            if (mSceneMode == null) {
2084                mSceneMode = Parameters.SCENE_MODE_AUTO;
2085            }
2086        }
2087
2088        if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
2089            // Set flash mode.
2090            updateParametersFlashMode();
2091
2092            // Set focus mode.
2093            mFocusManager.overrideFocusMode(null);
2094            mParameters.setFocusMode(mFocusManager.getFocusMode());
2095        } else {
2096            mFocusManager.overrideFocusMode(mParameters.getFocusMode());
2097        }
2098    }
2099
2100    private void updateParametersFlashMode() {
2101        SettingsManager settingsManager = mActivity.getSettingsManager();
2102
2103        String flashMode = settingsManager.get(SettingsManager.SETTING_FLASH_MODE);
2104        if (mCameraCapabilities
2105                .supports(mCameraCapabilities.getStringifier().flashModeFromString(flashMode))) {
2106            mParameters.setFlashMode(flashMode);
2107        }
2108    }
2109
2110    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
2111    private void updateAutoFocusMoveCallback() {
2112        if (mParameters.getFocusMode().equals(CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE)) {
2113            mCameraDevice.setAutoFocusMoveCallback(mHandler,
2114                    (CameraAFMoveCallback) mAutoFocusMoveCallback);
2115        } else {
2116            mCameraDevice.setAutoFocusMoveCallback(null, null);
2117        }
2118    }
2119
2120    /**
2121     * Sets the exposure compensation to the given value and also updates settings.
2122     *
2123     * @param value exposure compensation value to be set
2124     */
2125    public void setExposureCompensation(int value) {
2126        int max = mCameraCapabilities.getMaxExposureCompensation();
2127        int min = mCameraCapabilities.getMinExposureCompensation();
2128        if (value >= min && value <= max) {
2129            mParameters.setExposureCompensation(value);
2130            SettingsManager settingsManager = mActivity.getSettingsManager();
2131            settingsManager.set(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE, Integer.toString(value));
2132        } else {
2133            Log.w(TAG, "invalid exposure range: " + value);
2134        }
2135    }
2136
2137    // We separate the parameters into several subsets, so we can update only
2138    // the subsets actually need updating. The PREFERENCE set needs extra
2139    // locking because the preference can be changed from GLThread as well.
2140    private void setCameraParameters(int updateSet) {
2141        if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
2142            updateCameraParametersInitialize();
2143        }
2144
2145        if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
2146            updateCameraParametersZoom();
2147        }
2148
2149        if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
2150            updateCameraParametersPreference();
2151        }
2152
2153        mCameraDevice.setParameters(mParameters);
2154    }
2155
2156    // If the Camera is idle, update the parameters immediately, otherwise
2157    // accumulate them in mUpdateSet and update later.
2158    private void setCameraParametersWhenIdle(int additionalUpdateSet) {
2159        mUpdateSet |= additionalUpdateSet;
2160        if (mCameraDevice == null) {
2161            // We will update all the parameters when we open the device, so
2162            // we don't need to do anything now.
2163            mUpdateSet = 0;
2164            return;
2165        } else if (isCameraIdle()) {
2166            setCameraParameters(mUpdateSet);
2167            updateSceneMode();
2168            mUpdateSet = 0;
2169        } else {
2170            if (!mHandler.hasMessages(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
2171                mHandler.sendEmptyMessageDelayed(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
2172            }
2173        }
2174    }
2175
2176    @Override
2177    public boolean isCameraIdle() {
2178        return (mCameraState == IDLE) ||
2179                (mCameraState == PREVIEW_STOPPED) ||
2180                ((mFocusManager != null) && mFocusManager.isFocusCompleted()
2181                && (mCameraState != SWITCHING_CAMERA));
2182    }
2183
2184    @Override
2185    public boolean isImageCaptureIntent() {
2186        String action = mActivity.getIntent().getAction();
2187        return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
2188        || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
2189    }
2190
2191    private void setupCaptureParams() {
2192        Bundle myExtras = mActivity.getIntent().getExtras();
2193        if (myExtras != null) {
2194            mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
2195            mCropValue = myExtras.getString("crop");
2196        }
2197    }
2198
2199    private void initializeCapabilities() {
2200        mInitialParams = mCameraDevice.getParameters();
2201        mCameraCapabilities = mCameraDevice.getCapabilities();
2202        mFocusAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA);
2203        mMeteringAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.METERING_AREA);
2204        mAeLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK);
2205        mAwbLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK);
2206        mContinuousFocusSupported = mInitialParams.getSupportedFocusModes().contains(
2207                CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE);
2208    }
2209
2210    // TODO: Remove this
2211    @Override
2212    public int onZoomChanged(int index) {
2213        // Not useful to change zoom value when the activity is paused.
2214        if (mPaused) {
2215            return index;
2216        }
2217        mZoomValue = index;
2218        if (mParameters == null || mCameraDevice == null) {
2219            return index;
2220        }
2221        // Set zoom parameters asynchronously
2222        mParameters.setZoom(mZoomValue);
2223        mCameraDevice.setParameters(mParameters);
2224        Parameters p = mCameraDevice.getParameters();
2225        if (p != null) {
2226            return p.getZoom();
2227        }
2228        return index;
2229    }
2230
2231    @Override
2232    public int getCameraState() {
2233        return mCameraState;
2234    }
2235
2236    @Override
2237    public void onMemoryStateChanged(int state) {
2238        mAppController.setShutterEnabled(state == MemoryManager.STATE_OK);
2239    }
2240
2241    @Override
2242    public void onLowMemory() {
2243        // Not much we can do in the photo module.
2244    }
2245
2246    @Override
2247    public void onAccuracyChanged(Sensor sensor, int accuracy) {
2248    }
2249
2250    @Override
2251    public void onSensorChanged(SensorEvent event) {
2252        int type = event.sensor.getType();
2253        float[] data;
2254        if (type == Sensor.TYPE_ACCELEROMETER) {
2255            data = mGData;
2256        } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
2257            data = mMData;
2258        } else {
2259            // we should not be here.
2260            return;
2261        }
2262        for (int i = 0; i < 3; i++) {
2263            data[i] = event.values[i];
2264        }
2265        float[] orientation = new float[3];
2266        SensorManager.getRotationMatrix(mR, null, mGData, mMData);
2267        SensorManager.getOrientation(mR, orientation);
2268        mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
2269        if (mHeading < 0) {
2270            mHeading += 360;
2271        }
2272    }
2273
2274    // For debugging only.
2275    public void setDebugUri(Uri uri) {
2276        mDebugUri = uri;
2277    }
2278
2279    // For debugging only.
2280    private void saveToDebugUri(byte[] data) {
2281        if (mDebugUri != null) {
2282            OutputStream outputStream = null;
2283            try {
2284                outputStream = mContentResolver.openOutputStream(mDebugUri);
2285                outputStream.write(data);
2286                outputStream.close();
2287            } catch (IOException e) {
2288                Log.e(TAG, "Exception while writing debug jpeg file", e);
2289            } finally {
2290                CameraUtil.closeSilently(outputStream);
2291            }
2292        }
2293    }
2294
2295    @Override
2296    public void onRemoteShutterPress() {
2297        capture();
2298    }
2299
2300    /**
2301     * This class manages the loading/releasing/playing of the sounds needed for
2302     * countdown timer.
2303     */
2304    private class CountdownSoundPlayer {
2305        private SoundPool mSoundPool;
2306        private int mBeepOnce;
2307        private int mBeepTwice;
2308
2309        void loadSounds() {
2310            // Load the beeps.
2311            mSoundPool = new SoundPool(1, AudioManager.STREAM_NOTIFICATION, 0);
2312            mBeepOnce = mSoundPool.load(mAppController.getAndroidContext(), R.raw.beep_once, 1);
2313            mBeepTwice = mSoundPool.load(mAppController.getAndroidContext(), R.raw.beep_twice, 1);
2314        }
2315
2316        void onRemainingSecondsChanged(int newVal) {
2317            if (mSoundPool == null) {
2318                Log.e(TAG, "Cannot play sound - they have not been loaded.");
2319                return;
2320            }
2321            if (newVal == 1) {
2322                mSoundPool.play(mBeepTwice, 1.0f, 1.0f, 0, 0, 1.0f);
2323            } else if (newVal == 2 || newVal == 3) {
2324                mSoundPool.play(mBeepOnce, 1.0f, 1.0f, 0, 0, 1.0f);
2325            }
2326        }
2327
2328        void release() {
2329            mSoundPool.release();
2330            mSoundPool = null;
2331        }
2332    }
2333}
2334