OSDN Git Service

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