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