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.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         requestCameraOpen();
1459 
1460         mJpegPictureCallbackTime = 0;
1461         mZoomValue = 1.0f;
1462 
1463         mOnResumeTime = SystemClock.uptimeMillis();
1464         checkDisplayRotation();
1465 
1466         // If first time initialization is not finished, put it in the
1467         // message queue.
1468         if (!mFirstTimeInitialized) {
1469             mHandler.sendEmptyMessage(MSG_FIRST_TIME_INIT);
1470         } else {
1471             initializeSecondTime();
1472         }
1473 
1474         mHeadingSensor.activate();
1475 
1476         getServices().getRemoteShutterListener().onModuleReady(this);
1477         SessionStatsCollector.instance().sessionActive(true);
1478     }
1479 
1480     /**
1481      * @return Whether the currently active camera is front-facing.
1482      */
isCameraFrontFacing()1483     private boolean isCameraFrontFacing() {
1484         return mAppController.getCameraProvider().getCharacteristics(mCameraId)
1485                 .isFacingFront();
1486     }
1487 
1488     /**
1489      * The focus manager is the first UI related element to get initialized, and
1490      * it requires the RenderOverlay, so initialize it here
1491      */
initializeFocusManager()1492     private void initializeFocusManager() {
1493         // Create FocusManager object. startPreview needs it.
1494         // if mFocusManager not null, reuse it
1495         // otherwise create a new instance
1496         if (mFocusManager != null) {
1497             mFocusManager.removeMessages();
1498         } else {
1499             mMirror = isCameraFrontFacing();
1500             String[] defaultFocusModesStrings = mActivity.getResources().getStringArray(
1501                     R.array.pref_camera_focusmode_default_array);
1502             ArrayList<CameraCapabilities.FocusMode> defaultFocusModes =
1503                     new ArrayList<CameraCapabilities.FocusMode>();
1504             CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
1505             for (String modeString : defaultFocusModesStrings) {
1506                 CameraCapabilities.FocusMode mode = stringifier.focusModeFromString(modeString);
1507                 if (mode != null) {
1508                     defaultFocusModes.add(mode);
1509                 }
1510             }
1511             mFocusManager =
1512                     new FocusOverlayManager(mAppController, defaultFocusModes,
1513                             mCameraCapabilities, this, mMirror, mActivity.getMainLooper(),
1514                             mUI.getFocusRing());
1515             mMotionManager = getServices().getMotionManager();
1516             if (mMotionManager != null) {
1517                 mMotionManager.addListener(mFocusManager);
1518             }
1519         }
1520         mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
1521     }
1522 
1523     /**
1524      * @return Whether we are resuming from within the lockscreen.
1525      */
isResumeFromLockscreen()1526     private boolean isResumeFromLockscreen() {
1527         String action = mActivity.getIntent().getAction();
1528         return (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action)
1529                 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action));
1530     }
1531 
1532     @Override
pause()1533     public void pause() {
1534         Log.v(TAG, "pause");
1535         mPaused = true;
1536         getServices().getRemoteShutterListener().onModuleExit();
1537         SessionStatsCollector.instance().sessionActive(false);
1538 
1539         mHeadingSensor.deactivate();
1540 
1541         // Reset the focus first. Camera CTS does not guarantee that
1542         // cancelAutoFocus is allowed after preview stops.
1543         if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1544             mCameraDevice.cancelAutoFocus();
1545         }
1546 
1547         // If the camera has not been opened asynchronously yet,
1548         // and startPreview hasn't been called, then this is a no-op.
1549         // (e.g. onResume -> onPause -> onResume).
1550         stopPreview();
1551         cancelCountDown();
1552         mCountdownSoundPlayer.unloadSound(R.raw.timer_final_second);
1553         mCountdownSoundPlayer.unloadSound(R.raw.timer_increment);
1554 
1555         mNamedImages = null;
1556         // If we are in an image capture intent and has taken
1557         // a picture, we just clear it in onPause.
1558         mJpegImageData = null;
1559 
1560         // Remove the messages and runnables in the queue.
1561         mHandler.removeCallbacksAndMessages(null);
1562 
1563         if (mMotionManager != null) {
1564             mMotionManager.removeListener(mFocusManager);
1565             mMotionManager = null;
1566         }
1567 
1568         closeCamera();
1569         mActivity.enableKeepScreenOn(false);
1570         mUI.onPause();
1571 
1572         mPendingSwitchCameraId = -1;
1573         if (mFocusManager != null) {
1574             mFocusManager.removeMessages();
1575         }
1576         getServices().getMemoryManager().removeListener(this);
1577         mAppController.removePreviewAreaSizeChangedListener(mFocusManager);
1578         mAppController.removePreviewAreaSizeChangedListener(mUI);
1579 
1580         SettingsManager settingsManager = mActivity.getSettingsManager();
1581         settingsManager.removeListener(this);
1582     }
1583 
1584     @Override
destroy()1585     public void destroy() {
1586         mCountdownSoundPlayer.release();
1587     }
1588 
1589     @Override
onLayoutOrientationChanged(boolean isLandscape)1590     public void onLayoutOrientationChanged(boolean isLandscape) {
1591         setDisplayOrientation();
1592     }
1593 
1594     @Override
updateCameraOrientation()1595     public void updateCameraOrientation() {
1596         if (mDisplayRotation != CameraUtil.getDisplayRotation()) {
1597             setDisplayOrientation();
1598         }
1599     }
1600 
canTakePicture()1601     private boolean canTakePicture() {
1602         return isCameraIdle()
1603                 && (mActivity.getStorageSpaceBytes() > Storage.LOW_STORAGE_THRESHOLD_BYTES);
1604     }
1605 
1606     @Override
autoFocus()1607     public void autoFocus() {
1608         if (mCameraDevice == null) {
1609             return;
1610         }
1611         Log.v(TAG,"Starting auto focus");
1612         mFocusStartTime = System.currentTimeMillis();
1613         mCameraDevice.autoFocus(mHandler, mAutoFocusCallback);
1614         SessionStatsCollector.instance().autofocusManualTrigger();
1615         setCameraState(FOCUSING);
1616     }
1617 
1618     @Override
cancelAutoFocus()1619     public void cancelAutoFocus() {
1620         if (mCameraDevice == null) {
1621             return;
1622         }
1623         mCameraDevice.cancelAutoFocus();
1624         setCameraState(IDLE);
1625         setCameraParameters(UPDATE_PARAM_PREFERENCE);
1626     }
1627 
1628     @Override
onSingleTapUp(View view, int x, int y)1629     public void onSingleTapUp(View view, int x, int y) {
1630         if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1631                 || mCameraState == SNAPSHOT_IN_PROGRESS
1632                 || mCameraState == SWITCHING_CAMERA
1633                 || mCameraState == PREVIEW_STOPPED) {
1634             return;
1635         }
1636 
1637         // Check if metering area or focus area is supported.
1638         if (!mFocusAreaSupported && !mMeteringAreaSupported) {
1639             return;
1640         }
1641         mFocusManager.onSingleTapUp(x, y);
1642     }
1643 
1644     @Override
onBackPressed()1645     public boolean onBackPressed() {
1646         return mUI.onBackPressed();
1647     }
1648 
1649     @Override
onKeyDown(int keyCode, KeyEvent event)1650     public boolean onKeyDown(int keyCode, KeyEvent event) {
1651         switch (keyCode) {
1652             case KeyEvent.KEYCODE_VOLUME_UP:
1653             case KeyEvent.KEYCODE_VOLUME_DOWN:
1654             case KeyEvent.KEYCODE_FOCUS:
1655                 if (/* TODO: mActivity.isInCameraApp() && */mFirstTimeInitialized &&
1656                     !mActivity.getCameraAppUI().isInIntentReview()) {
1657                     if (event.getRepeatCount() == 0) {
1658                         onShutterButtonFocus(true);
1659                     }
1660                     return true;
1661                 }
1662                 return false;
1663             case KeyEvent.KEYCODE_CAMERA:
1664                 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1665                     onShutterButtonClick();
1666                 }
1667                 return true;
1668             case KeyEvent.KEYCODE_DPAD_CENTER:
1669                 // If we get a dpad center event without any focused view, move
1670                 // the focus to the shutter button and press it.
1671                 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1672                     // Start auto-focus immediately to reduce shutter lag. After
1673                     // the shutter button gets the focus, onShutterButtonFocus()
1674                     // will be called again but it is fine.
1675                     onShutterButtonFocus(true);
1676                 }
1677                 return true;
1678         }
1679         return false;
1680     }
1681 
1682     @Override
onKeyUp(int keyCode, KeyEvent event)1683     public boolean onKeyUp(int keyCode, KeyEvent event) {
1684         switch (keyCode) {
1685             case KeyEvent.KEYCODE_VOLUME_UP:
1686             case KeyEvent.KEYCODE_VOLUME_DOWN:
1687                 if (/* mActivity.isInCameraApp() && */mFirstTimeInitialized &&
1688                     !mActivity.getCameraAppUI().isInIntentReview()) {
1689                     if (mUI.isCountingDown()) {
1690                         cancelCountDown();
1691                     } else {
1692                         mVolumeButtonClickedFlag = true;
1693                         onShutterButtonClick();
1694                     }
1695                     return true;
1696                 }
1697                 return false;
1698             case KeyEvent.KEYCODE_FOCUS:
1699                 if (mFirstTimeInitialized) {
1700                     onShutterButtonFocus(false);
1701                 }
1702                 return true;
1703         }
1704         return false;
1705     }
1706 
closeCamera()1707     private void closeCamera() {
1708         if (mCameraDevice != null) {
1709             stopFaceDetection();
1710             mCameraDevice.setZoomChangeListener(null);
1711             mCameraDevice.setFaceDetectionCallback(null, null);
1712 
1713             mFaceDetectionStarted = false;
1714             mActivity.getCameraProvider().releaseCamera(mCameraDevice.getCameraId());
1715             mCameraDevice = null;
1716             setCameraState(PREVIEW_STOPPED);
1717             mFocusManager.onCameraReleased();
1718         }
1719     }
1720 
setDisplayOrientation()1721     private void setDisplayOrientation() {
1722         mDisplayRotation = CameraUtil.getDisplayRotation();
1723         Characteristics info =
1724                 mActivity.getCameraProvider().getCharacteristics(mCameraId);
1725         mDisplayOrientation = info.getPreviewOrientation(mDisplayRotation);
1726         mUI.setDisplayOrientation(mDisplayOrientation);
1727         if (mFocusManager != null) {
1728             mFocusManager.setDisplayOrientation(mDisplayOrientation);
1729         }
1730         // Change the camera display orientation
1731         if (mCameraDevice != null) {
1732             mCameraDevice.setDisplayOrientation(mDisplayRotation);
1733         }
1734         Log.v(TAG, "setDisplayOrientation (screen:preview) " +
1735                 mDisplayRotation + ":" + mDisplayOrientation);
1736     }
1737 
1738     /** Only called by UI thread. */
setupPreview()1739     private void setupPreview() {
1740         Log.i(TAG, "setupPreview");
1741         mFocusManager.resetTouchFocus();
1742         startPreview();
1743     }
1744 
1745     /**
1746      * Returns whether we can/should start the preview or not.
1747      */
checkPreviewPreconditions()1748     private boolean checkPreviewPreconditions() {
1749         if (mPaused) {
1750             return false;
1751         }
1752 
1753         if (mCameraDevice == null) {
1754             Log.w(TAG, "startPreview: camera device not ready yet.");
1755             return false;
1756         }
1757 
1758         SurfaceTexture st = mActivity.getCameraAppUI().getSurfaceTexture();
1759         if (st == null) {
1760             Log.w(TAG, "startPreview: surfaceTexture is not ready.");
1761             return false;
1762         }
1763 
1764         if (!mCameraPreviewParamsReady) {
1765             Log.w(TAG, "startPreview: parameters for preview is not ready.");
1766             return false;
1767         }
1768         return true;
1769     }
1770 
1771     /**
1772      * The start/stop preview should only run on the UI thread.
1773      */
startPreview()1774     private void startPreview() {
1775         if (mCameraDevice == null) {
1776             Log.i(TAG, "attempted to start preview before camera device");
1777             // do nothing
1778             return;
1779         }
1780 
1781         if (!checkPreviewPreconditions()) {
1782             return;
1783         }
1784 
1785         setDisplayOrientation();
1786 
1787         if (!mSnapshotOnIdle) {
1788             // If the focus mode is continuous autofocus, call cancelAutoFocus
1789             // to resume it because it may have been paused by autoFocus call.
1790             if (mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()) ==
1791                     CameraCapabilities.FocusMode.CONTINUOUS_PICTURE) {
1792                 mCameraDevice.cancelAutoFocus();
1793             }
1794             mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1795         }
1796 
1797         // Nexus 4 must have picture size set to > 640x480 before other
1798         // parameters are set in setCameraParameters, b/18227551. This call to
1799         // updateParametersPictureSize should occur before setCameraParameters
1800         // to address the issue.
1801         updateParametersPictureSize();
1802 
1803         setCameraParameters(UPDATE_PARAM_ALL);
1804 
1805         mCameraDevice.setPreviewTexture(mActivity.getCameraAppUI().getSurfaceTexture());
1806 
1807         Log.i(TAG, "startPreview");
1808         // If we're using API2 in portability layers, don't use startPreviewWithCallback()
1809         // b/17576554
1810         CameraAgent.CameraStartPreviewCallback startPreviewCallback =
1811             new CameraAgent.CameraStartPreviewCallback() {
1812                 @Override
1813                 public void onPreviewStarted() {
1814                     mFocusManager.onPreviewStarted();
1815                     PhotoModule.this.onPreviewStarted();
1816                     SessionStatsCollector.instance().previewActive(true);
1817                     if (mSnapshotOnIdle) {
1818                         mHandler.post(mDoSnapRunnable);
1819                     }
1820                 }
1821             };
1822         if (GservicesHelper.useCamera2ApiThroughPortabilityLayer(mActivity.getContentResolver())) {
1823             mCameraDevice.startPreview();
1824             startPreviewCallback.onPreviewStarted();
1825         } else {
1826             mCameraDevice.startPreviewWithCallback(new Handler(Looper.getMainLooper()),
1827                     startPreviewCallback);
1828         }
1829     }
1830 
1831     @Override
stopPreview()1832     public void stopPreview() {
1833         if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1834             Log.i(TAG, "stopPreview");
1835             mCameraDevice.stopPreview();
1836             mFaceDetectionStarted = false;
1837         }
1838         setCameraState(PREVIEW_STOPPED);
1839         if (mFocusManager != null) {
1840             mFocusManager.onPreviewStopped();
1841         }
1842         SessionStatsCollector.instance().previewActive(false);
1843     }
1844 
1845     @Override
onSettingChanged(SettingsManager settingsManager, String key)1846     public void onSettingChanged(SettingsManager settingsManager, String key) {
1847         if (key.equals(Keys.KEY_FLASH_MODE)) {
1848             updateParametersFlashMode();
1849         }
1850         if (key.equals(Keys.KEY_CAMERA_HDR)) {
1851             if (settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
1852                                            Keys.KEY_CAMERA_HDR)) {
1853                 // HDR is on.
1854                 mAppController.getButtonManager().disableButton(ButtonManager.BUTTON_FLASH);
1855                 mFlashModeBeforeSceneMode = settingsManager.getString(
1856                         mAppController.getCameraScope(), Keys.KEY_FLASH_MODE);
1857             } else {
1858                 if (mFlashModeBeforeSceneMode != null) {
1859                     settingsManager.set(mAppController.getCameraScope(),
1860                                         Keys.KEY_FLASH_MODE,
1861                                         mFlashModeBeforeSceneMode);
1862                     updateParametersFlashMode();
1863                     mFlashModeBeforeSceneMode = null;
1864                 }
1865                 mAppController.getButtonManager().enableButton(ButtonManager.BUTTON_FLASH);
1866             }
1867         }
1868 
1869         if (mCameraDevice != null) {
1870             mCameraDevice.applySettings(mCameraSettings);
1871         }
1872     }
1873 
updateCameraParametersInitialize()1874     private void updateCameraParametersInitialize() {
1875         // Reset preview frame rate to the maximum because it may be lowered by
1876         // video camera application.
1877         int[] fpsRange = CameraUtil.getPhotoPreviewFpsRange(mCameraCapabilities);
1878         if (fpsRange != null && fpsRange.length > 0) {
1879             mCameraSettings.setPreviewFpsRange(fpsRange[0], fpsRange[1]);
1880         }
1881 
1882         mCameraSettings.setRecordingHintEnabled(false);
1883 
1884         if (mCameraCapabilities.supports(CameraCapabilities.Feature.VIDEO_STABILIZATION)) {
1885             mCameraSettings.setVideoStabilization(false);
1886         }
1887     }
1888 
updateCameraParametersZoom()1889     private void updateCameraParametersZoom() {
1890         // Set zoom.
1891         if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
1892             mCameraSettings.setZoomRatio(mZoomValue);
1893         }
1894     }
1895 
1896     @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
setAutoExposureLockIfSupported()1897     private void setAutoExposureLockIfSupported() {
1898         if (mAeLockSupported) {
1899             mCameraSettings.setAutoExposureLock(mFocusManager.getAeAwbLock());
1900         }
1901     }
1902 
1903     @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
setAutoWhiteBalanceLockIfSupported()1904     private void setAutoWhiteBalanceLockIfSupported() {
1905         if (mAwbLockSupported) {
1906             mCameraSettings.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
1907         }
1908     }
1909 
setFocusAreasIfSupported()1910     private void setFocusAreasIfSupported() {
1911         if (mFocusAreaSupported) {
1912             mCameraSettings.setFocusAreas(mFocusManager.getFocusAreas());
1913         }
1914     }
1915 
setMeteringAreasIfSupported()1916     private void setMeteringAreasIfSupported() {
1917         if (mMeteringAreaSupported) {
1918             mCameraSettings.setMeteringAreas(mFocusManager.getMeteringAreas());
1919         }
1920     }
1921 
updateCameraParametersPreference()1922     private void updateCameraParametersPreference() {
1923         // some monkey tests can get here when shutting the app down
1924         // make sure mCameraDevice is still valid, b/17580046
1925         if (mCameraDevice == null) {
1926             return;
1927         }
1928 
1929         setAutoExposureLockIfSupported();
1930         setAutoWhiteBalanceLockIfSupported();
1931         setFocusAreasIfSupported();
1932         setMeteringAreasIfSupported();
1933 
1934         // Initialize focus mode.
1935         mFocusManager.overrideFocusMode(null);
1936         mCameraSettings
1937                 .setFocusMode(mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()));
1938         SessionStatsCollector.instance().autofocusActive(
1939                 mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()) ==
1940                         CameraCapabilities.FocusMode.CONTINUOUS_PICTURE
1941         );
1942 
1943         // Set JPEG quality.
1944         updateParametersPictureQuality();
1945 
1946         // For the following settings, we need to check if the settings are
1947         // still supported by latest driver, if not, ignore the settings.
1948 
1949         // Set exposure compensation
1950         updateParametersExposureCompensation();
1951 
1952         // Set the scene mode: also sets flash and white balance.
1953         updateParametersSceneMode();
1954 
1955         if (mContinuousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
1956             updateAutoFocusMoveCallback();
1957         }
1958     }
1959 
1960     /**
1961      * This method sets picture size parameters. Size parameters should only be
1962      * set when the preview is stopped, and so this method is only invoked in
1963      * {@link #startPreview()} just before starting the preview.
1964      */
updateParametersPictureSize()1965     private void updateParametersPictureSize() {
1966         if (mCameraDevice == null) {
1967             Log.w(TAG, "attempting to set picture size without caemra device");
1968             return;
1969         }
1970 
1971         List<Size> supported = Size.convert(mCameraCapabilities.getSupportedPhotoSizes());
1972         CameraPictureSizesCacher.updateSizesForCamera(mAppController.getAndroidContext(),
1973                 mCameraDevice.getCameraId(), supported);
1974 
1975         OneCamera.Facing cameraFacing =
1976               isCameraFrontFacing() ? OneCamera.Facing.FRONT : OneCamera.Facing.BACK;
1977         Size pictureSize;
1978         try {
1979             pictureSize = mAppController.getResolutionSetting().getPictureSize(
1980                   mAppController.getCameraProvider().getCurrentCameraId(),
1981                   cameraFacing);
1982         } catch (OneCameraAccessException ex) {
1983             mAppController.getFatalErrorHandler().onGenericCameraAccessFailure();
1984             return;
1985         }
1986 
1987         mCameraSettings.setPhotoSize(pictureSize.toPortabilitySize());
1988 
1989         if (ApiHelper.IS_NEXUS_5) {
1990             if (ResolutionUtil.NEXUS_5_LARGE_16_BY_9.equals(pictureSize)) {
1991                 mShouldResizeTo16x9 = true;
1992             } else {
1993                 mShouldResizeTo16x9 = false;
1994             }
1995         }
1996 
1997         // Set a preview size that is closest to the viewfinder height and has
1998         // the right aspect ratio.
1999         List<Size> sizes = Size.convert(mCameraCapabilities.getSupportedPreviewSizes());
2000         Size optimalSize = CameraUtil.getOptimalPreviewSize(sizes,
2001                 (double) pictureSize.width() / pictureSize.height());
2002         Size original = new Size(mCameraSettings.getCurrentPreviewSize());
2003         if (!optimalSize.equals(original)) {
2004             Log.v(TAG, "setting preview size. optimal: " + optimalSize + "original: " + original);
2005             mCameraSettings.setPreviewSize(optimalSize.toPortabilitySize());
2006 
2007             mCameraDevice.applySettings(mCameraSettings);
2008             mCameraSettings = mCameraDevice.getSettings();
2009         }
2010 
2011         if (optimalSize.width() != 0 && optimalSize.height() != 0) {
2012             Log.v(TAG, "updating aspect ratio");
2013             mUI.updatePreviewAspectRatio((float) optimalSize.width()
2014                     / (float) optimalSize.height());
2015         }
2016         Log.d(TAG, "Preview size is " + optimalSize);
2017     }
2018 
updateParametersPictureQuality()2019     private void updateParametersPictureQuality() {
2020         int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
2021                 CameraProfile.QUALITY_HIGH);
2022         mCameraSettings.setPhotoJpegCompressionQuality(jpegQuality);
2023     }
2024 
updateParametersExposureCompensation()2025     private void updateParametersExposureCompensation() {
2026         SettingsManager settingsManager = mActivity.getSettingsManager();
2027         if (settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
2028                                        Keys.KEY_EXPOSURE_COMPENSATION_ENABLED)) {
2029             int value = settingsManager.getInteger(mAppController.getCameraScope(),
2030                                                    Keys.KEY_EXPOSURE);
2031             int max = mCameraCapabilities.getMaxExposureCompensation();
2032             int min = mCameraCapabilities.getMinExposureCompensation();
2033             if (value >= min && value <= max) {
2034                 mCameraSettings.setExposureCompensationIndex(value);
2035             } else {
2036                 Log.w(TAG, "invalid exposure range: " + value);
2037             }
2038         } else {
2039             // If exposure compensation is not enabled, reset the exposure compensation value.
2040             setExposureCompensation(0);
2041         }
2042     }
2043 
updateParametersSceneMode()2044     private void updateParametersSceneMode() {
2045         CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
2046         SettingsManager settingsManager = mActivity.getSettingsManager();
2047 
2048         mSceneMode = stringifier.
2049             sceneModeFromString(settingsManager.getString(mAppController.getCameraScope(),
2050                                                           Keys.KEY_SCENE_MODE));
2051         if (mCameraCapabilities.supports(mSceneMode)) {
2052             if (mCameraSettings.getCurrentSceneMode() != mSceneMode) {
2053                 mCameraSettings.setSceneMode(mSceneMode);
2054 
2055                 // Setting scene mode will change the settings of flash mode,
2056                 // white balance, and focus mode. Here we read back the
2057                 // parameters, so we can know those settings.
2058                 mCameraDevice.applySettings(mCameraSettings);
2059                 mCameraSettings = mCameraDevice.getSettings();
2060             }
2061         } else {
2062             mSceneMode = mCameraSettings.getCurrentSceneMode();
2063             if (mSceneMode == null) {
2064                 mSceneMode = CameraCapabilities.SceneMode.AUTO;
2065             }
2066         }
2067 
2068         if (CameraCapabilities.SceneMode.AUTO == mSceneMode) {
2069             // Set flash mode.
2070             updateParametersFlashMode();
2071 
2072             // Set focus mode.
2073             mFocusManager.overrideFocusMode(null);
2074             mCameraSettings.setFocusMode(
2075                     mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()));
2076         } else {
2077             mFocusManager.overrideFocusMode(mCameraSettings.getCurrentFocusMode());
2078         }
2079     }
2080 
updateParametersFlashMode()2081     private void updateParametersFlashMode() {
2082         SettingsManager settingsManager = mActivity.getSettingsManager();
2083 
2084         CameraCapabilities.FlashMode flashMode = mCameraCapabilities.getStringifier()
2085             .flashModeFromString(settingsManager.getString(mAppController.getCameraScope(),
2086                                                            Keys.KEY_FLASH_MODE));
2087         if (mCameraCapabilities.supports(flashMode)) {
2088             mCameraSettings.setFlashMode(flashMode);
2089         }
2090     }
2091 
2092     @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
updateAutoFocusMoveCallback()2093     private void updateAutoFocusMoveCallback() {
2094         if (mCameraDevice == null) {
2095             return;
2096         }
2097         if (mCameraSettings.getCurrentFocusMode() ==
2098                 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE) {
2099             mCameraDevice.setAutoFocusMoveCallback(mHandler,
2100                     (CameraAFMoveCallback) mAutoFocusMoveCallback);
2101         } else {
2102             mCameraDevice.setAutoFocusMoveCallback(null, null);
2103         }
2104     }
2105 
2106     /**
2107      * Sets the exposure compensation to the given value and also updates settings.
2108      *
2109      * @param value exposure compensation value to be set
2110      */
setExposureCompensation(int value)2111     public void setExposureCompensation(int value) {
2112         int max = mCameraCapabilities.getMaxExposureCompensation();
2113         int min = mCameraCapabilities.getMinExposureCompensation();
2114         if (value >= min && value <= max) {
2115             mCameraSettings.setExposureCompensationIndex(value);
2116             SettingsManager settingsManager = mActivity.getSettingsManager();
2117             settingsManager.set(mAppController.getCameraScope(),
2118                                 Keys.KEY_EXPOSURE, value);
2119         } else {
2120             Log.w(TAG, "invalid exposure range: " + value);
2121         }
2122     }
2123 
2124     // We separate the parameters into several subsets, so we can update only
2125     // the subsets actually need updating. The PREFERENCE set needs extra
2126     // locking because the preference can be changed from GLThread as well.
setCameraParameters(int updateSet)2127     private void setCameraParameters(int updateSet) {
2128         if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
2129             updateCameraParametersInitialize();
2130         }
2131 
2132         if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
2133             updateCameraParametersZoom();
2134         }
2135 
2136         if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
2137             updateCameraParametersPreference();
2138         }
2139 
2140         if (mCameraDevice != null) {
2141             mCameraDevice.applySettings(mCameraSettings);
2142         }
2143     }
2144 
2145     // If the Camera is idle, update the parameters immediately, otherwise
2146     // accumulate them in mUpdateSet and update later.
setCameraParametersWhenIdle(int additionalUpdateSet)2147     private void setCameraParametersWhenIdle(int additionalUpdateSet) {
2148         mUpdateSet |= additionalUpdateSet;
2149         if (mCameraDevice == null) {
2150             // We will update all the parameters when we open the device, so
2151             // we don't need to do anything now.
2152             mUpdateSet = 0;
2153             return;
2154         } else if (isCameraIdle()) {
2155             setCameraParameters(mUpdateSet);
2156             updateSceneMode();
2157             mUpdateSet = 0;
2158         } else {
2159             if (!mHandler.hasMessages(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
2160                 mHandler.sendEmptyMessageDelayed(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
2161             }
2162         }
2163     }
2164 
2165     @Override
isCameraIdle()2166     public boolean isCameraIdle() {
2167         return (mCameraState == IDLE) ||
2168                 (mCameraState == PREVIEW_STOPPED) ||
2169                 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
2170                 && (mCameraState != SWITCHING_CAMERA));
2171     }
2172 
2173     @Override
isImageCaptureIntent()2174     public boolean isImageCaptureIntent() {
2175         String action = mActivity.getIntent().getAction();
2176         return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
2177         || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
2178     }
2179 
setupCaptureParams()2180     private void setupCaptureParams() {
2181         Bundle myExtras = mActivity.getIntent().getExtras();
2182         if (myExtras != null) {
2183             mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
2184             mCropValue = myExtras.getString("crop");
2185         }
2186     }
2187 
initializeCapabilities()2188     private void initializeCapabilities() {
2189         mCameraCapabilities = mCameraDevice.getCapabilities();
2190         mFocusAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA);
2191         mMeteringAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.METERING_AREA);
2192         mAeLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK);
2193         mAwbLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK);
2194         mContinuousFocusSupported =
2195                 mCameraCapabilities.supports(CameraCapabilities.FocusMode.CONTINUOUS_PICTURE);
2196     }
2197 
2198     @Override
onZoomChanged(float ratio)2199     public void onZoomChanged(float ratio) {
2200         // Not useful to change zoom value when the activity is paused.
2201         if (mPaused) {
2202             return;
2203         }
2204         mZoomValue = ratio;
2205         if (mCameraSettings == null || mCameraDevice == null) {
2206             return;
2207         }
2208         // Set zoom parameters asynchronously
2209         mCameraSettings.setZoomRatio(mZoomValue);
2210         mCameraDevice.applySettings(mCameraSettings);
2211     }
2212 
2213     @Override
getCameraState()2214     public int getCameraState() {
2215         return mCameraState;
2216     }
2217 
2218     @Override
onMemoryStateChanged(int state)2219     public void onMemoryStateChanged(int state) {
2220         mAppController.setShutterEnabled(state == MemoryManager.STATE_OK);
2221     }
2222 
2223     @Override
onLowMemory()2224     public void onLowMemory() {
2225         // Not much we can do in the photo module.
2226     }
2227 
2228     // For debugging only.
setDebugUri(Uri uri)2229     public void setDebugUri(Uri uri) {
2230         mDebugUri = uri;
2231     }
2232 
2233     // For debugging only.
saveToDebugUri(byte[] data)2234     private void saveToDebugUri(byte[] data) {
2235         if (mDebugUri != null) {
2236             OutputStream outputStream = null;
2237             try {
2238                 outputStream = mContentResolver.openOutputStream(mDebugUri);
2239                 outputStream.write(data);
2240                 outputStream.close();
2241             } catch (IOException e) {
2242                 Log.e(TAG, "Exception while writing debug jpeg file", e);
2243             } finally {
2244                 CameraUtil.closeSilently(outputStream);
2245             }
2246         }
2247     }
2248 
2249     @Override
onRemoteShutterPress()2250     public void onRemoteShutterPress() {
2251         mHandler.post(new Runnable() {
2252             @Override
2253             public void run() {
2254                 focusAndCapture();
2255             }
2256         });
2257     }
2258 }
2259