OSDN Git Service

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