OSDN Git Service

Merge "Custom checkbox drawable for K devices" into ub-camera-haleakala
[android-x86/packages-apps-Camera2.git] / src / com / android / camera / CaptureModule.java
1 /*
2  * Copyright (C) 2014 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.content.Context;
20 import android.graphics.Matrix;
21 import android.graphics.Point;
22 import android.graphics.RectF;
23 import android.graphics.SurfaceTexture;
24 import android.location.Location;
25 import android.media.MediaActionSound;
26 import android.net.Uri;
27 import android.os.Handler;
28 import android.os.HandlerThread;
29 import android.os.SystemClock;
30 import android.view.GestureDetector;
31 import android.view.KeyEvent;
32 import android.view.MotionEvent;
33 import android.view.Surface;
34 import android.view.View;
35
36 import com.android.camera.app.AppController;
37 import com.android.camera.app.CameraAppUI;
38 import com.android.camera.app.CameraAppUI.BottomBarUISpec;
39 import com.android.camera.app.LocationManager;
40 import com.android.camera.app.OrientationManager.DeviceOrientation;
41 import com.android.camera.async.MainThread;
42 import com.android.camera.burst.BurstFacade;
43 import com.android.camera.burst.BurstFacadeFactory;
44 import com.android.camera.burst.BurstReadyStateChangeListener;
45 import com.android.camera.burst.OrientationLockController;
46 import com.android.camera.captureintent.PreviewTransformCalculator;
47 import com.android.camera.debug.DebugPropertyHelper;
48 import com.android.camera.debug.Log;
49 import com.android.camera.debug.Log.Tag;
50 import com.android.camera.hardware.HardwareSpec;
51 import com.android.camera.hardware.HeadingSensor;
52 import com.android.camera.module.ModuleController;
53 import com.android.camera.one.OneCamera;
54 import com.android.camera.one.OneCamera.AutoFocusState;
55 import com.android.camera.one.OneCamera.CaptureReadyCallback;
56 import com.android.camera.one.OneCamera.Facing;
57 import com.android.camera.one.OneCamera.OpenCallback;
58 import com.android.camera.one.OneCamera.PhotoCaptureParameters;
59 import com.android.camera.one.OneCameraAccessException;
60 import com.android.camera.one.OneCameraCaptureSetting;
61 import com.android.camera.one.OneCameraCharacteristics;
62 import com.android.camera.one.OneCameraManager;
63 import com.android.camera.one.v2.OneCameraManagerImpl;
64 import com.android.camera.one.v2.photo.ImageRotationCalculator;
65 import com.android.camera.one.v2.photo.ImageRotationCalculatorImpl;
66 import com.android.camera.remote.RemoteCameraModule;
67 import com.android.camera.session.CaptureSession;
68 import com.android.camera.settings.Keys;
69 import com.android.camera.settings.SettingsManager;
70 import com.android.camera.stats.UsageStatistics;
71 import com.android.camera.stats.profiler.Profile;
72 import com.android.camera.stats.profiler.Profiler;
73 import com.android.camera.stats.profiler.Profilers;
74 import com.android.camera.ui.CountDownView;
75 import com.android.camera.ui.PreviewStatusListener;
76 import com.android.camera.ui.TouchCoordinate;
77 import com.android.camera.ui.focus.FocusController;
78 import com.android.camera.ui.focus.FocusSound;
79 import com.android.camera.util.AndroidServices;
80 import com.android.camera.util.ApiHelper;
81 import com.android.camera.util.CameraUtil;
82 import com.android.camera.util.GcamHelper;
83 import com.android.camera.util.Size;
84 import com.android.camera2.R;
85 import com.android.ex.camera2.portability.CameraAgent.CameraProxy;
86
87 import java.util.concurrent.Semaphore;
88 import java.util.concurrent.TimeUnit;
89
90 /**
91  * New Capture module that is made to support photo and video capture on top of
92  * the OneCamera API, to transparently support GCam.
93  * <p>
94  * This has been a re-write with pieces taken and improved from GCamModule and
95  * PhotoModule, which are to be retired eventually.
96  * <p>
97  */
98 public class CaptureModule extends CameraModule implements
99         ModuleController,
100         CountDownView.OnCountDownStatusListener,
101         OneCamera.PictureCallback,
102         OneCamera.FocusStateListener,
103         OneCamera.ReadyStateChangedListener,
104         RemoteCameraModule {
105
106     private static final Tag TAG = new Tag("CaptureModule");
107     /** Enable additional debug output. */
108     private static final boolean DEBUG = true;
109     /** Workaround Flag for b/19271661 to use autotransformation in Capture Layout in Nexus4 **/
110     private static final boolean USE_AUTOTRANSFORM_UI_LAYOUT = ApiHelper.IS_NEXUS_4;
111
112     /** Timeout for camera open/close operations. */
113     private static final int CAMERA_OPEN_CLOSE_TIMEOUT_MILLIS = 2500;
114
115     /** System Properties switch to enable debugging focus UI. */
116     private static final boolean CAPTURE_DEBUG_UI = DebugPropertyHelper.showCaptureDebugUI();
117
118     private final Object mDimensionLock = new Object();
119
120     /**
121      * Sticky Gcam mode is when this module's sole purpose it to be the Gcam
122      * mode. If true, the device uses {@link PhotoModule} for normal picture
123      * taking.
124      */
125     private final boolean mStickyGcamCamera;
126
127     /** Controller giving us access to other services. */
128     private final AppController mAppController;
129     /** The applications settings manager. */
130     private final SettingsManager mSettingsManager;
131     /** Application context. */
132     private final Context mContext;
133     /** Module UI. */
134     private CaptureModuleUI mUI;
135     /** The camera manager used to open cameras. */
136     private OneCameraManager mCameraManager;
137     /** The currently opened camera device, or null if the camera is closed. */
138     private OneCamera mCamera;
139     /** The selected picture size. */
140     private Size mPictureSize;
141     /** Held when opening or closing the camera. */
142     private final Semaphore mCameraOpenCloseLock = new Semaphore(1);
143     /** The direction the currently opened camera is facing to. */
144     private Facing mCameraFacing;
145     /** Whether HDR Scene mode is currently enabled. */
146     private boolean mHdrSceneEnabled = false;
147     private boolean mHdrPlusEnabled = false;
148     private final Object mSurfaceTextureLock = new Object();
149
150     private FocusController mFocusController;
151     private OneCameraCharacteristics mCameraCharacteristics;
152     final private PreviewTransformCalculator mPreviewTransformCalculator;
153
154     /** The listener to listen events from the CaptureModuleUI. */
155     private final CaptureModuleUI.CaptureModuleUIListener mUIListener =
156             new CaptureModuleUI.CaptureModuleUIListener() {
157                 @Override
158                 public void onZoomRatioChanged(float zoomRatio) {
159                     mZoomValue = zoomRatio;
160                     if (mCamera != null) {
161                         mCamera.setZoom(zoomRatio);
162                     }
163                 }
164             };
165
166     /** The listener to respond preview area changes. */
167     private final PreviewStatusListener.PreviewAreaChangedListener mPreviewAreaChangedListener =
168             new PreviewStatusListener.PreviewAreaChangedListener() {
169                 @Override
170                 public void onPreviewAreaChanged(RectF previewArea) {
171                     mPreviewArea = previewArea;
172                     mFocusController.configurePreviewDimensions(previewArea);
173                 }
174             };
175
176     /** The listener to listen events from the preview. */
177     private final PreviewStatusListener mPreviewStatusListener = new PreviewStatusListener() {
178         @Override
179         public void onPreviewLayoutChanged(View v, int left, int top, int right,
180                 int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
181             int width = right - left;
182             int height = bottom - top;
183             updatePreviewTransform(width, height, false);
184         }
185
186         @Override
187         public boolean shouldAutoAdjustTransformMatrixOnLayout() {
188             return USE_AUTOTRANSFORM_UI_LAYOUT;
189         }
190
191         @Override
192         public void onPreviewFlipped() {
193             // Do nothing because when preview is flipped, TextureView will lay
194             // itself out again, which will then trigger a transform matrix
195             // update.
196         }
197
198         @Override
199         public GestureDetector.OnGestureListener getGestureListener() {
200             return new GestureDetector.SimpleOnGestureListener() {
201                 @Override
202                 public boolean onSingleTapUp(MotionEvent ev) {
203                     Point tapPoint = new Point((int) ev.getX(), (int) ev.getY());
204                     Log.v(TAG, "onSingleTapUpPreview location=" + tapPoint);
205                     // TODO: This should query actual capability.
206                     if (mCameraFacing == Facing.FRONT) {
207                         return false;
208                     }
209                     startActiveFocusAt(tapPoint.x, tapPoint.y);
210                     return true;
211                 }
212             };
213         }
214
215         @Override
216         public View.OnTouchListener getTouchListener() {
217             return null;
218         }
219
220         @Override
221         public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
222             Log.d(TAG, "onSurfaceTextureAvailable");
223             // Force to re-apply transform matrix here as a workaround for
224             // b/11168275
225             updatePreviewTransform(width, height, true);
226             synchronized (mSurfaceTextureLock) {
227                 mPreviewSurfaceTexture = surface;
228             }
229             reopenCamera();
230         }
231
232         @Override
233         public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
234             Log.d(TAG, "onSurfaceTextureDestroyed");
235             synchronized (mSurfaceTextureLock) {
236                 mPreviewSurfaceTexture = null;
237             }
238             closeCamera();
239             return true;
240         }
241
242         @Override
243         public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
244             Log.d(TAG, "onSurfaceTextureSizeChanged");
245             updatePreviewBufferSize();
246         }
247
248         @Override
249         public void onSurfaceTextureUpdated(SurfaceTexture surface) {
250             if (mState == ModuleState.UPDATE_TRANSFORM_ON_NEXT_SURFACE_TEXTURE_UPDATE) {
251                 Log.d(TAG, "onSurfaceTextureUpdated --> updatePreviewTransform");
252                 mState = ModuleState.IDLE;
253                 CameraAppUI appUI = mAppController.getCameraAppUI();
254                 updatePreviewTransform(appUI.getSurfaceWidth(), appUI.getSurfaceHeight(), true);
255             }
256         }
257     };
258
259     private final OneCamera.PictureSaverCallback mPictureSaverCallback =
260             new OneCamera.PictureSaverCallback() {
261                 @Override
262                 public void onRemoteThumbnailAvailable(final byte[] jpegImage) {
263                     mMainThread.execute(new Runnable() {
264                         @Override
265                         public void run() {
266                             mAppController.getServices().getRemoteShutterListener()
267                                     .onPictureTaken(jpegImage);
268                         }
269                     });
270                 }
271             };
272
273     /** State by the module state machine. */
274     private static enum ModuleState {
275         IDLE,
276         WATCH_FOR_NEXT_FRAME_AFTER_PREVIEW_STARTED,
277         UPDATE_TRANSFORM_ON_NEXT_SURFACE_TEXTURE_UPDATE,
278     }
279
280     /** The current state of the module. */
281     private ModuleState mState = ModuleState.IDLE;
282     /** Current zoom value. */
283     private float mZoomValue = 1f;
284
285     /** Records beginning frame of each AF scan. */
286     private long mAutoFocusScanStartFrame = -1;
287     /** Records beginning time of each AF scan in uptimeMillis. */
288     private long mAutoFocusScanStartTime;
289
290     /** Heading sensor. */
291     private HeadingSensor mHeadingSensor;
292
293     /** Used to fetch and embed the location into captured images. */
294     private final LocationManager mLocationManager;
295     /** Plays sounds for countdown timer. */
296     private SoundPlayer mSoundPlayer;
297     private final MediaActionSound mMediaActionSound;
298
299     /** Whether the module is paused right now. */
300     private boolean mPaused;
301
302     /** Main thread. */
303     private final MainThread mMainThread;
304     /** Handler thread for camera-related operations. */
305     private Handler mCameraHandler;
306
307     /** Current display rotation in degrees. */
308     private int mDisplayRotation;
309     /** Current screen width in pixels. */
310     private int mScreenWidth;
311     /** Current screen height in pixels. */
312     private int mScreenHeight;
313     /** Current width of preview frames from camera. */
314     private int mPreviewBufferWidth;
315     /** Current height of preview frames from camera.. */
316     private int mPreviewBufferHeight;
317     /** Area used by preview. */
318     RectF mPreviewArea;
319
320     /** The surface texture for the preview. */
321     private SurfaceTexture mPreviewSurfaceTexture;
322
323     /** The burst manager for controlling the burst. */
324     private final BurstFacade mBurstController;
325     private static final String BURST_SESSIONS_DIR = "burst_sessions";
326
327     private final Profiler mProfiler = Profilers.instance().guard();
328
329     public CaptureModule(AppController appController) {
330         this(appController, false);
331     }
332
333     /** Constructs a new capture module. */
334     public CaptureModule(AppController appController, boolean stickyHdr) {
335         super(appController);
336         Profile guard = mProfiler.create("new CaptureModule").start();
337         mPaused = true;
338         mMainThread = MainThread.create();
339         mAppController = appController;
340         mContext = mAppController.getAndroidContext();
341         mSettingsManager = mAppController.getSettingsManager();
342         mStickyGcamCamera = stickyHdr;
343         mLocationManager = mAppController.getLocationManager();
344         mPreviewTransformCalculator = new PreviewTransformCalculator(
345                 mAppController.getOrientationManager());
346
347         mBurstController = BurstFacadeFactory.create(mContext,
348                 new OrientationLockController() {
349                     @Override
350                     public void unlockOrientation() {
351                         mAppController.getOrientationManager().unlockOrientation();
352                     }
353
354                         @Override
355                     public void lockOrientation() {
356                         mAppController.getOrientationManager().lockOrientation();
357                     }
358                 },
359                 new BurstReadyStateChangeListener() {
360                    @Override
361                     public void onBurstReadyStateChanged(boolean ready) {
362                         // TODO: This needs to take into account the state of
363                         // the whole system, not just burst.
364                         mAppController.setShutterEnabled(ready);
365                     }
366                 });
367         mMediaActionSound = new MediaActionSound();
368         guard.stop();
369     }
370
371     private void updateCameraCharacteristics() {
372         try {
373             mCameraCharacteristics = mCameraManager.getCameraCharacteristics(mCameraFacing);
374         } catch (OneCameraAccessException ocae) {
375             mAppController.showErrorAndFinish(R.string.cannot_connect_camera);
376             return;
377         }
378     }
379
380     @Override
381     public void init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent) {
382         Profile guard = mProfiler.create("CaptureModule.init").start();
383         Log.d(TAG, "init UseAutotransformUiLayout = " + USE_AUTOTRANSFORM_UI_LAYOUT);
384         HandlerThread thread = new HandlerThread("CaptureModule.mCameraHandler");
385         thread.start();
386         mCameraHandler = new Handler(thread.getLooper());
387         mCameraManager = mAppController.getCameraManager();
388         mDisplayRotation = CameraUtil.getDisplayRotation();
389         mCameraFacing = getFacingFromCameraId(
390               mSettingsManager.getInteger(mAppController.getModuleScope(), Keys.KEY_CAMERA_ID));
391         updateCameraCharacteristics();
392         mUI = new CaptureModuleUI(activity, mAppController.getModuleLayoutRoot(), mUIListener);
393         mAppController.setPreviewStatusListener(mPreviewStatusListener);
394         synchronized (mSurfaceTextureLock) {
395             mPreviewSurfaceTexture = mAppController.getCameraAppUI().getSurfaceTexture();
396         }
397         mSoundPlayer = new SoundPlayer(mContext);
398
399         FocusSound focusSound = new FocusSound(mSoundPlayer, R.raw.material_camera_focus);
400         mFocusController = new FocusController(mUI.getFocusRing(), focusSound, mMainThread);
401
402         mHeadingSensor = new HeadingSensor(AndroidServices.instance().provideSensorManager());
403
404         View cancelButton = activity.findViewById(R.id.shutter_cancel_button);
405         cancelButton.setOnClickListener(new View.OnClickListener() {
406             @Override
407             public void onClick(View view) {
408                 cancelCountDown();
409             }
410         });
411
412         mMediaActionSound.load(MediaActionSound.SHUTTER_CLICK);
413         guard.stop();
414     }
415
416     @Override
417     public void onShutterButtonLongPressed() {
418         try {
419             OneCameraCharacteristics cameraCharacteristics;
420             cameraCharacteristics = mCameraManager.getCameraCharacteristics(mCameraFacing);
421             DeviceOrientation deviceOrientation = mAppController.getOrientationManager()
422                     .getDeviceOrientation();
423             ImageRotationCalculator imageRotationCalculator = ImageRotationCalculatorImpl
424                     .from(mAppController.getOrientationManager(), cameraCharacteristics);
425
426             mBurstController.startBurst(
427                     new CaptureSession.CaptureSessionCreator() {
428                         @Override
429                         public CaptureSession createAndStartEmpty() {
430                             return createAndStartEmpty();
431                         }
432                     },
433                     deviceOrientation,
434                     mCamera.getDirection(),
435                     imageRotationCalculator.toImageRotation().getDegrees());
436
437         } catch (OneCameraAccessException e) {
438             Log.e(TAG, "Cannot start burst", e);
439             return;
440         }
441     }
442
443     @Override
444     public void onShutterButtonFocus(boolean pressed) {
445         if (!pressed) {
446             // the shutter button was released, stop any bursts.
447             mBurstController.stopBurst();
448         }
449     }
450
451     @Override
452     public void onShutterCoordinate(TouchCoordinate coord) {
453         // TODO Auto-generated method stub
454     }
455
456     @Override
457     public void onShutterButtonClick() {
458         if (mCamera == null) {
459             return;
460         }
461
462         int countDownDuration = mSettingsManager
463                 .getInteger(SettingsManager.SCOPE_GLOBAL, Keys.KEY_COUNTDOWN_DURATION);
464         if (countDownDuration > 0) {
465             // Start count down.
466             mAppController.getCameraAppUI().transitionToCancel();
467             mAppController.getCameraAppUI().hideModeOptions();
468             mUI.setCountdownFinishedListener(this);
469             mUI.startCountdown(countDownDuration);
470             // Will take picture later via listener callback.
471         } else {
472             takePictureNow();
473         }
474     }
475
476     private void takePictureNow() {
477         CaptureSession session = createAndStartCaptureSession();
478         int orientation = mAppController.getOrientationManager().getDeviceOrientation()
479                 .getDegrees();
480
481         // TODO: This should really not use getExternalCacheDir and instead use
482         // the SessionStorage API. Need to sync with gcam if that's OK.
483         PhotoCaptureParameters params = new PhotoCaptureParameters(
484                 session.getTitle(), orientation, session.getLocation(),
485                 mContext.getExternalCacheDir(), this, mPictureSaverCallback,
486                 mHeadingSensor.getCurrentHeading(), mZoomValue, 0);
487         mCamera.takePicture(params, session);
488     }
489
490     /**
491      * Creates, starts and returns a new capture session. The returned session
492      * will have been started with an empty placeholder image.
493      */
494     private CaptureSession createAndStartCaptureSession() {
495         long sessionTime = getSessionTime();
496         Location location = mLocationManager.getCurrentLocation();
497         String title = CameraUtil.instance().createJpegName(sessionTime);
498         CaptureSession session = getServices().getCaptureSessionManager()
499                 .createNewSession(title, sessionTime, location);
500         session.startEmpty(new Size((int) mPreviewArea.width(), (int) mPreviewArea.height()));
501         return session;
502     }
503
504     private long getSessionTime() {
505         // TODO: Replace with a mockable TimeProvider interface.
506         return System.currentTimeMillis();
507     }
508
509     @Override
510     public void onCountDownFinished() {
511         mAppController.getCameraAppUI().transitionToCapture();
512         mAppController.getCameraAppUI().showModeOptions();
513         if (mPaused) {
514             return;
515         }
516         takePictureNow();
517     }
518
519     @Override
520     public void onRemainingSecondsChanged(int remainingSeconds) {
521         if (remainingSeconds == 1) {
522             mSoundPlayer.play(R.raw.timer_final_second, 0.6f);
523         } else if (remainingSeconds == 2 || remainingSeconds == 3) {
524             mSoundPlayer.play(R.raw.timer_increment, 0.6f);
525         }
526     }
527
528     private void cancelCountDown() {
529         if (mUI.isCountingDown()) {
530             // Cancel on-going countdown.
531             mUI.cancelCountDown();
532         }
533         mAppController.getCameraAppUI().showModeOptions();
534         mAppController.getCameraAppUI().transitionToCapture();
535     }
536
537     @Override
538     public void onQuickExpose() {
539         mMainThread.execute(new Runnable() {
540             @Override
541             public void run() {
542                 // Starts the short version of the capture animation UI.
543                 mAppController.startFlashAnimation(true);
544                 mMediaActionSound.play(MediaActionSound.SHUTTER_CLICK);
545             }
546         });
547     }
548
549     @Override
550     public void onRemoteShutterPress() {
551         Log.d(TAG, "onRemoteShutterPress");
552         // TODO: Check whether shutter is enabled.
553         takePictureNow();
554     }
555
556     private void initSurfaceTextureConsumer() {
557         synchronized (mSurfaceTextureLock) {
558             if (mPreviewSurfaceTexture != null) {
559                 mPreviewSurfaceTexture.setDefaultBufferSize(
560                         mAppController.getCameraAppUI().getSurfaceWidth(),
561                         mAppController.getCameraAppUI().getSurfaceHeight());
562             }
563         }
564         reopenCamera();
565     }
566
567     private void reopenCamera() {
568         if (mPaused) {
569             return;
570         }
571         closeCamera();
572         openCameraAndStartPreview();
573     }
574
575     private SurfaceTexture getPreviewSurfaceTexture() {
576         synchronized (mSurfaceTextureLock) {
577             return mPreviewSurfaceTexture;
578         }
579     }
580
581     private void updatePreviewBufferSize() {
582         synchronized (mSurfaceTextureLock) {
583             if (mPreviewSurfaceTexture != null) {
584                 mPreviewSurfaceTexture.setDefaultBufferSize(mPreviewBufferWidth,
585                         mPreviewBufferHeight);
586             }
587         }
588     }
589
590     @Override
591     public void resume() {
592         Profile guard = mProfiler.create("CaptureModule.resume").start();
593
594         // We'll transition into 'ready' once the preview is started.
595         onReadyStateChanged(false);
596         mPaused = false;
597         mAppController.addPreviewAreaSizeChangedListener(mPreviewAreaChangedListener);
598         mAppController.addPreviewAreaSizeChangedListener(mUI);
599
600         mAppController.getCameraAppUI().onChangeCamera();
601
602         guard.mark();
603         getServices().getRemoteShutterListener().onModuleReady(this);
604         guard.mark("getRemoteShutterListener.onModuleReady");
605         mBurstController.initialize(new SurfaceTexture(0));
606
607         // TODO: Check if we can really take a photo right now (memory, camera
608         // state, ... ).
609         mAppController.getCameraAppUI().enableModeOptions();
610         mAppController.setShutterEnabled(true);
611         mAppController.getCameraAppUI().showAccessibilityZoomUI(
612                 mCameraCharacteristics.getAvailableMaxDigitalZoom());
613
614         mHdrPlusEnabled = mStickyGcamCamera || mAppController.getSettingsManager().getInteger(
615                 SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR_PLUS) == 1;
616
617         mHdrSceneEnabled = !mStickyGcamCamera && mAppController.getSettingsManager().getBoolean(
618               SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR);
619
620         // The lock only exists for HDR and causes trouble for non-HDR
621         // OneCameras.
622         // TODO: Fix for removing the locks completely is tracked at b/17985028
623         if (!mHdrPlusEnabled) {
624             mCameraOpenCloseLock.release();
625         }
626
627         // This means we are resuming with an existing preview texture. This
628         // means we will never get the onSurfaceTextureAvailable call. So we
629         // have to open the camera and start the preview here.
630         SurfaceTexture texture = getPreviewSurfaceTexture();
631
632         guard.mark();
633         if (texture != null) {
634             initSurfaceTextureConsumer();
635             guard.mark("initSurfaceTextureConsumer");
636         }
637
638         mSoundPlayer.loadSound(R.raw.timer_final_second);
639         mSoundPlayer.loadSound(R.raw.timer_increment);
640
641         guard.mark();
642         mHeadingSensor.activate();
643         guard.stop("mHeadingSensor.activate()");
644     }
645
646     @Override
647     public void pause() {
648         mPaused = true;
649         mHeadingSensor.deactivate();
650
651         mAppController.removePreviewAreaSizeChangedListener(mUI);
652         mAppController.removePreviewAreaSizeChangedListener(mPreviewAreaChangedListener);
653         getServices().getRemoteShutterListener().onModuleExit();
654         mBurstController.release();
655         cancelCountDown();
656         closeCamera();
657         resetTextureBufferSize();
658         mSoundPlayer.unloadSound(R.raw.timer_final_second);
659         mSoundPlayer.unloadSound(R.raw.timer_increment);
660     }
661
662     @Override
663     public void destroy() {
664         mSoundPlayer.release();
665         mMediaActionSound.release();
666         mCameraHandler.getLooper().quitSafely();
667     }
668
669     @Override
670     public void onLayoutOrientationChanged(boolean isLandscape) {
671         Log.d(TAG, "onLayoutOrientationChanged");
672     }
673
674     @Override
675     public void onCameraAvailable(CameraProxy cameraProxy) {
676         // Ignore since we manage the camera ourselves until we remove this.
677     }
678
679     @Override
680     public void hardResetSettings(SettingsManager settingsManager) {
681         if (mStickyGcamCamera) {
682             // Sticky HDR+ mode should hard reset HDR+ to on, and camera back
683             // facing.
684             settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR_PLUS, true);
685             settingsManager.set(mAppController.getModuleScope(), Keys.KEY_CAMERA_ID,
686                     getBackFacingCameraId());
687         }
688     }
689
690     @Override
691     public HardwareSpec getHardwareSpec() {
692         return new HardwareSpec() {
693             @Override
694             public boolean isFrontCameraSupported() {
695                 return true;
696             }
697
698             @Override
699             public boolean isHdrSupported() {
700                 return mCameraCharacteristics.isHdrSceneSupported();
701             }
702
703             @Override
704             public boolean isHdrPlusSupported() {
705                 return GcamHelper.hasGcamCapture(mAppController.getCameraFeatureConfig());
706             }
707
708             @Override
709             public boolean isFlashSupported() {
710                 return mCameraCharacteristics.isFlashSupported();
711             }
712         };
713     }
714
715     @Override
716     public BottomBarUISpec getBottomBarSpec() {
717         HardwareSpec hardwareSpec = getHardwareSpec();
718         BottomBarUISpec bottomBarSpec = new BottomBarUISpec();
719         bottomBarSpec.enableGridLines = true;
720         bottomBarSpec.enableCamera = true;
721         bottomBarSpec.cameraCallback = getCameraCallback();
722         bottomBarSpec.enableHdr = hardwareSpec.isHdrSupported() || hardwareSpec.isHdrPlusSupported();
723         bottomBarSpec.hdrCallback = getHdrButtonCallback();
724         bottomBarSpec.enableSelfTimer = true;
725         bottomBarSpec.showSelfTimer = true;
726
727         // We must read the key from the settings because the button callback
728         // is not executed until after this method is called.
729         if ((hardwareSpec.isHdrPlusSupported() &&
730                 mAppController.getSettingsManager().getBoolean(
731                 SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR_PLUS)) ||
732               ( hardwareSpec.isHdrSupported() &&
733                 mAppController.getSettingsManager().getBoolean(
734                 SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR))) {
735             // Disable flash if this is a sticky gcam camera, or if
736             // HDR is enabled.
737             bottomBarSpec.enableFlash = false;
738         } else {
739             // If we are not in HDR / GCAM mode, fallback on the
740             // flash supported property for this camera.
741             bottomBarSpec.enableFlash = mCameraCharacteristics.isFlashSupported();
742         }
743
744         bottomBarSpec.enableExposureCompensation =
745                 mCameraCharacteristics.isExposureCompensationSupported();
746         bottomBarSpec.minExposureCompensation =
747                 mCameraCharacteristics.getMinExposureCompensation();
748         bottomBarSpec.maxExposureCompensation =
749                 mCameraCharacteristics.getMaxExposureCompensation();
750         bottomBarSpec.exposureCompensationStep =
751                 mCameraCharacteristics.getExposureCompensationStep();
752         bottomBarSpec.exposureCompensationSetCallback =
753                 new BottomBarUISpec.ExposureCompensationSetCallback() {
754                     @Override
755                     public void setExposure(int value) {
756                         mSettingsManager.set(
757                                 mAppController.getCameraScope(), Keys.KEY_EXPOSURE, value);
758                     }
759                 };
760
761         return bottomBarSpec;
762     }
763
764     @Override
765     public boolean isUsingBottomBar() {
766         return true;
767     }
768
769     @Override
770     public boolean onKeyDown(int keyCode, KeyEvent event) {
771         switch (keyCode) {
772             case KeyEvent.KEYCODE_CAMERA:
773             case KeyEvent.KEYCODE_DPAD_CENTER:
774                 if (mUI.isCountingDown()) {
775                     cancelCountDown();
776                 } else if (event.getRepeatCount() == 0) {
777                     onShutterButtonClick();
778                 }
779                 return true;
780             case KeyEvent.KEYCODE_VOLUME_UP:
781             case KeyEvent.KEYCODE_VOLUME_DOWN:
782                 // Prevent default.
783                 return true;
784         }
785         return false;
786     }
787
788     @Override
789     public boolean onKeyUp(int keyCode, KeyEvent event) {
790         switch (keyCode) {
791             case KeyEvent.KEYCODE_VOLUME_UP:
792             case KeyEvent.KEYCODE_VOLUME_DOWN:
793                 onShutterButtonClick();
794                 return true;
795         }
796         return false;
797     }
798
799     // TODO: Consider refactoring FocusOverlayManager.
800     // Currently AF state transitions are controlled in OneCameraImpl.
801     // PhotoModule uses FocusOverlayManager which uses API1/portability
802     // logic and coordinates.
803     private void startActiveFocusAt(int viewX, int viewY) {
804         if (mCamera == null) {
805             // If we receive this after the camera is closed, do nothing.
806             return;
807         }
808
809         // TODO: make mFocusController final and remove null check.
810         if (mFocusController == null) {
811             Log.v(TAG, "CaptureModule mFocusController is null!");
812             return;
813         }
814         mFocusController.showActiveFocusAt(viewX, viewY);
815
816         // Normalize coordinates to [0,1] per CameraOne API.
817         float points[] = new float[2];
818         points[0] = (viewX - mPreviewArea.left) / mPreviewArea.width();
819         points[1] = (viewY - mPreviewArea.top) / mPreviewArea.height();
820
821         // Rotate coordinates to portrait orientation per CameraOne API.
822         Matrix rotationMatrix = new Matrix();
823         rotationMatrix.setRotate(mDisplayRotation, 0.5f, 0.5f);
824         rotationMatrix.mapPoints(points);
825         mCamera.triggerFocusAndMeterAtPoint(points[0], points[1]);
826
827         // Log touch (screen coordinates).
828         if (mZoomValue == 1f) {
829             TouchCoordinate touchCoordinate = new TouchCoordinate(
830                     viewX - mPreviewArea.left,
831                     viewY - mPreviewArea.top,
832                     mPreviewArea.width(),
833                     mPreviewArea.height());
834             // TODO: Add to logging: duration, rotation.
835             UsageStatistics.instance().tapToFocus(touchCoordinate, null);
836         }
837     }
838
839     /**
840      * Show AF target in center of preview.
841      */
842     private void startPassiveFocus() {
843         // TODO: make mFocusController final and remove null check.
844         if (mFocusController == null) {
845             return;
846         }
847
848         // TODO: Some passive focus scans may trigger on a location
849         // instead of the center of the screen.
850         mFocusController.showPassiveFocusAtCenter();
851     }
852
853     /**
854      * Update UI based on AF state changes.
855      */
856     @Override
857     public void onFocusStatusUpdate(final AutoFocusState state, long frameNumber) {
858         Log.v(TAG, "AF status is state:" + state);
859
860         switch (state) {
861             case PASSIVE_SCAN:
862                 startPassiveFocus();
863                 break;
864             case ACTIVE_SCAN:
865                 // Unused, manual scans are triggered via the UI
866                 break;
867             case PASSIVE_FOCUSED:
868             case PASSIVE_UNFOCUSED:
869                 // Unused
870                 break;
871             case ACTIVE_FOCUSED:
872             case ACTIVE_UNFOCUSED:
873                 // Unused
874                 break;
875         }
876
877         if (CAPTURE_DEBUG_UI) {
878             measureAutoFocusScans(state, frameNumber);
879         }
880     }
881
882     private void measureAutoFocusScans(final AutoFocusState state, long frameNumber) {
883         // Log AF scan lengths.
884         boolean passive = false;
885         switch (state) {
886             case PASSIVE_SCAN:
887             case ACTIVE_SCAN:
888                 if (mAutoFocusScanStartFrame == -1) {
889                     mAutoFocusScanStartFrame = frameNumber;
890                     mAutoFocusScanStartTime = SystemClock.uptimeMillis();
891                 }
892                 break;
893             case PASSIVE_FOCUSED:
894             case PASSIVE_UNFOCUSED:
895                 passive = true;
896             case ACTIVE_FOCUSED:
897             case ACTIVE_UNFOCUSED:
898                 if (mAutoFocusScanStartFrame != -1) {
899                     long frames = frameNumber - mAutoFocusScanStartFrame;
900                     long dt = SystemClock.uptimeMillis() - mAutoFocusScanStartTime;
901                     int fps = Math.round(frames * 1000f / dt);
902                     String report = String.format("%s scan: fps=%d frames=%d",
903                             passive ? "CAF" : "AF", fps, frames);
904                     Log.v(TAG, report);
905                     mUI.showDebugMessage(String.format("%d / %d", frames, fps));
906                     mAutoFocusScanStartFrame = -1;
907                 }
908                 break;
909         }
910     }
911
912     @Override
913     public void onReadyStateChanged(boolean readyForCapture) {
914         if (!mBurstController.isReady()) {
915             return;
916         }
917
918         if (readyForCapture) {
919             mAppController.getCameraAppUI().enableModeOptions();
920         }
921         mAppController.setShutterEnabled(readyForCapture);
922     }
923
924     @Override
925     public String getPeekAccessibilityString() {
926         return mAppController.getAndroidContext()
927                 .getResources().getString(R.string.photo_accessibility_peek);
928     }
929
930     @Override
931     public void onThumbnailResult(byte[] jpegData) {
932         getServices().getRemoteShutterListener().onPictureTaken(jpegData);
933     }
934
935     @Override
936     public void onPictureTaken(CaptureSession session) {
937         mAppController.getCameraAppUI().enableModeOptions();
938     }
939
940     @Override
941     public void onPictureSaved(Uri uri) {
942         mAppController.notifyNewMedia(uri);
943     }
944
945     @Override
946     public void onTakePictureProgress(float progress) {
947         mUI.setPictureTakingProgress((int) (progress * 100));
948     }
949
950     @Override
951     public void onPictureTakingFailed() {
952     }
953
954     /**
955      * Updates the preview transform matrix to adapt to the current preview
956      * width, height, and orientation.
957      */
958     public void updatePreviewTransform() {
959         int width;
960         int height;
961         synchronized (mDimensionLock) {
962             width = mScreenWidth;
963             height = mScreenHeight;
964         }
965         updatePreviewTransform(width, height);
966     }
967
968     /**
969      * TODO: Remove this method once we are in pure CaptureModule land.
970      */
971     private String getBackFacingCameraId() {
972         if (!(mCameraManager instanceof OneCameraManagerImpl)) {
973             throw new IllegalStateException("This should never be called with Camera API V1");
974         }
975         OneCameraManagerImpl manager = (OneCameraManagerImpl) mCameraManager;
976         return manager.getFirstBackCameraId();
977     }
978
979     /**
980      * @return Depending on whether we're in sticky-HDR mode or not, return the
981      *         proper callback to be used for when the HDR/HDR+ button is
982      *         pressed.
983      */
984     private ButtonManager.ButtonCallback getHdrButtonCallback() {
985         if (mStickyGcamCamera) {
986             return new ButtonManager.ButtonCallback() {
987                 @Override
988                 public void onStateChanged(int state) {
989                     if (mPaused) {
990                         return;
991                     }
992                     if (state == ButtonManager.ON) {
993                         throw new IllegalStateException(
994                                 "Can't leave hdr plus mode if switching to hdr plus mode.");
995                     }
996                     SettingsManager settingsManager = mAppController.getSettingsManager();
997                     settingsManager.set(mAppController.getModuleScope(),
998                             Keys.KEY_REQUEST_RETURN_HDR_PLUS, false);
999                     switchToRegularCapture();
1000                 }
1001             };
1002         } else {
1003             return new ButtonManager.ButtonCallback() {
1004                 @Override
1005                 public void onStateChanged(int hdrEnabled) {
1006                     if (mPaused) {
1007                         return;
1008                     }
1009
1010                     // Only reload the camera if we are toggling HDR+.
1011                     if (GcamHelper.hasGcamCapture(mAppController.getCameraFeatureConfig())) {
1012                         mHdrPlusEnabled = hdrEnabled == 1;
1013                         switchCamera();
1014                     } else {
1015                         mHdrSceneEnabled = hdrEnabled == 1;
1016                     }
1017                 }
1018             };
1019         }
1020     }
1021
1022     /**
1023      * @return Depending on whether we're in sticky-HDR mode or not, this
1024      *         returns the proper callback to be used for when the camera
1025      *         (front/back switch) button is pressed.
1026      */
1027     private ButtonManager.ButtonCallback getCameraCallback() {
1028         if (mStickyGcamCamera) {
1029             return new ButtonManager.ButtonCallback() {
1030                 @Override
1031                 public void onStateChanged(int state) {
1032                     if (mPaused) {
1033                         return;
1034                     }
1035
1036                     // At the time this callback is fired, the camera id setting
1037                     // has changed to the desired camera.
1038                     SettingsManager settingsManager = mAppController.getSettingsManager();
1039                     if (Keys.isCameraBackFacing(settingsManager,
1040                             mAppController.getModuleScope())) {
1041                         throw new IllegalStateException(
1042                                 "Hdr plus should never be switching from front facing camera.");
1043                     }
1044
1045                     // Switch to photo mode, but request a return to hdr plus on
1046                     // switching to back camera again.
1047                     settingsManager.set(mAppController.getModuleScope(),
1048                             Keys.KEY_REQUEST_RETURN_HDR_PLUS, true);
1049                     switchToRegularCapture();
1050                 }
1051             };
1052         } else {
1053             return new ButtonManager.ButtonCallback() {
1054                 @Override
1055                 public void onStateChanged(int cameraId) {
1056                     if (mPaused) {
1057                         return;
1058                     }
1059
1060                     // At the time this callback is fired, the camera id
1061                     // has be set to the desired camera.
1062                     mSettingsManager.set(mAppController.getModuleScope(), Keys.KEY_CAMERA_ID,
1063                             cameraId);
1064
1065                     Log.d(TAG, "Start to switch camera. cameraId=" + cameraId);
1066                     mCameraFacing = getFacingFromCameraId(cameraId);
1067                     updateCameraCharacteristics();
1068                     switchCamera();
1069                 }
1070             };
1071         }
1072     }
1073
1074     /**
1075      * Switches to PhotoModule to do regular photo captures.
1076      * <p>
1077      * TODO: Remove this once we use CaptureModule for photo taking.
1078      */
1079     private void switchToRegularCapture() {
1080         // Turn off HDR+ before switching back to normal photo mode.
1081         SettingsManager settingsManager = mAppController.getSettingsManager();
1082         settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR_PLUS, false);
1083
1084         // Disable this button to prevent callbacks from this module from firing
1085         // while we are transitioning modules.
1086         ButtonManager buttonManager = mAppController.getButtonManager();
1087         buttonManager.disableButtonClick(ButtonManager.BUTTON_HDR_PLUS);
1088         mAppController.getCameraAppUI().freezeScreenUntilPreviewReady();
1089         mAppController.onModeSelected(mContext.getResources().getInteger(
1090                 R.integer.camera_mode_photo));
1091         buttonManager.enableButtonClick(ButtonManager.BUTTON_HDR_PLUS);
1092     }
1093
1094     /**
1095      * Called when the preview started. Informs the app controller and queues a
1096      * transform update when the next preview frame arrives.
1097      */
1098     private void onPreviewStarted() {
1099         if (mState == ModuleState.WATCH_FOR_NEXT_FRAME_AFTER_PREVIEW_STARTED) {
1100             mState = ModuleState.UPDATE_TRANSFORM_ON_NEXT_SURFACE_TEXTURE_UPDATE;
1101         }
1102         mAppController.onPreviewStarted();
1103     }
1104
1105     /**
1106      * Update the preview transform based on the new dimensions. Will not force
1107      * an update, if it's not necessary.
1108      */
1109     private void updatePreviewTransform(int incomingWidth, int incomingHeight) {
1110         updatePreviewTransform(incomingWidth, incomingHeight, false);
1111     }
1112
1113     /**
1114      * Returns whether it is necessary to apply device-specific fix for b/19271661
1115      * on the AutoTransform Path, i.e. USE_AUTOTRANSFORM_UI_LAYOUT == true
1116      *
1117      * @return whether to apply workaround fix for b/19271661
1118      */
1119     private boolean requiresNexus4SpecificFixFor16By9Previews() {
1120         return USE_AUTOTRANSFORM_UI_LAYOUT && ApiHelper.IS_NEXUS_4
1121                 && is16by9AspectRatio(mPictureSize);
1122     }
1123
1124     /***
1125      * Update the preview transform based on the new dimensions. TODO: Make work
1126      * with all: aspect ratios/resolutions x screens/cameras.
1127      */
1128     private void updatePreviewTransform(int incomingWidth, int incomingHeight,
1129             boolean forceUpdate) {
1130         Log.d(TAG, "updatePreviewTransform: " + incomingWidth + " x " + incomingHeight);
1131
1132         synchronized (mDimensionLock) {
1133             int incomingRotation = CameraUtil.getDisplayRotation();
1134             // Check for an actual change:
1135             if (mScreenHeight == incomingHeight && mScreenWidth == incomingWidth &&
1136                     incomingRotation == mDisplayRotation && !forceUpdate) {
1137                 return;
1138             }
1139             // Update display rotation and dimensions
1140             mDisplayRotation = incomingRotation;
1141             mScreenWidth = incomingWidth;
1142             mScreenHeight = incomingHeight;
1143             updatePreviewBufferDimension();
1144
1145             // Assumptions:
1146             // - Aspect ratio for the sensor buffers is in landscape
1147             // orientation,
1148             // - Dimensions of buffers received are rotated to the natural
1149             // device orientation.
1150             // - The contents of each buffer are rotated by the inverse of
1151             // the display rotation.
1152             // - Surface scales the buffer to fit the current view bounds.
1153
1154             // Get natural orientation and buffer dimensions
1155
1156             if(USE_AUTOTRANSFORM_UI_LAYOUT) {
1157                 // Use PhotoUI-based AutoTransformation Interface
1158                 if (mPreviewBufferWidth != 0 && mPreviewBufferHeight != 0) {
1159                     if (requiresNexus4SpecificFixFor16By9Previews()) {
1160                         // Force preview size to be 16:9, even though surface is 4:3
1161                         // Surface content is assumed to be 16:9.
1162                         mAppController.updatePreviewAspectRatio(16.f / 9.f);
1163                     } else {
1164                         mAppController.updatePreviewAspectRatio(
1165                                 mPreviewBufferWidth / (float) mPreviewBufferHeight);
1166                     }
1167                 }
1168             } else {
1169                 Matrix transformMatrix = mPreviewTransformCalculator.toTransformMatrix(
1170                         new Size(mScreenWidth, mScreenHeight),
1171                         new Size(mPreviewBufferWidth, mPreviewBufferHeight));
1172                 mAppController.updatePreviewTransform(transformMatrix);
1173             }
1174         }
1175     }
1176
1177
1178     /**
1179      * Calculates whether a picture size is 16:9 ratio, regardless of its
1180      * orientation.
1181      *
1182      * @param size the size of the picture to be considered
1183      * @return true, if the picture is 16:9; false if it's invalid or size is null
1184      */
1185     private boolean is16by9AspectRatio(Size size) {
1186         if (size == null || size.getWidth() == 0 || size.getHeight() == 0) {
1187             return false;
1188         }
1189
1190         // Normalize aspect ratio to be greater than 1.
1191         final float aspectRatio = (size.getHeight() > size.getWidth())
1192                 ? (size.getHeight() / (float) size.getWidth())
1193                 : (size.getWidth() / (float) size.getHeight());
1194
1195         return Math.abs(aspectRatio - (16.f / 9.f)) < 0.001f;
1196     }
1197
1198     /**
1199      * Based on the current picture size, selects the best preview dimension and
1200      * stores it in {@link #mPreviewBufferWidth} and
1201      * {@link #mPreviewBufferHeight}.
1202      */
1203     private void updatePreviewBufferDimension() {
1204         if (mCamera == null) {
1205             return;
1206         }
1207
1208         Size previewBufferSize = mCamera.pickPreviewSize(mPictureSize, mContext);
1209         mPreviewBufferWidth = previewBufferSize.getWidth();
1210         mPreviewBufferHeight = previewBufferSize.getHeight();
1211
1212         // Workaround for N4 TextureView/HAL issues b/19271661 for 16:9 preview
1213         // streams.
1214         if (requiresNexus4SpecificFixFor16By9Previews()) {
1215             // Override the preview selection logic to the largest N4 4:3
1216             // preview size but pass in 16:9 aspect ratio in
1217             // UpdatePreviewAspectRatio later.
1218             mPreviewBufferWidth = 1280;
1219             mPreviewBufferHeight = 960;
1220         }
1221         updatePreviewBufferSize();
1222     }
1223
1224     /**
1225      * Open camera and start the preview.
1226      */
1227     private void openCameraAndStartPreview() {
1228         Profile guard = mProfiler.create("CaptureModule.openCameraAndStartPreview()").start();
1229         try {
1230             // TODO Given the current design, we cannot guarantee that one of
1231             // CaptureReadyCallback.onSetupFailed or onReadyForCapture will
1232             // be called (see below), so it's possible that
1233             // mCameraOpenCloseLock.release() is never called under extremely
1234             // rare cases. If we leak the lock, this timeout ensures that we at
1235             // least crash so we don't deadlock the app.
1236             if (!mCameraOpenCloseLock.tryAcquire(CAMERA_OPEN_CLOSE_TIMEOUT_MILLIS,
1237                     TimeUnit.MILLISECONDS)) {
1238                 throw new RuntimeException("Time out waiting to acquire camera-open lock.");
1239             }
1240         } catch (InterruptedException e) {
1241             throw new RuntimeException("Interrupted while waiting to acquire camera-open lock.", e);
1242         }
1243
1244         guard.mark("Acquired mCameraOpenCloseLock");
1245
1246         if (mCameraManager == null) {
1247             Log.e(TAG, "no available OneCameraManager, showing error dialog");
1248             mCameraOpenCloseLock.release();
1249             mAppController.showErrorAndFinish(R.string.cannot_connect_camera);
1250             guard.stop("No OneCameraManager");
1251             return;
1252         }
1253         if (mCamera != null) {
1254             // If the camera is already open, do nothing.
1255             Log.d(TAG, "Camera already open, not re-opening.");
1256             mCameraOpenCloseLock.release();
1257             guard.stop("Camera is already open");
1258             return;
1259         }
1260
1261         // Derive objects necessary for camera creation.
1262         MainThread mainThread = MainThread.create();
1263         ImageRotationCalculator imageRotationCalculator = ImageRotationCalculatorImpl
1264                 .from(mAppController.getOrientationManager(), mCameraCharacteristics);
1265
1266         // Only enable GCam on the back camera
1267         boolean useHdr = mHdrPlusEnabled && mCameraFacing == Facing.BACK;
1268
1269         OneCameraCaptureSetting captureSetting;
1270         // Read the preferred picture size from the setting.
1271         try {
1272             captureSetting = OneCameraCaptureSetting.create(
1273                     mCameraFacing, mAppController.getResolutionSetting(), mSettingsManager,
1274                     mAppController.getModuleScope(), useHdr);
1275         } catch (OneCameraAccessException ex) {
1276             mAppController.showErrorAndFinish(R.string.cannot_connect_camera);
1277             return;
1278         }
1279         mPictureSize = captureSetting.getCaptureSize();
1280
1281         mCameraManager.open(captureSetting, mCameraHandler, mainThread,
1282                 imageRotationCalculator, mBurstController, mSoundPlayer,
1283                 new OpenCallback() {
1284                     @Override
1285                     public void onFailure() {
1286                         Log.e(TAG, "Could not open camera.");
1287                         mCamera = null;
1288                         mCameraOpenCloseLock.release();
1289                         mAppController.showErrorAndFinish(R.string.cannot_connect_camera);
1290                     }
1291
1292                     @Override
1293                     public void onCameraClosed() {
1294                         mCamera = null;
1295                         mCameraOpenCloseLock.release();
1296                     }
1297
1298                     @Override
1299                     public void onCameraOpened(final OneCamera camera) {
1300                         Log.d(TAG, "onCameraOpened: " + camera);
1301                         mCamera = camera;
1302                         updatePreviewBufferDimension();
1303
1304                         // If the surface texture is not destroyed, it may have
1305                         // the last frame lingering. We need to hold off setting
1306                         // transform until preview is started.
1307                         updatePreviewBufferSize();
1308                         mState = ModuleState.WATCH_FOR_NEXT_FRAME_AFTER_PREVIEW_STARTED;
1309                         Log.d(TAG, "starting preview ...");
1310
1311                         // TODO: make mFocusController final and remove null
1312                         // check.
1313                         if (mFocusController != null) {
1314                             camera.setFocusDistanceListener(mFocusController);
1315                         }
1316
1317                         // TODO: Consider rolling these two calls into one.
1318                         camera.startPreview(new Surface(getPreviewSurfaceTexture()),
1319                                 new CaptureReadyCallback() {
1320                                     @Override
1321                                     public void onSetupFailed() {
1322                                         // We must release this lock here,
1323                                         // before posting to the main handler
1324                                         // since we may be blocked in pause(),
1325                                         // getting ready to close the camera.
1326                                         mCameraOpenCloseLock.release();
1327                                         Log.e(TAG, "Could not set up preview.");
1328                                         mMainThread.execute(new Runnable() {
1329                                             @Override
1330                                             public void run() {
1331                                                 if (mCamera == null) {
1332                                                     Log.d(TAG, "Camera closed, aborting.");
1333                                                     return;
1334                                                 }
1335                                                 mCamera.close();
1336                                                 mCamera = null;
1337                                                 // TODO: Show an error message
1338                                                 // and exit.
1339                                             }
1340                                         });
1341                                     }
1342
1343                                     @Override
1344                                     public void onReadyForCapture() {
1345                                         // We must release this lock here,
1346                                         // before posting to the main handler
1347                                         // since we may be blocked in pause(),
1348                                         // getting ready to close the camera.
1349                                         mCameraOpenCloseLock.release();
1350                                         mMainThread.execute(new Runnable() {
1351                                             @Override
1352                                             public void run() {
1353                                                 Log.d(TAG, "Ready for capture.");
1354                                                 if (mCamera == null) {
1355                                                     Log.d(TAG, "Camera closed, aborting.");
1356                                                     return;
1357                                                 }
1358                                                 onPreviewStarted();
1359                                                 // May be overridden by
1360                                                 // subsequent call to
1361                                                 // onReadyStateChanged().
1362                                                 onReadyStateChanged(true);
1363                                                 mCamera.setReadyStateChangedListener(
1364                                                         CaptureModule.this);
1365                                                 // Enable zooming after preview
1366                                                 // has started.
1367                                                 mUI.initializeZoom(mCamera.getMaxZoom());
1368                                                 mCamera.setFocusStateListener(CaptureModule.this);
1369                                             }
1370                                         });
1371                                     }
1372                                 });
1373                         }
1374                 }, mAppController.getFatalErrorHandler());
1375         guard.stop("mCameraManager.open()");
1376     }
1377
1378     private void closeCamera() {
1379         Profile profile = mProfiler.create("CaptureModule.closeCamera()").start();
1380         try {
1381             mCameraOpenCloseLock.acquire();
1382         } catch (InterruptedException e) {
1383             throw new RuntimeException("Interrupted while waiting to acquire camera-open lock.", e);
1384         }
1385         profile.mark("mCameraOpenCloseLock.acquire()");
1386         try {
1387             if (mCamera != null) {
1388                 mCamera.close();
1389                 profile.mark("mCamera.close()");
1390                 mCamera.setFocusStateListener(null);
1391                 mCamera = null;
1392             }
1393         } finally {
1394             mCameraOpenCloseLock.release();
1395         }
1396         profile.stop();
1397     }
1398
1399     /**
1400      * Re-initialize the camera if e.g. the HDR mode or facing property changed.
1401      */
1402     private void switchCamera() {
1403         if (mPaused) {
1404             return;
1405         }
1406         cancelCountDown();
1407         mAppController.freezeScreenUntilPreviewReady();
1408         initSurfaceTextureConsumer();
1409     }
1410
1411     private int getPreviewOrientation(int deviceOrientationDegrees) {
1412         // Important: Camera2 buffers are already rotated to the natural
1413         // orientation of the device (at least for the back-camera).
1414
1415         return (360 - deviceOrientationDegrees) % 360;
1416     }
1417
1418     /**
1419      * Returns which way around the camera is facing, based on it's ID.
1420      * <p>
1421      * TODO: This needs to change so that we store the direction directly in the
1422      * settings, rather than a Camera ID.
1423      */
1424     private static Facing getFacingFromCameraId(int cameraId) {
1425         return cameraId == 1 ? Facing.FRONT : Facing.BACK;
1426     }
1427
1428     private void resetTextureBufferSize() {
1429         // According to the documentation for
1430         // SurfaceTexture.setDefaultBufferSize,
1431         // photo and video based image producers (presumably only Camera 1 api),
1432         // override this buffer size. Any module that uses egl to render to a
1433         // SurfaceTexture must have these buffer sizes reset manually. Otherwise
1434         // the SurfaceTexture cannot be transformed by matrix set on the
1435         // TextureView.
1436         updatePreviewBufferSize();
1437     }
1438 }