• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.app.Activity;
20 import android.content.Context;
21 import android.content.res.Configuration;
22 import android.graphics.Bitmap;
23 import android.graphics.Matrix;
24 import android.graphics.RectF;
25 import android.graphics.SurfaceTexture;
26 import android.hardware.Sensor;
27 import android.hardware.SensorEvent;
28 import android.hardware.SensorEventListener;
29 import android.hardware.SensorManager;
30 import android.location.Location;
31 import android.net.Uri;
32 import android.os.Handler;
33 import android.os.HandlerThread;
34 import android.os.SystemClock;
35 import android.provider.MediaStore;
36 import android.view.KeyEvent;
37 import android.view.OrientationEventListener;
38 import android.view.Surface;
39 import android.view.TextureView;
40 import android.view.View;
41 import android.view.View.OnLayoutChangeListener;
42 
43 import com.android.camera.app.AppController;
44 import com.android.camera.app.CameraAppUI;
45 import com.android.camera.app.CameraAppUI.BottomBarUISpec;
46 import com.android.camera.app.LocationManager;
47 import com.android.camera.app.MediaSaver;
48 import com.android.camera.debug.DebugPropertyHelper;
49 import com.android.camera.debug.Log;
50 import com.android.camera.debug.Log.Tag;
51 import com.android.camera.hardware.HardwareSpec;
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.OneCamera.PhotoCaptureParameters.Flash;
60 import com.android.camera.one.OneCameraManager;
61 import com.android.camera.one.Settings3A;
62 import com.android.camera.one.v2.OneCameraManagerImpl;
63 import com.android.camera.remote.RemoteCameraModule;
64 import com.android.camera.session.CaptureSession;
65 import com.android.camera.settings.Keys;
66 import com.android.camera.settings.SettingsManager;
67 import com.android.camera.ui.CountDownView;
68 import com.android.camera.ui.PreviewStatusListener;
69 import com.android.camera.ui.TouchCoordinate;
70 import com.android.camera.util.CameraUtil;
71 import com.android.camera.util.GcamHelper;
72 import com.android.camera.util.Size;
73 import com.android.camera.util.UsageStatistics;
74 import com.android.camera2.R;
75 import com.android.ex.camera2.portability.CameraAgent.CameraProxy;
76 
77 import java.io.File;
78 import java.util.concurrent.Semaphore;
79 import java.util.concurrent.TimeUnit;
80 
81 /**
82  * New Capture module that is made to support photo and video capture on top of
83  * the OneCamera API, to transparently support GCam.
84  * <p>
85  * This has been a re-write with pieces taken and improved from GCamModule and
86  * PhotoModule, which are to be retired eventually.
87  * <p>
88  * TODO:
89  * <ul>
90  * <li>Server-side logging
91  * <li>Focusing (managed by OneCamera implementations)
92  * <li>Show location dialog on first start
93  * <li>Show resolution dialog on certain devices
94  * <li>Store location
95  * <li>Capture intent
96  * </ul>
97  */
98 public class CaptureModule extends CameraModule
99         implements MediaSaver.QueueListener,
100         ModuleController,
101         CountDownView.OnCountDownStatusListener,
102         OneCamera.PictureCallback,
103         OneCamera.FocusStateListener,
104         OneCamera.ReadyStateChangedListener,
105         PreviewStatusListener.PreviewAreaChangedListener,
106         RemoteCameraModule,
107         SensorEventListener,
108         SettingsManager.OnSettingChangedListener,
109         TextureView.SurfaceTextureListener {
110 
111     /**
112      * Called on layout changes.
113      */
114     private final OnLayoutChangeListener mLayoutListener = new OnLayoutChangeListener() {
115         @Override
116         public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
117                 int oldTop, int oldRight, int oldBottom) {
118             int width = right - left;
119             int height = bottom - top;
120             updatePreviewTransform(width, height, false);
121         }
122     };
123 
124     /**
125      * Hide AF target UI element.
126      */
127     Runnable mHideAutoFocusTargetRunnable = new Runnable() {
128         @Override
129         public void run() {
130             // For debug UI off, showAutoFocusSuccess() just hides the AF UI.
131             if (mFocusedAtEnd) {
132                 mUI.showAutoFocusSuccess();
133             } else {
134                 mUI.showAutoFocusFailure();
135             }
136         }
137     };
138 
139     private static final Tag TAG = new Tag("CaptureModule");
140     private static final String PHOTO_MODULE_STRING_ID = "PhotoModule";
141     /** Enable additional debug output. */
142     private static final boolean DEBUG = true;
143     /**
144      * This is the delay before we execute onResume tasks when coming from the
145      * lock screen, to allow time for onPause to execute.
146      * <p>
147      * TODO: Make sure this value is in sync with what we see on L.
148      */
149     private static final int ON_RESUME_TASKS_DELAY_MSEC = 20;
150 
151     /** Timeout for camera open/close operations. */
152     private static final int CAMERA_OPEN_CLOSE_TIMEOUT_MILLIS = 2500;
153 
154     /** System Properties switch to enable debugging focus UI. */
155     private static final boolean CAPTURE_DEBUG_UI = DebugPropertyHelper.showCaptureDebugUI();
156 
157     private final Object mDimensionLock = new Object();
158 
159     /**
160      * Sticky Gcam mode is when this module's sole purpose it to be the Gcam
161      * mode. If true, the device uses {@link PhotoModule} for normal picture
162      * taking.
163      */
164     private final boolean mStickyGcamCamera;
165 
166     /**
167      * Lock for race conditions in the SurfaceTextureListener callbacks.
168      */
169     private final Object mSurfaceLock = new Object();
170     /** Controller giving us access to other services. */
171     private final AppController mAppController;
172     /** The applications settings manager. */
173     private final SettingsManager mSettingsManager;
174     /** Application context. */
175     private final Context mContext;
176     private CaptureModuleUI mUI;
177     /** The camera manager used to open cameras. */
178     private OneCameraManager mCameraManager;
179     /** The currently opened camera device, or null if the camera is closed. */
180     private OneCamera mCamera;
181     /** Held when opening or closing the camera. */
182     private final Semaphore mCameraOpenCloseLock = new Semaphore(1);
183     /** The direction the currently opened camera is facing to. */
184     private Facing mCameraFacing = Facing.BACK;
185     /** Whether HDR is currently enabled. */
186     private boolean mHdrEnabled = false;
187 
188     /** The texture used to render the preview in. */
189     private SurfaceTexture mPreviewTexture;
190 
191     /** State by the module state machine. */
192     private static enum ModuleState {
193         IDLE,
194         WATCH_FOR_NEXT_FRAME_AFTER_PREVIEW_STARTED,
195         UPDATE_TRANSFORM_ON_NEXT_SURFACE_TEXTURE_UPDATE,
196     }
197 
198     /** The current state of the module. */
199     private ModuleState mState = ModuleState.IDLE;
200     /** Current orientation of the device. */
201     private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
202     /** Current zoom value. */
203     private float mZoomValue = 1f;
204     /** Current duration of capture timer in seconds. */
205     private int mTimerDuration;
206     // TODO: Get image capture intent UI working.
207     private boolean mIsImageCaptureIntent;
208 
209     /** True if in AF tap-to-focus sequence. */
210     private boolean mTapToFocusWaitForActiveScan = false;
211     /** Records beginning frame of each AF scan. */
212     private long mAutoFocusScanStartFrame = -1;
213     /** Records beginning time of each AF scan in uptimeMillis. */
214     private long mAutoFocusScanStartTime;
215 
216     /** Persistence of Tap to Focus target UI after scan complete. */
217     private static final int FOCUS_HOLD_UI_MILLIS = 0;
218     /** Worst case persistence of TTF target UI. */
219     private static final int FOCUS_UI_TIMEOUT_MILLIS = 2000;
220     /** Results from last tap to focus scan */
221     private boolean mFocusedAtEnd;
222     /** Sensor manager we use to get the heading of the device. */
223     private SensorManager mSensorManager;
224     /** Accelerometer. */
225     private Sensor mAccelerometerSensor;
226     /** Compass. */
227     private Sensor mMagneticSensor;
228 
229     /** Accelerometer data. */
230     private final float[] mGData = new float[3];
231     /** Magnetic sensor data. */
232     private final float[] mMData = new float[3];
233     /** Temporary rotation matrix. */
234     private final float[] mR = new float[16];
235     /** Current compass heading. */
236     private int mHeading = -1;
237 
238     /** Used to fetch and embed the location into captured images. */
239     private LocationManager mLocationManager;
240     /** Plays sounds for countdown timer. */
241     private SoundPlayer mCountdownSoundPlayer;
242 
243     /** Whether the module is paused right now. */
244     private boolean mPaused;
245 
246     /** Whether this module was resumed from lockscreen capture intent. */
247     private boolean mIsResumeFromLockScreen = false;
248 
249     private final Runnable mResumeTaskRunnable = new Runnable() {
250         @Override
251         public void run() {
252             onResumeTasks();
253         }
254     };
255 
256     /** Main thread handler. */
257     private Handler mMainHandler;
258     /** Handler thread for camera-related operations. */
259     private Handler mCameraHandler;
260 
261     /** Current display rotation in degrees. */
262     private int mDisplayRotation;
263     /** Current screen width in pixels. */
264     private int mScreenWidth;
265     /** Current screen height in pixels. */
266     private int mScreenHeight;
267     /** Current width of preview frames from camera. */
268     private int mPreviewBufferWidth;
269     /** Current height of preview frames from camera.. */
270     private int mPreviewBufferHeight;
271     /** Area used by preview. */
272     RectF mPreviewArea;
273 
274     /** The current preview transformation matrix. */
275     private Matrix mPreviewTranformationMatrix = new Matrix();
276     /** TODO: This is N5 specific. */
277     public static final float FULLSCREEN_ASPECT_RATIO = 16 / 9f;
278 
279     /** A directory to store debug information in during development. */
280     private final File mDebugDataDir;
281 
282     /** CLEAN UP START */
283     // private boolean mFirstLayout;
284     // private int[] mTargetFPSRanges;
285     // private float mZoomValue;
286     // private int mSensorOrientation;
287     // private int mLensFacing;
288     // private String mFlashMode;
289     /** CLEAN UP END */
290 
CaptureModule(AppController appController)291     public CaptureModule(AppController appController) {
292         this(appController, false);
293     }
294 
295     /** Constructs a new capture module. */
CaptureModule(AppController appController, boolean stickyHdr)296     public CaptureModule(AppController appController, boolean stickyHdr) {
297         super(appController);
298         mAppController = appController;
299         mContext = mAppController.getAndroidContext();
300         mSettingsManager = mAppController.getSettingsManager();
301         mSettingsManager.addListener(this);
302         mDebugDataDir = mContext.getExternalCacheDir();
303         mStickyGcamCamera = stickyHdr;
304     }
305 
306     @Override
init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent)307     public void init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent) {
308         Log.d(TAG, "init");
309         mIsResumeFromLockScreen = isResumeFromLockscreen(activity);
310         mMainHandler = new Handler(activity.getMainLooper());
311         HandlerThread thread = new HandlerThread("CaptureModule.mCameraHandler");
312         thread.start();
313         mCameraHandler = new Handler(thread.getLooper());
314         mCameraManager = mAppController.getCameraManager();
315         mLocationManager = mAppController.getLocationManager();
316         mDisplayRotation = CameraUtil.getDisplayRotation(mContext);
317         mCameraFacing = getFacingFromCameraId(mSettingsManager.getInteger(
318                 mAppController.getModuleScope(),
319                 Keys.KEY_CAMERA_ID));
320         mUI = new CaptureModuleUI(activity, this, mAppController.getModuleLayoutRoot(),
321                 mLayoutListener);
322         mAppController.setPreviewStatusListener(mUI);
323         mPreviewTexture = mAppController.getCameraAppUI().getSurfaceTexture();
324         mSensorManager = (SensorManager) (mContext.getSystemService(Context.SENSOR_SERVICE));
325         mAccelerometerSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
326         mMagneticSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
327         mCountdownSoundPlayer = new SoundPlayer(mContext);
328 
329         String action = activity.getIntent().getAction();
330         mIsImageCaptureIntent = (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
331                 || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
332         View cancelButton = activity.findViewById(R.id.shutter_cancel_button);
333         cancelButton.setOnClickListener(new View.OnClickListener() {
334             @Override
335             public void onClick(View view) {
336                 cancelCountDown();
337             }
338         });
339     }
340 
341     @Override
onShutterButtonFocus(boolean pressed)342     public void onShutterButtonFocus(boolean pressed) {
343         // TODO Auto-generated method stub
344     }
345 
346     @Override
onShutterCoordinate(TouchCoordinate coord)347     public void onShutterCoordinate(TouchCoordinate coord) {
348         // TODO Auto-generated method stub
349     }
350 
351     @Override
onShutterButtonClick()352     public void onShutterButtonClick() {
353         if (mCamera == null) {
354             return;
355         }
356 
357         int countDownDuration = mSettingsManager
358                 .getInteger(SettingsManager.SCOPE_GLOBAL, Keys.KEY_COUNTDOWN_DURATION);
359         mTimerDuration = countDownDuration;
360         if (countDownDuration > 0) {
361             // Start count down.
362             mAppController.getCameraAppUI().transitionToCancel();
363             mAppController.getCameraAppUI().hideModeOptions();
364             mUI.setCountdownFinishedListener(this);
365             mUI.startCountdown(countDownDuration);
366             // Will take picture later via listener callback.
367         } else {
368             takePictureNow();
369         }
370     }
371 
takePictureNow()372     private void takePictureNow() {
373         Location location = mLocationManager.getCurrentLocation();
374 
375         // Set up the capture session.
376         long sessionTime = System.currentTimeMillis();
377         String title = CameraUtil.createJpegName(sessionTime);
378         CaptureSession session = getServices().getCaptureSessionManager()
379                 .createNewSession(title, sessionTime, location);
380 
381         // Set up the parameters for this capture.
382         PhotoCaptureParameters params = new PhotoCaptureParameters();
383         params.title = title;
384         params.callback = this;
385         params.orientation = getOrientation();
386         params.flashMode = getFlashModeFromSettings();
387         params.heading = mHeading;
388         params.debugDataFolder = mDebugDataDir;
389         params.location = location;
390 
391         mCamera.takePicture(params, session);
392     }
393 
394     @Override
onCountDownFinished()395     public void onCountDownFinished() {
396         if (mIsImageCaptureIntent) {
397             mAppController.getCameraAppUI().transitionToIntentReviewLayout();
398         } else {
399             mAppController.getCameraAppUI().transitionToCapture();
400         }
401         mAppController.getCameraAppUI().showModeOptions();
402         if (mPaused) {
403             return;
404         }
405         takePictureNow();
406     }
407 
408     @Override
onRemainingSecondsChanged(int remainingSeconds)409     public void onRemainingSecondsChanged(int remainingSeconds) {
410         if (remainingSeconds == 1) {
411             mCountdownSoundPlayer.play(R.raw.timer_final_second, 0.6f);
412         } else if (remainingSeconds == 2 || remainingSeconds == 3) {
413             mCountdownSoundPlayer.play(R.raw.timer_increment, 0.6f);
414         }
415     }
416 
cancelCountDown()417     private void cancelCountDown() {
418         if (mUI.isCountingDown()) {
419             // Cancel on-going countdown.
420             mUI.cancelCountDown();
421         }
422         mAppController.getCameraAppUI().showModeOptions();
423         mAppController.getCameraAppUI().transitionToCapture();
424     }
425 
426     @Override
onQuickExpose()427     public void onQuickExpose() {
428         mMainHandler.post(new Runnable() {
429             @Override
430             public void run() {
431                 // Starts the short version of the capture animation UI.
432                 mAppController.startPreCaptureAnimation(true);
433             }
434         });
435     }
436 
437     @Override
onPreviewAreaChanged(RectF previewArea)438     public void onPreviewAreaChanged(RectF previewArea) {
439         mPreviewArea = previewArea;
440         mUI.onPreviewAreaChanged(previewArea);
441         // mUI.updatePreviewAreaRect(previewArea);
442         mUI.positionProgressOverlay(previewArea);
443     }
444 
445     @Override
onSensorChanged(SensorEvent event)446     public void onSensorChanged(SensorEvent event) {
447         // This is literally the same as the GCamModule implementation.
448         int type = event.sensor.getType();
449         float[] data;
450         if (type == Sensor.TYPE_ACCELEROMETER) {
451             data = mGData;
452         } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
453             data = mMData;
454         } else {
455             Log.w(TAG, String.format("Unexpected sensor type %s", event.sensor.getName()));
456             return;
457         }
458         for (int i = 0; i < 3; i++) {
459             data[i] = event.values[i];
460         }
461         float[] orientation = new float[3];
462         SensorManager.getRotationMatrix(mR, null, mGData, mMData);
463         SensorManager.getOrientation(mR, orientation);
464         mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
465 
466         if (mHeading < 0) {
467             mHeading += 360;
468         }
469     }
470 
471     @Override
onAccuracyChanged(Sensor sensor, int accuracy)472     public void onAccuracyChanged(Sensor sensor, int accuracy) {
473         // TODO Auto-generated method stub
474     }
475 
476     @Override
onQueueStatus(boolean full)477     public void onQueueStatus(boolean full) {
478         // TODO Auto-generated method stub
479     }
480 
481     @Override
onRemoteShutterPress()482     public void onRemoteShutterPress() {
483         // TODO: Check whether shutter is enabled.
484         onShutterButtonClick();
485     }
486 
487     @Override
onSurfaceTextureAvailable(final SurfaceTexture surface, int width, int height)488     public void onSurfaceTextureAvailable(final SurfaceTexture surface, int width, int height) {
489         Log.d(TAG, "onSurfaceTextureAvailable");
490         // Force to re-apply transform matrix here as a workaround for
491         // b/11168275
492         updatePreviewTransform(width, height, true);
493         initSurface(surface);
494     }
495 
initSurface(final SurfaceTexture surface)496     public void initSurface(final SurfaceTexture surface) {
497         mPreviewTexture = surface;
498         closeCamera();
499         openCameraAndStartPreview();
500     }
501 
502     @Override
onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height)503     public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
504         Log.d(TAG, "onSurfaceTextureSizeChanged");
505         resetDefaultBufferSize();
506     }
507 
508     @Override
onSurfaceTextureDestroyed(SurfaceTexture surface)509     public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
510         Log.d(TAG, "onSurfaceTextureDestroyed");
511         mPreviewTexture = null;
512         closeCamera();
513         return true;
514     }
515 
516     @Override
onSurfaceTextureUpdated(SurfaceTexture surface)517     public void onSurfaceTextureUpdated(SurfaceTexture surface) {
518         if (mState == ModuleState.UPDATE_TRANSFORM_ON_NEXT_SURFACE_TEXTURE_UPDATE) {
519             Log.d(TAG, "onSurfaceTextureUpdated --> updatePreviewTransform");
520             mState = ModuleState.IDLE;
521             CameraAppUI appUI = mAppController.getCameraAppUI();
522             updatePreviewTransform(appUI.getSurfaceWidth(), appUI.getSurfaceHeight(), true);
523         }
524     }
525 
526     @Override
getModuleStringIdentifier()527     public String getModuleStringIdentifier() {
528         return PHOTO_MODULE_STRING_ID;
529     }
530 
531     @Override
resume()532     public void resume() {
533         // Add delay on resume from lock screen only, in order to to speed up
534         // the onResume --> onPause --> onResume cycle from lock screen.
535         // Don't do always because letting go of thread can cause delay.
536         if (mIsResumeFromLockScreen) {
537             Log.v(TAG, "Delayng onResumeTasks from lock screen. " + System.currentTimeMillis());
538             // Note: onPauseAfterSuper() will delete this runnable, so we will
539             // at most have 1 copy queued up.
540             mMainHandler.postDelayed(mResumeTaskRunnable, ON_RESUME_TASKS_DELAY_MSEC);
541         } else {
542             onResumeTasks();
543         }
544     }
545 
onResumeTasks()546     private void onResumeTasks() {
547         Log.d(TAG, "onResumeTasks + " + System.currentTimeMillis());
548         mPaused = false;
549         mAppController.getCameraAppUI().onChangeCamera();
550         mAppController.addPreviewAreaSizeChangedListener(this);
551         resetDefaultBufferSize();
552         getServices().getRemoteShutterListener().onModuleReady(this);
553         // TODO: Check if we can really take a photo right now (memory, camera
554         // state, ... ).
555         mAppController.getCameraAppUI().enableModeOptions();
556         mAppController.setShutterEnabled(true);
557 
558         // Get events from the accelerometer and magnetic sensor.
559         if (mAccelerometerSensor != null) {
560             mSensorManager.registerListener(this, mAccelerometerSensor,
561                     SensorManager.SENSOR_DELAY_NORMAL);
562         }
563         if (mMagneticSensor != null) {
564             mSensorManager.registerListener(this, mMagneticSensor,
565                     SensorManager.SENSOR_DELAY_NORMAL);
566         }
567         mHdrEnabled = mStickyGcamCamera || mAppController.getSettingsManager().getInteger(
568                 SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR_PLUS) == 1;
569 
570         // This means we are resuming with an existing preview texture. This
571         // means we will never get the onSurfaceTextureAvailable call. So we
572         // have to open the camera and start the preview here.
573         if (mPreviewTexture != null) {
574             initSurface(mPreviewTexture);
575         }
576 
577         mCountdownSoundPlayer.loadSound(R.raw.timer_final_second);
578         mCountdownSoundPlayer.loadSound(R.raw.timer_increment);
579     }
580 
581     @Override
pause()582     public void pause() {
583         mPaused = true;
584         cancelCountDown();
585         resetTextureBufferSize();
586         closeCamera();
587         mCountdownSoundPlayer.unloadSound(R.raw.timer_final_second);
588         mCountdownSoundPlayer.unloadSound(R.raw.timer_increment);
589         // Remove delayed resume trigger, if it hasn't been executed yet.
590         mMainHandler.removeCallbacksAndMessages(null);
591 
592         // Unregister the sensors.
593         if (mAccelerometerSensor != null) {
594             mSensorManager.unregisterListener(this, mAccelerometerSensor);
595         }
596         if (mMagneticSensor != null) {
597             mSensorManager.unregisterListener(this, mMagneticSensor);
598         }
599     }
600 
601     @Override
destroy()602     public void destroy() {
603         mCountdownSoundPlayer.release();
604         mCameraHandler.getLooper().quitSafely();
605     }
606 
607     @Override
onLayoutOrientationChanged(boolean isLandscape)608     public void onLayoutOrientationChanged(boolean isLandscape) {
609         Log.d(TAG, "onLayoutOrientationChanged");
610     }
611 
612     @Override
onOrientationChanged(int orientation)613     public void onOrientationChanged(int orientation) {
614         // We keep the last known orientation. So if the user first orient
615         // the camera then point the camera to floor or sky, we still have
616         // the correct orientation.
617         if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
618             return;
619         }
620 
621         // TODO: Document orientation compute logic and unify them in OrientationManagerImpl.
622         // b/17443789
623         // Flip to counter-clockwise orientation.
624         mOrientation = (360 - orientation) % 360;
625     }
626 
627     @Override
onCameraAvailable(CameraProxy cameraProxy)628     public void onCameraAvailable(CameraProxy cameraProxy) {
629         // Ignore since we manage the camera ourselves until we remove this.
630     }
631 
632     @Override
hardResetSettings(SettingsManager settingsManager)633     public void hardResetSettings(SettingsManager settingsManager) {
634         if (mStickyGcamCamera) {
635             // Sitcky HDR+ mode should hard reset HDR+ to on, and camera back
636             // facing.
637             settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR_PLUS, true);
638             settingsManager.set(mAppController.getModuleScope(), Keys.KEY_CAMERA_ID,
639                     getBackFacingCameraId());
640         }
641     }
642 
643     @Override
getHardwareSpec()644     public HardwareSpec getHardwareSpec() {
645         return new HardwareSpec() {
646             @Override
647             public boolean isFrontCameraSupported() {
648                 return true;
649             }
650 
651             @Override
652             public boolean isHdrSupported() {
653                 // TODO: Check if the device has HDR and not HDR+.
654                 return false;
655             }
656 
657             @Override
658             public boolean isHdrPlusSupported() {
659                 return GcamHelper.hasGcamCapture();
660             }
661 
662             @Override
663             public boolean isFlashSupported() {
664                 return true;
665             }
666         };
667     }
668 
669     @Override
670     public BottomBarUISpec getBottomBarSpec() {
671         CameraAppUI.BottomBarUISpec bottomBarSpec = new CameraAppUI.BottomBarUISpec();
672         bottomBarSpec.enableGridLines = true;
673         bottomBarSpec.enableCamera = true;
674         bottomBarSpec.cameraCallback = getCameraCallback();
675         bottomBarSpec.enableHdr = GcamHelper.hasGcamCapture();
676         bottomBarSpec.hdrCallback = getHdrButtonCallback();
677         bottomBarSpec.enableSelfTimer = true;
678         bottomBarSpec.showSelfTimer = true;
679         if (!mHdrEnabled) {
680             bottomBarSpec.enableFlash = true;
681         }
682         // Added to handle case of CaptureModule being used only for Gcam.
683         if (mStickyGcamCamera) {
684             bottomBarSpec.enableFlash = false;
685         }
686         return bottomBarSpec;
687     }
688 
689     @Override
690     public boolean isUsingBottomBar() {
691         return true;
692     }
693 
694     @Override
695     public boolean onKeyDown(int keyCode, KeyEvent event) {
696         switch (keyCode) {
697             case KeyEvent.KEYCODE_CAMERA:
698             case KeyEvent.KEYCODE_DPAD_CENTER:
699                 if (mUI.isCountingDown()) {
700                     cancelCountDown();
701                 } else if (event.getRepeatCount() == 0) {
702                     onShutterButtonClick();
703                 }
704                 return true;
705             case KeyEvent.KEYCODE_VOLUME_UP:
706             case KeyEvent.KEYCODE_VOLUME_DOWN:
707                 // Prevent default.
708                 return true;
709         }
710         return false;
711     }
712 
713     @Override
714     public boolean onKeyUp(int keyCode, KeyEvent event) {
715         switch (keyCode) {
716             case KeyEvent.KEYCODE_VOLUME_UP:
717             case KeyEvent.KEYCODE_VOLUME_DOWN:
718                 onShutterButtonClick();
719                 return true;
720         }
721         return false;
722     }
723 
724     /**
725      * Focus sequence starts for zone around tap location for single tap.
726      */
727     @Override
728     public void onSingleTapUp(View view, int x, int y) {
729         Log.v(TAG, "onSingleTapUp x=" + x + " y=" + y);
730         // TODO: This should query actual capability.
731         if (mCameraFacing == Facing.FRONT) {
732             return;
733         }
734         triggerFocusAtScreenCoord(x, y);
735     }
736 
737     // TODO: Consider refactoring FocusOverlayManager.
738     // Currently AF state transitions are controlled in OneCameraImpl.
739     // PhotoModule uses FocusOverlayManager which uses API1/portability
740     // logic and coordinates.
741     private void triggerFocusAtScreenCoord(int x, int y) {
742         if (mCamera == null) {
743             // If we receive this after the camera is closed, do nothing.
744             return;
745         }
746 
747         mTapToFocusWaitForActiveScan = true;
748         // Show UI immediately even though scan has not started yet.
749         float minEdge = Math.min(mPreviewArea.width(), mPreviewArea.height());
750         mUI.setAutoFocusTarget(x, y, false,
751                 (int) (Settings3A.getAutoFocusRegionWidth() * mZoomValue * minEdge),
752                 (int) (Settings3A.getMeteringRegionWidth() * mZoomValue * minEdge));
753         mUI.showAutoFocusInProgress();
754 
755         // Cancel any scheduled auto focus target UI actions.
756         mMainHandler.removeCallbacks(mHideAutoFocusTargetRunnable);
757         // Timeout in case camera fails to stop (unlikely).
758         mMainHandler.postDelayed(new Runnable() {
759             @Override
760             public void run() {
761                 mMainHandler.post(mHideAutoFocusTargetRunnable);
762             }
763         }, FOCUS_UI_TIMEOUT_MILLIS);
764 
765         // Normalize coordinates to [0,1] per CameraOne API.
766         float points[] = new float[2];
767         points[0] = (x - mPreviewArea.left) / mPreviewArea.width();
768         points[1] = (y - mPreviewArea.top) / mPreviewArea.height();
769 
770         // Rotate coordinates to portrait orientation per CameraOne API.
771         Matrix rotationMatrix = new Matrix();
772         rotationMatrix.setRotate(mDisplayRotation, 0.5f, 0.5f);
773         rotationMatrix.mapPoints(points);
774         mCamera.triggerFocusAndMeterAtPoint(points[0], points[1]);
775 
776         // Log touch (screen coordinates).
777         if (mZoomValue == 1f) {
778             TouchCoordinate touchCoordinate = new TouchCoordinate(x - mPreviewArea.left,
779                     y - mPreviewArea.top, mPreviewArea.width(), mPreviewArea.height());
780             // TODO: Add to logging: duration, rotation.
781             UsageStatistics.instance().tapToFocus(touchCoordinate, null);
782         }
783     }
784 
785     /**
786      * Show AF target in center of preview.
787      */
788     private void setAutoFocusTargetPassive() {
789         float minEdge = Math.min(mPreviewArea.width(), mPreviewArea.height());
790         mUI.setAutoFocusTarget((int) mPreviewArea.centerX(), (int) mPreviewArea.centerY(),
791                 true,
792                 (int) (Settings3A.getAutoFocusRegionWidth() * mZoomValue * minEdge),
793                 (int) (Settings3A.getMeteringRegionWidth() * mZoomValue * minEdge));
794         mUI.showAutoFocusInProgress();
795     }
796 
797     /**
798      * Update UI based on AF state changes.
799      */
800     @Override
801     public void onFocusStatusUpdate(final AutoFocusState state, long frameNumber) {
802         Log.v(TAG, "AF status is state:" + state);
803 
804         switch (state) {
805             case PASSIVE_SCAN:
806                 mMainHandler.removeCallbacks(mHideAutoFocusTargetRunnable);
807                 mMainHandler.post(new Runnable() {
808                     @Override
809                     public void run() {
810                         setAutoFocusTargetPassive();
811                     }
812                 });
813                 break;
814             case ACTIVE_SCAN:
815                 mTapToFocusWaitForActiveScan = false;
816                 break;
817             case PASSIVE_FOCUSED:
818             case PASSIVE_UNFOCUSED:
819                 mMainHandler.post(new Runnable() {
820                     @Override
821                     public void run() {
822                         mUI.setPassiveFocusSuccess(state == AutoFocusState.PASSIVE_FOCUSED);
823                     }
824                 });
825                 break;
826             case ACTIVE_FOCUSED:
827             case ACTIVE_UNFOCUSED:
828                 if (!mTapToFocusWaitForActiveScan) {
829                     mFocusedAtEnd = state != AutoFocusState.ACTIVE_UNFOCUSED;
830                     mMainHandler.removeCallbacks(mHideAutoFocusTargetRunnable);
831                     mMainHandler.post(mHideAutoFocusTargetRunnable);
832                 }
833                 break;
834         }
835 
836         if (CAPTURE_DEBUG_UI) {
837             measureAutoFocusScans(state, frameNumber);
838         }
839     }
840 
841     private void measureAutoFocusScans(final AutoFocusState state, long frameNumber) {
842         // Log AF scan lengths.
843         boolean passive = false;
844         switch (state) {
845             case PASSIVE_SCAN:
846             case ACTIVE_SCAN:
847                 if (mAutoFocusScanStartFrame == -1) {
848                     mAutoFocusScanStartFrame = frameNumber;
849                     mAutoFocusScanStartTime = SystemClock.uptimeMillis();
850                 }
851                 break;
852             case PASSIVE_FOCUSED:
853             case PASSIVE_UNFOCUSED:
854                 passive = true;
855             case ACTIVE_FOCUSED:
856             case ACTIVE_UNFOCUSED:
857                 if (mAutoFocusScanStartFrame != -1) {
858                     long frames = frameNumber - mAutoFocusScanStartFrame;
859                     long dt = SystemClock.uptimeMillis() - mAutoFocusScanStartTime;
860                     int fps = Math.round(frames * 1000f / dt);
861                     String report = String.format("%s scan: fps=%d frames=%d",
862                             passive ? "CAF" : "AF", fps, frames);
863                     Log.v(TAG, report);
864                     mUI.showDebugMessage(String.format("%d / %d", frames, fps));
865                     mAutoFocusScanStartFrame = -1;
866                 }
867                 break;
868         }
869     }
870 
871     @Override
872     public void onReadyStateChanged(boolean readyForCapture) {
873         if (readyForCapture) {
874             mAppController.getCameraAppUI().enableModeOptions();
875         }
876         mAppController.setShutterEnabled(readyForCapture);
877     }
878 
879     @Override
880     public String getPeekAccessibilityString() {
881         return mAppController.getAndroidContext()
882                 .getResources().getString(R.string.photo_accessibility_peek);
883     }
884 
885     @Override
886     public void onThumbnailResult(Bitmap bitmap) {
887         // TODO
888     }
889 
890     @Override
891     public void onPictureTaken(CaptureSession session) {
892         mAppController.getCameraAppUI().enableModeOptions();
893     }
894 
895     @Override
896     public void onPictureSaved(Uri uri) {
897         mAppController.notifyNewMedia(uri);
898     }
899 
900     @Override
901     public void onTakePictureProgress(float progress) {
902         mUI.setPictureTakingProgress((int) (progress * 100));
903     }
904 
905     @Override
906     public void onPictureTakenFailed() {
907     }
908 
909     @Override
910     public void onSettingChanged(SettingsManager settingsManager, String key) {
911         // TODO Auto-generated method stub
912     }
913 
914     /**
915      * Updates the preview transform matrix to adapt to the current preview
916      * width, height, and orientation.
917      */
918     public void updatePreviewTransform() {
919         int width;
920         int height;
921         synchronized (mDimensionLock) {
922             width = mScreenWidth;
923             height = mScreenHeight;
924         }
925         updatePreviewTransform(width, height);
926     }
927 
928     /**
929      * Set zoom value.
930      *
931      * @param zoom Zoom value, must be between 1.0 and mCamera.getMaxZoom().
932      */
933     public void setZoom(float zoom) {
934         mZoomValue = zoom;
935         if (mCamera != null) {
936             mCamera.setZoom(zoom);
937         }
938     }
939 
940     /**
941      * TODO: Remove this method once we are in pure CaptureModule land.
942      */
943     private String getBackFacingCameraId() {
944         if (!(mCameraManager instanceof OneCameraManagerImpl)) {
945             throw new IllegalStateException("This should never be called with Camera API V1");
946         }
947         OneCameraManagerImpl manager = (OneCameraManagerImpl) mCameraManager;
948         return manager.getFirstBackCameraId();
949     }
950 
951     /**
952      * @return Depending on whether we're in sticky-HDR mode or not, return the
953      *         proper callback to be used for when the HDR/HDR+ button is
954      *         pressed.
955      */
956     private ButtonManager.ButtonCallback getHdrButtonCallback() {
957         if (mStickyGcamCamera) {
958             return new ButtonManager.ButtonCallback() {
959                 @Override
960                 public void onStateChanged(int state) {
961                     if (mPaused) {
962                         return;
963                     }
964                     if (state == ButtonManager.ON) {
965                         throw new IllegalStateException(
966                                 "Can't leave hdr plus mode if switching to hdr plus mode.");
967                     }
968                     SettingsManager settingsManager = mAppController.getSettingsManager();
969                     settingsManager.set(mAppController.getModuleScope(),
970                             Keys.KEY_REQUEST_RETURN_HDR_PLUS, false);
971                     switchToRegularCapture();
972                 }
973             };
974         } else {
975             return new ButtonManager.ButtonCallback() {
976                 @Override
977                 public void onStateChanged(int hdrEnabled) {
978                     if (mPaused) {
979                         return;
980                     }
981                     Log.d(TAG, "HDR enabled =" + hdrEnabled);
982                     mHdrEnabled = hdrEnabled == 1;
983                     switchCamera();
984                 }
985             };
986         }
987     }
988 
989     /**
990      * @return Depending on whether we're in sticky-HDR mode or not, this
991      *         returns the proper callback to be used for when the camera
992      *         (front/back switch) button is pressed.
993      */
994     private ButtonManager.ButtonCallback getCameraCallback() {
995         if (mStickyGcamCamera) {
996             return new ButtonManager.ButtonCallback() {
997                 @Override
998                 public void onStateChanged(int state) {
999                     if (mPaused) {
1000                         return;
1001                     }
1002 
1003                     // At the time this callback is fired, the camera id setting
1004                     // has changed to the desired camera.
1005                     SettingsManager settingsManager = mAppController.getSettingsManager();
1006                     if (Keys.isCameraBackFacing(settingsManager,
1007                             mAppController.getModuleScope())) {
1008                         throw new IllegalStateException(
1009                                 "Hdr plus should never be switching from front facing camera.");
1010                     }
1011 
1012                     // Switch to photo mode, but request a return to hdr plus on
1013                     // switching to back camera again.
1014                     settingsManager.set(mAppController.getModuleScope(),
1015                             Keys.KEY_REQUEST_RETURN_HDR_PLUS, true);
1016                     switchToRegularCapture();
1017                 }
1018             };
1019         } else {
1020             return new ButtonManager.ButtonCallback() {
1021                 @Override
1022                 public void onStateChanged(int cameraId) {
1023                     if (mPaused) {
1024                         return;
1025                     }
1026 
1027                     // At the time this callback is fired, the camera id
1028                     // has be set to the desired camera.
1029                     mSettingsManager.set(mAppController.getModuleScope(), Keys.KEY_CAMERA_ID,
1030                             cameraId);
1031 
1032                     Log.d(TAG, "Start to switch camera. cameraId=" + cameraId);
1033                     mCameraFacing = getFacingFromCameraId(cameraId);
1034                     switchCamera();
1035                 }
1036             };
1037         }
1038     }
1039 
1040     /**
1041      * Switches to PhotoModule to do regular photo captures.
1042      * <p>
1043      * TODO: Remove this once we use CaptureModule for photo taking.
1044      */
1045     private void switchToRegularCapture() {
1046         // Turn off HDR+ before switching back to normal photo mode.
1047         SettingsManager settingsManager = mAppController.getSettingsManager();
1048         settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR_PLUS, false);
1049 
1050         // Disable this button to prevent callbacks from this module from firing
1051         // while we are transitioning modules.
1052         ButtonManager buttonManager = mAppController.getButtonManager();
1053         buttonManager.disableButtonClick(ButtonManager.BUTTON_HDR_PLUS);
1054         mAppController.getCameraAppUI().freezeScreenUntilPreviewReady();
1055         mAppController.onModeSelected(mContext.getResources().getInteger(
1056                 R.integer.camera_mode_photo));
1057         buttonManager.enableButtonClick(ButtonManager.BUTTON_HDR_PLUS);
1058     }
1059 
1060     /**
1061      * Called when the preview started. Informs the app controller and queues a
1062      * transform update when the next preview frame arrives.
1063      */
1064     private void onPreviewStarted() {
1065         if (mState == ModuleState.WATCH_FOR_NEXT_FRAME_AFTER_PREVIEW_STARTED) {
1066             mState = ModuleState.UPDATE_TRANSFORM_ON_NEXT_SURFACE_TEXTURE_UPDATE;
1067         }
1068         mAppController.onPreviewStarted();
1069     }
1070 
1071     /**
1072      * Update the preview transform based on the new dimensions. Will not force
1073      * an update, if it's not necessary.
1074      */
1075     private void updatePreviewTransform(int incomingWidth, int incomingHeight) {
1076         updatePreviewTransform(incomingWidth, incomingHeight, false);
1077     }
1078 
1079     /***
1080      * Update the preview transform based on the new dimensions. TODO: Make work
1081      * with all: aspect ratios/resolutions x screens/cameras.
1082      */
1083     private void updatePreviewTransform(int incomingWidth, int incomingHeight,
1084             boolean forceUpdate) {
1085         Log.d(TAG, "updatePreviewTransform: " + incomingWidth + " x " + incomingHeight);
1086 
1087         synchronized (mDimensionLock) {
1088             int incomingRotation = CameraUtil
1089                     .getDisplayRotation(mContext);
1090             // Check for an actual change:
1091             if (mScreenHeight == incomingHeight && mScreenWidth == incomingWidth &&
1092                     incomingRotation == mDisplayRotation && !forceUpdate) {
1093                 return;
1094             }
1095             // Update display rotation and dimensions
1096             mDisplayRotation = incomingRotation;
1097             mScreenWidth = incomingWidth;
1098             mScreenHeight = incomingHeight;
1099             updatePreviewBufferDimension();
1100 
1101             mPreviewTranformationMatrix = mAppController.getCameraAppUI().getPreviewTransform(
1102                     mPreviewTranformationMatrix);
1103             int width = mScreenWidth;
1104             int height = mScreenHeight;
1105 
1106             // Assumptions:
1107             // - Aspect ratio for the sensor buffers is in landscape
1108             // orientation,
1109             // - Dimensions of buffers received are rotated to the natural
1110             // device orientation.
1111             // - The contents of each buffer are rotated by the inverse of
1112             // the display rotation.
1113             // - Surface scales the buffer to fit the current view bounds.
1114 
1115             // Get natural orientation and buffer dimensions
1116             int naturalOrientation = CaptureModuleUtil
1117                     .getDeviceNaturalOrientation(mContext);
1118             int effectiveWidth = mPreviewBufferWidth;
1119             int effectiveHeight = mPreviewBufferHeight;
1120 
1121             if (DEBUG) {
1122                 Log.v(TAG, "Rotation: " + mDisplayRotation);
1123                 Log.v(TAG, "Screen Width: " + mScreenWidth);
1124                 Log.v(TAG, "Screen Height: " + mScreenHeight);
1125                 Log.v(TAG, "Buffer width: " + mPreviewBufferWidth);
1126                 Log.v(TAG, "Buffer height: " + mPreviewBufferHeight);
1127                 Log.v(TAG, "Natural orientation: " + naturalOrientation);
1128             }
1129 
1130             // If natural orientation is portrait, rotate the buffer
1131             // dimensions
1132             if (naturalOrientation == Configuration.ORIENTATION_PORTRAIT) {
1133                 int temp = effectiveWidth;
1134                 effectiveWidth = effectiveHeight;
1135                 effectiveHeight = temp;
1136             }
1137 
1138             // Find and center view rect and buffer rect
1139             RectF viewRect = new RectF(0, 0, width, height);
1140             RectF bufRect = new RectF(0, 0, effectiveWidth, effectiveHeight);
1141             float centerX = viewRect.centerX();
1142             float centerY = viewRect.centerY();
1143             bufRect.offset(centerX - bufRect.centerX(), centerY - bufRect.centerY());
1144 
1145             // Undo ScaleToFit.FILL done by the surface
1146             mPreviewTranformationMatrix.setRectToRect(viewRect, bufRect, Matrix.ScaleToFit.FILL);
1147 
1148             // Rotate buffer contents to proper orientation
1149             mPreviewTranformationMatrix.postRotate(getPreviewOrientation(mDisplayRotation),
1150                     centerX, centerY);
1151 
1152             // TODO: This is probably only working for the N5. Need to test
1153             // on a device like N10 with different sensor orientation.
1154             if ((mDisplayRotation % 180) == 90) {
1155                 int temp = effectiveWidth;
1156                 effectiveWidth = effectiveHeight;
1157                 effectiveHeight = temp;
1158             }
1159 
1160             // Scale to fit view, cropping the longest dimension
1161             float scale =
1162                     Math.min(width / (float) effectiveWidth, height
1163                             / (float) effectiveHeight);
1164             mPreviewTranformationMatrix.postScale(scale, scale, centerX, centerY);
1165 
1166             // TODO: Take these quantities from mPreviewArea.
1167             float previewWidth = effectiveWidth * scale;
1168             float previewHeight = effectiveHeight * scale;
1169             float previewCenterX = previewWidth / 2;
1170             float previewCenterY = previewHeight / 2;
1171             mPreviewTranformationMatrix.postTranslate(previewCenterX - centerX, previewCenterY
1172                     - centerY);
1173 
1174             mAppController.updatePreviewTransform(mPreviewTranformationMatrix);
1175             mAppController.getCameraAppUI().hideLetterboxing();
1176             // if (mGcamProxy != null) {
1177             // mGcamProxy.postSetAspectRatio(mFinalAspectRatio);
1178             // }
1179             // mUI.updatePreviewAreaRect(new RectF(0, 0, previewWidth,
1180             // previewHeight));
1181 
1182             // TODO: Add face detection.
1183             // Characteristics info =
1184             // mapp.getCameraProvider().getCharacteristics(0);
1185             // mUI.setupFaceDetection(CameraUtil.getDisplayOrientation(incomingRotation,
1186             // info), false);
1187             // updateCamera2FaceBoundTransform(new
1188             // RectF(mEffectiveCropRegion),
1189             // new RectF(0, 0, mBufferWidth, mBufferHeight),
1190             // new RectF(0, 0, previewWidth, previewHeight), getRotation());
1191         }
1192     }
1193 
1194     /**
1195      * Based on the current picture size, selects the best preview dimension and
1196      * stores it in {@link #mPreviewBufferWidth} and
1197      * {@link #mPreviewBufferHeight}.
1198      */
1199     private void updatePreviewBufferDimension() {
1200         if (mCamera == null) {
1201             return;
1202         }
1203 
1204         Size pictureSize = getPictureSizeFromSettings();
1205         Size previewBufferSize = mCamera.pickPreviewSize(pictureSize, mContext);
1206         mPreviewBufferWidth = previewBufferSize.getWidth();
1207         mPreviewBufferHeight = previewBufferSize.getHeight();
1208     }
1209 
1210     /**
1211      * Resets the default buffer size to the initially calculated size.
1212      */
1213     private void resetDefaultBufferSize() {
1214         synchronized (mSurfaceLock) {
1215             if (mPreviewTexture != null) {
1216                 mPreviewTexture.setDefaultBufferSize(mPreviewBufferWidth, mPreviewBufferHeight);
1217             }
1218         }
1219     }
1220 
1221     /**
1222      * Open camera and start the preview.
1223      */
1224     private void openCameraAndStartPreview() {
1225         // Only enable HDR on the back camera
1226         boolean useHdr = mHdrEnabled && mCameraFacing == Facing.BACK;
1227 
1228         try {
1229             // TODO Given the current design, we cannot guarantee that one of
1230             // CaptureReadyCallback.onSetupFailed or onReadyForCapture will
1231             // be called (see below), so it's possible that
1232             // mCameraOpenCloseLock.release() is never called under extremely
1233             // rare cases.  If we leak the lock, this timeout ensures that we at
1234             // least crash so we don't deadlock the app.
1235             if (!mCameraOpenCloseLock.tryAcquire(CAMERA_OPEN_CLOSE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)) {
1236                 throw new RuntimeException("Time out waiting to acquire camera-open lock.");
1237             }
1238         } catch (InterruptedException e) {
1239             throw new RuntimeException("Interrupted while waiting to acquire camera-open lock.", e);
1240         }
1241         mCameraManager.open(mCameraFacing, useHdr, getPictureSizeFromSettings(),
1242                 new OpenCallback() {
1243                     @Override
1244                     public void onFailure() {
1245                         Log.e(TAG, "Could not open camera.");
1246                         mCamera = null;
1247                         mCameraOpenCloseLock.release();
1248                         mAppController.showErrorAndFinish(R.string.cannot_connect_camera);
1249                     }
1250 
1251                     @Override
1252                     public void onCameraClosed() {
1253                         mCamera = null;
1254                         mCameraOpenCloseLock.release();
1255                     }
1256 
1257                     @Override
1258                     public void onCameraOpened(final OneCamera camera) {
1259                         Log.d(TAG, "onCameraOpened: " + camera);
1260                         mCamera = camera;
1261                         updatePreviewBufferDimension();
1262 
1263                         // If the surface texture is not destroyed, it may have
1264                         // the last frame lingering. We need to hold off setting
1265                         // transform until preview is started.
1266                         resetDefaultBufferSize();
1267                         mState = ModuleState.WATCH_FOR_NEXT_FRAME_AFTER_PREVIEW_STARTED;
1268                         Log.d(TAG, "starting preview ...");
1269 
1270                         // TODO: Consider rolling these two calls into one.
1271                         camera.startPreview(new Surface(mPreviewTexture),
1272                                 new CaptureReadyCallback() {
1273                                     @Override
1274                                     public void onSetupFailed() {
1275                                         // We must release this lock here, before posting
1276                                         // to the main handler since we may be blocked
1277                                         // in pause(), getting ready to close the camera.
1278                                         mCameraOpenCloseLock.release();
1279                                         Log.e(TAG, "Could not set up preview.");
1280                                         mMainHandler.post(new Runnable() {
1281                                            @Override
1282                                            public void run() {
1283                                                if (mCamera == null) {
1284                                                    Log.d(TAG, "Camera closed, aborting.");
1285                                                    return;
1286                                                }
1287                                                mCamera.close(null);
1288                                                mCamera = null;
1289                                                // TODO: Show an error message and exit.
1290                                            }
1291                                         });
1292                                     }
1293 
1294                                     @Override
1295                                     public void onReadyForCapture() {
1296                                         // We must release this lock here, before posting
1297                                         // to the main handler since we may be blocked
1298                                         // in pause(), getting ready to close the camera.
1299                                         mCameraOpenCloseLock.release();
1300                                         mMainHandler.post(new Runnable() {
1301                                            @Override
1302                                            public void run() {
1303                                                Log.d(TAG, "Ready for capture.");
1304                                                if (mCamera == null) {
1305                                                    Log.d(TAG, "Camera closed, aborting.");
1306                                                    return;
1307                                                }
1308                                                onPreviewStarted();
1309                                                // Enable zooming after preview has
1310                                                // started.
1311                                                mUI.initializeZoom(mCamera.getMaxZoom());
1312                                                mCamera.setFocusStateListener(CaptureModule.this);
1313                                                mCamera.setReadyStateChangedListener(CaptureModule.this);
1314                                            }
1315                                         });
1316                                     }
1317                                 });
1318                     }
1319                 }, mCameraHandler);
1320     }
1321 
1322     private void closeCamera() {
1323         try {
1324             mCameraOpenCloseLock.acquire();
1325         } catch(InterruptedException e) {
1326             throw new RuntimeException("Interrupted while waiting to acquire camera-open lock.", e);
1327         }
1328         try {
1329             if (mCamera != null) {
1330                 mCamera.setFocusStateListener(null);
1331                 mCamera.close(null);
1332                 mCamera = null;
1333             }
1334         } finally {
1335             mCameraOpenCloseLock.release();
1336         }
1337     }
1338 
1339     private int getOrientation() {
1340         if (mAppController.isAutoRotateScreen()) {
1341             return mDisplayRotation;
1342         } else {
1343             return mOrientation;
1344         }
1345     }
1346 
1347     /**
1348      * @return Whether we are resuming from within the lockscreen.
1349      */
1350     private static boolean isResumeFromLockscreen(Activity activity) {
1351         String action = activity.getIntent().getAction();
1352         return (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action)
1353         || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action));
1354     }
1355 
1356     /**
1357      * Re-initialize the camera if e.g. the HDR mode or facing property changed.
1358      */
1359     private void switchCamera() {
1360         if (mPaused) {
1361             return;
1362         }
1363         cancelCountDown();
1364         mAppController.freezeScreenUntilPreviewReady();
1365         initSurface(mPreviewTexture);
1366 
1367         // TODO: Un-comment once we have focus back.
1368         // if (mFocusManager != null) {
1369         // mFocusManager.removeMessages();
1370         // }
1371         // mFocusManager.setMirror(mMirror);
1372     }
1373 
1374     private Size getPictureSizeFromSettings() {
1375         String pictureSizeKey = mCameraFacing == Facing.FRONT ? Keys.KEY_PICTURE_SIZE_FRONT
1376                 : Keys.KEY_PICTURE_SIZE_BACK;
1377         return mSettingsManager.getSize(SettingsManager.SCOPE_GLOBAL, pictureSizeKey);
1378     }
1379 
1380     private int getPreviewOrientation(int deviceOrientationDegrees) {
1381         // Important: Camera2 buffers are already rotated to the natural
1382         // orientation of the device (at least for the back-camera).
1383 
1384         // TODO: Remove this hack for the front camera as soon as b/16637957 is
1385         // fixed.
1386         if (mCameraFacing == Facing.FRONT) {
1387             deviceOrientationDegrees += 180;
1388         }
1389         return (360 - deviceOrientationDegrees) % 360;
1390     }
1391 
1392     /**
1393      * Returns which way around the camera is facing, based on it's ID.
1394      * <p>
1395      * TODO: This needs to change so that we store the direction directly in the
1396      * settings, rather than a Camera ID.
1397      */
1398     private static Facing getFacingFromCameraId(int cameraId) {
1399         return cameraId == 1 ? Facing.FRONT : Facing.BACK;
1400     }
1401 
1402     private void resetTextureBufferSize() {
1403         // Reset the default buffer sizes on the shared SurfaceTexture
1404         // so they are not scaled for gcam.
1405         //
1406         // According to the documentation for
1407         // SurfaceTexture.setDefaultBufferSize,
1408         // photo and video based image producers (presumably only Camera 1 api),
1409         // override this buffer size. Any module that uses egl to render to a
1410         // SurfaceTexture must have these buffer sizes reset manually. Otherwise
1411         // the SurfaceTexture cannot be transformed by matrix set on the
1412         // TextureView.
1413         if (mPreviewTexture != null) {
1414             mPreviewTexture.setDefaultBufferSize(mAppController.getCameraAppUI().getSurfaceWidth(),
1415                     mAppController.getCameraAppUI().getSurfaceHeight());
1416         }
1417     }
1418 
1419     /**
1420      * @return The currently set Flash settings. Defaults to AUTO if the setting
1421      *         could not be parsed.
1422      */
1423     private Flash getFlashModeFromSettings() {
1424         String flashSetting = mSettingsManager.getString(mAppController.getCameraScope(),
1425                 Keys.KEY_FLASH_MODE);
1426         try {
1427             return Flash.valueOf(flashSetting.toUpperCase());
1428         } catch (IllegalArgumentException ex) {
1429             Log.w(TAG, "Could not parse Flash Setting. Defaulting to AUTO.");
1430             return Flash.AUTO;
1431         }
1432     }
1433 }
1434