2 * Copyright (C) 2014 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.camera;
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;
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.device.CameraId;
51 import com.android.camera.hardware.HardwareSpec;
52 import com.android.camera.hardware.HeadingSensor;
53 import com.android.camera.module.ModuleController;
54 import com.android.camera.one.OneCamera;
55 import com.android.camera.one.OneCamera.AutoFocusState;
56 import com.android.camera.one.OneCamera.CaptureReadyCallback;
57 import com.android.camera.one.OneCamera.Facing;
58 import com.android.camera.one.OneCamera.OpenCallback;
59 import com.android.camera.one.OneCamera.PhotoCaptureParameters;
60 import com.android.camera.one.OneCameraAccessException;
61 import com.android.camera.one.OneCameraCaptureSetting;
62 import com.android.camera.one.OneCameraCharacteristics;
63 import com.android.camera.one.OneCameraException;
64 import com.android.camera.one.OneCameraManager;
65 import com.android.camera.one.OneCameraModule;
66 import com.android.camera.one.OneCameraOpener;
67 import com.android.camera.one.config.OneCameraFeatureConfig;
68 import com.android.camera.one.v2.photo.ImageRotationCalculator;
69 import com.android.camera.one.v2.photo.ImageRotationCalculatorImpl;
70 import com.android.camera.remote.RemoteCameraModule;
71 import com.android.camera.session.CaptureSession;
72 import com.android.camera.settings.Keys;
73 import com.android.camera.settings.SettingsManager;
74 import com.android.camera.stats.UsageStatistics;
75 import com.android.camera.stats.profiler.Profile;
76 import com.android.camera.stats.profiler.Profiler;
77 import com.android.camera.stats.profiler.Profilers;
78 import com.android.camera.ui.CountDownView;
79 import com.android.camera.ui.PreviewStatusListener;
80 import com.android.camera.ui.TouchCoordinate;
81 import com.android.camera.ui.focus.FocusController;
82 import com.android.camera.ui.focus.FocusSound;
83 import com.android.camera.util.AndroidServices;
84 import com.android.camera.util.ApiHelper;
85 import com.android.camera.util.CameraUtil;
86 import com.android.camera.util.GcamHelper;
87 import com.android.camera.util.Size;
88 import com.android.camera2.R;
89 import com.android.ex.camera2.portability.CameraAgent.CameraProxy;
90 import com.google.common.logging.eventprotos;
92 import java.util.concurrent.Semaphore;
93 import java.util.concurrent.TimeUnit;
96 * New Capture module that is made to support photo and video capture on top of
97 * the OneCamera API, to transparently support GCam.
99 * This has been a re-write with pieces taken and improved from GCamModule and
100 * PhotoModule, which are to be retired eventually.
103 public class CaptureModule extends CameraModule implements
105 CountDownView.OnCountDownStatusListener,
106 OneCamera.PictureCallback,
107 OneCamera.FocusStateListener,
108 OneCamera.ReadyStateChangedListener,
111 private static final Tag TAG = new Tag("CaptureModule");
112 /** Enable additional debug output. */
113 private static final boolean DEBUG = true;
114 /** Workaround Flag for b/19271661 to use autotransformation in Capture Layout in Nexus4 **/
115 private static final boolean USE_AUTOTRANSFORM_UI_LAYOUT = ApiHelper.IS_NEXUS_4;
117 /** Timeout for camera open/close operations. */
118 private static final int CAMERA_OPEN_CLOSE_TIMEOUT_MILLIS = 2500;
120 /** System Properties switch to enable debugging focus UI. */
121 private static final boolean CAPTURE_DEBUG_UI = DebugPropertyHelper.showCaptureDebugUI();
123 private final Object mDimensionLock = new Object();
126 * Sticky Gcam mode is when this module's sole purpose it to be the Gcam
127 * mode. If true, the device uses {@link PhotoModule} for normal picture
130 private final boolean mStickyGcamCamera;
132 /** Controller giving us access to other services. */
133 private final AppController mAppController;
134 /** The applications settings manager. */
135 private final SettingsManager mSettingsManager;
136 /** Application context. */
137 private final Context mContext;
139 private CaptureModuleUI mUI;
140 /** The camera manager used to open cameras. */
141 private OneCameraOpener mOneCameraOpener;
142 /** The manager to query for camera device information */
143 private OneCameraManager mOneCameraManager;
144 /** The currently opened camera device, or null if the camera is closed. */
145 private OneCamera mCamera;
146 /** The selected picture size. */
147 private Size mPictureSize;
148 /** Held when opening or closing the camera. */
149 private final Semaphore mCameraOpenCloseLock = new Semaphore(1);
150 /** The direction the currently opened camera is facing to. */
151 private Facing mCameraFacing;
152 /** Whether HDR Scene mode is currently enabled. */
153 private boolean mHdrSceneEnabled = false;
154 private boolean mHdrPlusEnabled = false;
155 private final Object mSurfaceTextureLock = new Object();
157 * Flag that is used when Fatal Error Handler is running and the app should
158 * not continue execution
160 private boolean mShowErrorAndFinish;
161 private TouchCoordinate mLastShutterTouchCoordinate = null;
163 private FocusController mFocusController;
164 private OneCameraCharacteristics mCameraCharacteristics;
165 final private PreviewTransformCalculator mPreviewTransformCalculator;
167 /** The listener to listen events from the CaptureModuleUI. */
168 private final CaptureModuleUI.CaptureModuleUIListener mUIListener =
169 new CaptureModuleUI.CaptureModuleUIListener() {
171 public void onZoomRatioChanged(float zoomRatio) {
172 mZoomValue = zoomRatio;
173 if (mCamera != null) {
174 mCamera.setZoom(zoomRatio);
179 /** The listener to respond preview area changes. */
180 private final PreviewStatusListener.PreviewAreaChangedListener mPreviewAreaChangedListener =
181 new PreviewStatusListener.PreviewAreaChangedListener() {
183 public void onPreviewAreaChanged(RectF previewArea) {
184 mPreviewArea = previewArea;
185 mFocusController.configurePreviewDimensions(previewArea);
189 /** The listener to listen events from the preview. */
190 private final PreviewStatusListener mPreviewStatusListener = new PreviewStatusListener() {
192 public void onPreviewLayoutChanged(View v, int left, int top, int right,
193 int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
194 int width = right - left;
195 int height = bottom - top;
196 updatePreviewTransform(width, height, false);
200 public boolean shouldAutoAdjustTransformMatrixOnLayout() {
201 return USE_AUTOTRANSFORM_UI_LAYOUT;
205 public void onPreviewFlipped() {
206 // Do nothing because when preview is flipped, TextureView will lay
207 // itself out again, which will then trigger a transform matrix
212 public GestureDetector.OnGestureListener getGestureListener() {
213 return new GestureDetector.SimpleOnGestureListener() {
215 public boolean onSingleTapUp(MotionEvent ev) {
216 Point tapPoint = new Point((int) ev.getX(), (int) ev.getY());
217 Log.v(TAG, "onSingleTapUpPreview location=" + tapPoint);
218 if (!mCameraCharacteristics.isAutoExposureSupported() &&
219 !mCameraCharacteristics.isAutoFocusSupported()) {
222 startActiveFocusAt(tapPoint.x, tapPoint.y);
229 public View.OnTouchListener getTouchListener() {
234 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
235 Log.d(TAG, "onSurfaceTextureAvailable");
236 // Force to re-apply transform matrix here as a workaround for
238 updatePreviewTransform(width, height, true);
239 synchronized (mSurfaceTextureLock) {
240 mPreviewSurfaceTexture = surface;
246 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
247 Log.d(TAG, "onSurfaceTextureDestroyed");
248 synchronized (mSurfaceTextureLock) {
249 mPreviewSurfaceTexture = null;
256 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
257 Log.d(TAG, "onSurfaceTextureSizeChanged");
258 updatePreviewBufferSize();
262 public void onSurfaceTextureUpdated(SurfaceTexture surface) {
263 if (mState == ModuleState.UPDATE_TRANSFORM_ON_NEXT_SURFACE_TEXTURE_UPDATE) {
264 Log.d(TAG, "onSurfaceTextureUpdated --> updatePreviewTransform");
265 mState = ModuleState.IDLE;
266 CameraAppUI appUI = mAppController.getCameraAppUI();
267 updatePreviewTransform(appUI.getSurfaceWidth(), appUI.getSurfaceHeight(), true);
272 private final OneCamera.PictureSaverCallback mPictureSaverCallback =
273 new OneCamera.PictureSaverCallback() {
275 public void onRemoteThumbnailAvailable(final byte[] jpegImage) {
276 mMainThread.execute(new Runnable() {
279 mAppController.getServices().getRemoteShutterListener()
280 .onPictureTaken(jpegImage);
286 /** State by the module state machine. */
287 private static enum ModuleState {
289 WATCH_FOR_NEXT_FRAME_AFTER_PREVIEW_STARTED,
290 UPDATE_TRANSFORM_ON_NEXT_SURFACE_TEXTURE_UPDATE,
293 /** The current state of the module. */
294 private ModuleState mState = ModuleState.IDLE;
295 /** Current zoom value. */
296 private float mZoomValue = 1f;
298 /** Records beginning frame of each AF scan. */
299 private long mAutoFocusScanStartFrame = -1;
300 /** Records beginning time of each AF scan in uptimeMillis. */
301 private long mAutoFocusScanStartTime;
303 /** Heading sensor. */
304 private HeadingSensor mHeadingSensor;
306 /** Used to fetch and embed the location into captured images. */
307 private final LocationManager mLocationManager;
308 /** Plays sounds for countdown timer. */
309 private SoundPlayer mSoundPlayer;
310 private final MediaActionSound mMediaActionSound;
312 /** Whether the module is paused right now. */
313 private boolean mPaused;
316 private final MainThread mMainThread;
317 /** Handler thread for camera-related operations. */
318 private Handler mCameraHandler;
320 /** Current display rotation in degrees. */
321 private int mDisplayRotation;
322 /** Current screen width in pixels. */
323 private int mScreenWidth;
324 /** Current screen height in pixels. */
325 private int mScreenHeight;
326 /** Current width of preview frames from camera. */
327 private int mPreviewBufferWidth;
328 /** Current height of preview frames from camera.. */
329 private int mPreviewBufferHeight;
330 /** Area used by preview. */
333 /** The surface texture for the preview. */
334 private SurfaceTexture mPreviewSurfaceTexture;
336 /** The burst manager for controlling the burst. */
337 private final BurstFacade mBurstController;
338 private static final String BURST_SESSIONS_DIR = "burst_sessions";
340 private final Profiler mProfiler = Profilers.instance().guard();
342 public CaptureModule(AppController appController) {
343 this(appController, false);
346 /** Constructs a new capture module. */
347 public CaptureModule(AppController appController, boolean stickyHdr) {
348 super(appController);
349 Profile guard = mProfiler.create("new CaptureModule").start();
351 mMainThread = MainThread.create();
352 mAppController = appController;
353 mContext = mAppController.getAndroidContext();
354 mSettingsManager = mAppController.getSettingsManager();
355 mStickyGcamCamera = stickyHdr;
356 mLocationManager = mAppController.getLocationManager();
357 mPreviewTransformCalculator = new PreviewTransformCalculator(
358 mAppController.getOrientationManager());
360 mBurstController = BurstFacadeFactory.create(mContext,
361 new OrientationLockController() {
363 public void unlockOrientation() {
364 mAppController.getOrientationManager().unlockOrientation();
368 public void lockOrientation() {
369 mAppController.getOrientationManager().lockOrientation();
372 new BurstReadyStateChangeListener() {
374 public void onBurstReadyStateChanged(boolean ready) {
375 // TODO: This needs to take into account the state of
376 // the whole system, not just burst.
377 onReadyStateChanged(false);
380 mMediaActionSound = new MediaActionSound();
384 private boolean updateCameraCharacteristics() {
386 CameraId cameraId = mOneCameraManager.findFirstCameraFacing(mCameraFacing);
387 if (cameraId != null && cameraId.getValue() != null) {
388 mCameraCharacteristics = mOneCameraManager.getOneCameraCharacteristics(cameraId);
389 return mCameraCharacteristics != null;
391 } catch (OneCameraAccessException ignored) { }
392 mAppController.getFatalErrorHandler().onGenericCameraAccessFailure();
397 public void init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent) {
398 Profile guard = mProfiler.create("CaptureModule.init").start();
399 Log.d(TAG, "init UseAutotransformUiLayout = " + USE_AUTOTRANSFORM_UI_LAYOUT);
400 HandlerThread thread = new HandlerThread("CaptureModule.mCameraHandler");
402 mCameraHandler = new Handler(thread.getLooper());
403 mOneCameraOpener = mAppController.getCameraOpener();
406 mOneCameraManager = OneCameraModule.provideOneCameraManager();
407 } catch (OneCameraException e) {
408 Log.e(TAG, "Unable to provide a OneCameraManager. ", e);
410 mDisplayRotation = CameraUtil.getDisplayRotation();
411 mCameraFacing = getFacingFromCameraId(
412 mSettingsManager.getInteger(mAppController.getModuleScope(), Keys.KEY_CAMERA_ID));
413 mShowErrorAndFinish = !updateCameraCharacteristics();
414 if (mShowErrorAndFinish) {
417 mUI = new CaptureModuleUI(activity, mAppController.getModuleLayoutRoot(), mUIListener);
418 mAppController.setPreviewStatusListener(mPreviewStatusListener);
419 synchronized (mSurfaceTextureLock) {
420 mPreviewSurfaceTexture = mAppController.getCameraAppUI().getSurfaceTexture();
422 mSoundPlayer = new SoundPlayer(mContext);
424 FocusSound focusSound = new FocusSound(mSoundPlayer, R.raw.material_camera_focus);
425 mFocusController = new FocusController(mUI.getFocusRing(), focusSound, mMainThread);
427 mHeadingSensor = new HeadingSensor(AndroidServices.instance().provideSensorManager());
429 View cancelButton = activity.findViewById(R.id.shutter_cancel_button);
430 cancelButton.setOnClickListener(new View.OnClickListener() {
432 public void onClick(View view) {
437 mMediaActionSound.load(MediaActionSound.SHUTTER_CLICK);
442 public void onShutterButtonLongPressed() {
444 OneCameraCharacteristics cameraCharacteristics;
445 CameraId cameraId = mOneCameraManager.findFirstCameraFacing(mCameraFacing);
446 cameraCharacteristics = mOneCameraManager.getOneCameraCharacteristics(cameraId);
447 DeviceOrientation deviceOrientation = mAppController.getOrientationManager()
448 .getDeviceOrientation();
449 ImageRotationCalculator imageRotationCalculator = ImageRotationCalculatorImpl
450 .from(mAppController.getOrientationManager(), cameraCharacteristics);
452 mBurstController.startBurst(
453 new CaptureSession.CaptureSessionCreator() {
455 public CaptureSession createAndStartEmpty() {
456 return createAndStartCaptureSession();
460 mCamera.getDirection(),
461 imageRotationCalculator.toImageRotation().getDegrees());
463 } catch (OneCameraAccessException e) {
464 Log.e(TAG, "Cannot start burst", e);
470 public void onShutterButtonFocus(boolean pressed) {
472 // the shutter button was released, stop any bursts.
473 mBurstController.stopBurst();
478 public void onShutterCoordinate(TouchCoordinate coord) {
479 mLastShutterTouchCoordinate = coord;
483 public void onShutterButtonClick() {
484 if (mCamera == null) {
488 int countDownDuration = mSettingsManager
489 .getInteger(SettingsManager.SCOPE_GLOBAL, Keys.KEY_COUNTDOWN_DURATION);
490 if (countDownDuration > 0) {
492 mAppController.getCameraAppUI().transitionToCancel();
493 mAppController.getCameraAppUI().hideModeOptions();
494 mUI.setCountdownFinishedListener(this);
495 mUI.startCountdown(countDownDuration);
496 // Will take picture later via listener callback.
503 private void decorateSessionAtCaptureTime(CaptureSession session) {
504 String flashSetting =
505 mSettingsManager.getString(mAppController.getCameraScope(),
506 Keys.KEY_FLASH_MODE);
507 boolean gridLinesOn = Keys.areGridLinesOn(mSettingsManager);
508 float timerDuration = mSettingsManager
509 .getInteger(SettingsManager.SCOPE_GLOBAL, Keys.KEY_COUNTDOWN_DURATION);
511 session.getCollector().decorateAtTimeCaptureRequest(
512 eventprotos.NavigationChange.Mode.PHOTO_CAPTURE,
513 session.getTitle() + ".jpg",
514 (mCameraFacing == Facing.FRONT),
520 mLastShutterTouchCoordinate,
521 null /* TODO: Implement Volume Button Shutter Click Instrumentation */,
522 mCameraCharacteristics.getSensorInfoActiveArraySize()
526 private void takePictureNow() {
527 if (mCamera == null) {
528 Log.i(TAG, "Not taking picture since Camera is closed.");
532 CaptureSession session = createAndStartCaptureSession();
533 int orientation = mAppController.getOrientationManager().getDeviceOrientation()
536 // TODO: This should really not use getExternalCacheDir and instead use
537 // the SessionStorage API. Need to sync with gcam if that's OK.
538 PhotoCaptureParameters params = new PhotoCaptureParameters(
539 session.getTitle(), orientation, session.getLocation(),
540 mContext.getExternalCacheDir(), this, mPictureSaverCallback,
541 mHeadingSensor.getCurrentHeading(), mZoomValue, 0);
542 decorateSessionAtCaptureTime(session);
543 mCamera.takePicture(params, session);
547 * Creates, starts and returns a new capture session. The returned session
548 * will have been started with an empty placeholder image.
550 private CaptureSession createAndStartCaptureSession() {
551 long sessionTime = getSessionTime();
552 Location location = mLocationManager.getCurrentLocation();
553 String title = CameraUtil.instance().createJpegName(sessionTime);
554 CaptureSession session = getServices().getCaptureSessionManager()
555 .createNewSession(title, sessionTime, location);
556 session.startEmpty(new Size((int) mPreviewArea.width(), (int) mPreviewArea.height()));
560 private long getSessionTime() {
561 // TODO: Replace with a mockable TimeProvider interface.
562 return System.currentTimeMillis();
566 public void onCountDownFinished() {
567 mAppController.getCameraAppUI().transitionToCapture();
568 mAppController.getCameraAppUI().showModeOptions();
576 public void onRemainingSecondsChanged(int remainingSeconds) {
577 if (remainingSeconds == 1) {
578 mSoundPlayer.play(R.raw.timer_final_second, 0.6f);
579 } else if (remainingSeconds == 2 || remainingSeconds == 3) {
580 mSoundPlayer.play(R.raw.timer_increment, 0.6f);
584 private void cancelCountDown() {
585 if (mUI.isCountingDown()) {
586 // Cancel on-going countdown.
587 mUI.cancelCountDown();
591 mAppController.getCameraAppUI().showModeOptions();
592 mAppController.getCameraAppUI().transitionToCapture();
597 public void onQuickExpose() {
598 mMainThread.execute(new Runnable() {
601 // Starts the short version of the capture animation UI.
602 mAppController.startFlashAnimation(true);
603 mMediaActionSound.play(MediaActionSound.SHUTTER_CLICK);
609 public void onRemoteShutterPress() {
610 Log.d(TAG, "onRemoteShutterPress");
611 // TODO: Check whether shutter is enabled.
615 private void initSurfaceTextureConsumer() {
616 synchronized (mSurfaceTextureLock) {
617 if (mPreviewSurfaceTexture != null) {
618 mPreviewSurfaceTexture.setDefaultBufferSize(
619 mAppController.getCameraAppUI().getSurfaceWidth(),
620 mAppController.getCameraAppUI().getSurfaceHeight());
626 private void reopenCamera() {
631 openCameraAndStartPreview();
634 private SurfaceTexture getPreviewSurfaceTexture() {
635 synchronized (mSurfaceTextureLock) {
636 return mPreviewSurfaceTexture;
640 private void updatePreviewBufferSize() {
641 synchronized (mSurfaceTextureLock) {
642 if (mPreviewSurfaceTexture != null) {
643 mPreviewSurfaceTexture.setDefaultBufferSize(mPreviewBufferWidth,
644 mPreviewBufferHeight);
650 public void resume() {
651 if (mShowErrorAndFinish) {
654 Profile guard = mProfiler.create("CaptureModule.resume").start();
656 // We'll transition into 'ready' once the preview is started.
657 onReadyStateChanged(false);
659 mAppController.addPreviewAreaSizeChangedListener(mPreviewAreaChangedListener);
660 mAppController.addPreviewAreaSizeChangedListener(mUI);
663 getServices().getRemoteShutterListener().onModuleReady(this);
664 guard.mark("getRemoteShutterListener.onModuleReady");
665 mBurstController.initialize(new SurfaceTexture(0));
667 // TODO: Check if we can really take a photo right now (memory, camera
669 mAppController.getCameraAppUI().enableModeOptions();
670 mAppController.setShutterEnabled(true);
671 mAppController.getCameraAppUI().showAccessibilityZoomUI(
672 mCameraCharacteristics.getAvailableMaxDigitalZoom());
674 mHdrPlusEnabled = mStickyGcamCamera || mAppController.getSettingsManager().getInteger(
675 SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR_PLUS) == 1;
677 mHdrSceneEnabled = !mStickyGcamCamera && mAppController.getSettingsManager().getBoolean(
678 SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR);
680 // The lock only exists for HDR and causes trouble for non-HDR
682 // TODO: Fix for removing the locks completely is tracked at b/17985028
683 if (!mHdrPlusEnabled) {
684 mCameraOpenCloseLock.release();
687 // This means we are resuming with an existing preview texture. This
688 // means we will never get the onSurfaceTextureAvailable call. So we
689 // have to open the camera and start the preview here.
690 SurfaceTexture texture = getPreviewSurfaceTexture();
693 if (texture != null) {
694 initSurfaceTextureConsumer();
695 guard.mark("initSurfaceTextureConsumer");
698 mSoundPlayer.loadSound(R.raw.timer_final_second);
699 mSoundPlayer.loadSound(R.raw.timer_increment);
702 mHeadingSensor.activate();
703 guard.stop("mHeadingSensor.activate()");
707 public void pause() {
708 if (mShowErrorAndFinish) {
713 mHeadingSensor.deactivate();
715 mAppController.removePreviewAreaSizeChangedListener(mUI);
716 mAppController.removePreviewAreaSizeChangedListener(mPreviewAreaChangedListener);
717 getServices().getRemoteShutterListener().onModuleExit();
718 mBurstController.release();
720 resetTextureBufferSize();
721 mSoundPlayer.unloadSound(R.raw.timer_final_second);
722 mSoundPlayer.unloadSound(R.raw.timer_increment);
726 public void destroy() {
727 mSoundPlayer.release();
728 mMediaActionSound.release();
729 mCameraHandler.getLooper().quitSafely();
733 public void onLayoutOrientationChanged(boolean isLandscape) {
734 Log.d(TAG, "onLayoutOrientationChanged");
738 public void onCameraAvailable(CameraProxy cameraProxy) {
739 // Ignore since we manage the camera ourselves until we remove this.
743 public void hardResetSettings(SettingsManager settingsManager) {
744 if (mStickyGcamCamera) {
745 // Sticky HDR+ mode should hard reset HDR+ to on, and camera back
747 settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR_PLUS, true);
748 settingsManager.set(mAppController.getModuleScope(), Keys.KEY_CAMERA_ID,
749 mOneCameraManager.findFirstCameraFacing(Facing.BACK).getValue());
754 public HardwareSpec getHardwareSpec() {
755 return new HardwareSpec() {
757 public boolean isFrontCameraSupported() {
762 public boolean isHdrSupported() {
763 if (ApiHelper.IS_NEXUS_4 && is16by9AspectRatio(mPictureSize)) {
764 Log.v(TAG, "16:9 N4, no HDR support");
767 return mCameraCharacteristics.isHdrSceneSupported();
772 public boolean isHdrPlusSupported() {
773 OneCameraFeatureConfig featureConfig = mAppController.getCameraFeatureConfig();
774 return featureConfig.getHdrPlusSupportLevel(mCameraFacing) !=
775 OneCameraFeatureConfig.HdrPlusSupportLevel.NONE;
779 public boolean isFlashSupported() {
780 return mCameraCharacteristics.isFlashSupported();
786 public BottomBarUISpec getBottomBarSpec() {
787 HardwareSpec hardwareSpec = getHardwareSpec();
788 BottomBarUISpec bottomBarSpec = new BottomBarUISpec();
789 bottomBarSpec.enableGridLines = true;
790 bottomBarSpec.enableCamera = true;
791 bottomBarSpec.cameraCallback = getCameraCallback();
792 bottomBarSpec.enableHdr =
793 hardwareSpec.isHdrSupported() || hardwareSpec.isHdrPlusSupported();
794 bottomBarSpec.hdrCallback = getHdrButtonCallback();
795 bottomBarSpec.enableSelfTimer = true;
796 bottomBarSpec.showSelfTimer = true;
797 bottomBarSpec.isExposureCompensationSupported = mCameraCharacteristics
798 .isExposureCompensationSupported();
799 bottomBarSpec.enableExposureCompensation = bottomBarSpec.isExposureCompensationSupported;
801 // We must read the key from the settings because the button callback
802 // is not executed until after this method is called.
803 if ((hardwareSpec.isHdrPlusSupported() &&
804 mAppController.getSettingsManager().getBoolean(
805 SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR_PLUS)) ||
806 ( hardwareSpec.isHdrSupported() &&
807 mAppController.getSettingsManager().getBoolean(
808 SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR))) {
809 // Disable flash if this is a sticky gcam camera, or if
811 bottomBarSpec.enableFlash = false;
812 // Disable manual exposure if HDR is enabled.
813 bottomBarSpec.enableExposureCompensation = false;
815 // If we are not in HDR / GCAM mode, fallback on the
816 // flash supported property and manual exposure supported property
818 bottomBarSpec.enableFlash = mCameraCharacteristics.isFlashSupported();
821 bottomBarSpec.minExposureCompensation =
822 mCameraCharacteristics.getMinExposureCompensation();
823 bottomBarSpec.maxExposureCompensation =
824 mCameraCharacteristics.getMaxExposureCompensation();
825 bottomBarSpec.exposureCompensationStep =
826 mCameraCharacteristics.getExposureCompensationStep();
827 bottomBarSpec.exposureCompensationSetCallback =
828 new BottomBarUISpec.ExposureCompensationSetCallback() {
830 public void setExposure(int value) {
831 mSettingsManager.set(
832 mAppController.getCameraScope(), Keys.KEY_EXPOSURE, value);
836 return bottomBarSpec;
840 public boolean isUsingBottomBar() {
845 public boolean onKeyDown(int keyCode, KeyEvent event) {
847 case KeyEvent.KEYCODE_CAMERA:
848 case KeyEvent.KEYCODE_DPAD_CENTER:
849 if (mUI.isCountingDown()) {
851 } else if (event.getRepeatCount() == 0) {
852 onShutterButtonClick();
855 case KeyEvent.KEYCODE_VOLUME_UP:
856 case KeyEvent.KEYCODE_VOLUME_DOWN:
864 public boolean onKeyUp(int keyCode, KeyEvent event) {
866 case KeyEvent.KEYCODE_VOLUME_UP:
867 case KeyEvent.KEYCODE_VOLUME_DOWN:
868 onShutterButtonClick();
874 // TODO: Consider refactoring FocusOverlayManager.
875 // Currently AF state transitions are controlled in OneCameraImpl.
876 // PhotoModule uses FocusOverlayManager which uses API1/portability
877 // logic and coordinates.
878 private void startActiveFocusAt(int viewX, int viewY) {
879 if (mCamera == null) {
880 // If we receive this after the camera is closed, do nothing.
884 // TODO: make mFocusController final and remove null check.
885 if (mFocusController == null) {
886 Log.v(TAG, "CaptureModule mFocusController is null!");
889 mFocusController.showActiveFocusAt(viewX, viewY);
891 // Normalize coordinates to [0,1] per CameraOne API.
892 float points[] = new float[2];
893 points[0] = (viewX - mPreviewArea.left) / mPreviewArea.width();
894 points[1] = (viewY - mPreviewArea.top) / mPreviewArea.height();
896 // Rotate coordinates to portrait orientation per CameraOne API.
897 Matrix rotationMatrix = new Matrix();
898 rotationMatrix.setRotate(mDisplayRotation, 0.5f, 0.5f);
899 rotationMatrix.mapPoints(points);
901 // Invert X coordinate on front camera since the display is mirrored.
902 if (mCameraCharacteristics.getCameraDirection() == Facing.FRONT) {
903 points[0] = 1 - points[0];
906 mCamera.triggerFocusAndMeterAtPoint(points[0], points[1]);
908 // Log touch (screen coordinates).
909 if (mZoomValue == 1f) {
910 TouchCoordinate touchCoordinate = new TouchCoordinate(
911 viewX - mPreviewArea.left,
912 viewY - mPreviewArea.top,
913 mPreviewArea.width(),
914 mPreviewArea.height());
915 // TODO: Add to logging: duration, rotation.
916 UsageStatistics.instance().tapToFocus(touchCoordinate, null);
921 * Show AF target in center of preview.
923 private void startPassiveFocus() {
924 // TODO: make mFocusController final and remove null check.
925 if (mFocusController == null) {
929 // TODO: Some passive focus scans may trigger on a location
930 // instead of the center of the screen.
931 mFocusController.showPassiveFocusAtCenter();
935 * Update UI based on AF state changes.
938 public void onFocusStatusUpdate(final AutoFocusState state, long frameNumber) {
939 Log.v(TAG, "AF status is state:" + state);
946 // Unused, manual scans are triggered via the UI
948 case PASSIVE_FOCUSED:
949 case PASSIVE_UNFOCUSED:
953 case ACTIVE_UNFOCUSED:
958 if (CAPTURE_DEBUG_UI) {
959 measureAutoFocusScans(state, frameNumber);
963 private void measureAutoFocusScans(final AutoFocusState state, long frameNumber) {
964 // Log AF scan lengths.
965 boolean passive = false;
969 if (mAutoFocusScanStartFrame == -1) {
970 mAutoFocusScanStartFrame = frameNumber;
971 mAutoFocusScanStartTime = SystemClock.uptimeMillis();
974 case PASSIVE_FOCUSED:
975 case PASSIVE_UNFOCUSED:
978 case ACTIVE_UNFOCUSED:
979 if (mAutoFocusScanStartFrame != -1) {
980 long frames = frameNumber - mAutoFocusScanStartFrame;
981 long dt = SystemClock.uptimeMillis() - mAutoFocusScanStartTime;
982 int fps = Math.round(frames * 1000f / dt);
983 String report = String.format("%s scan: fps=%d frames=%d",
984 passive ? "CAF" : "AF", fps, frames);
986 mUI.showDebugMessage(String.format("%d / %d", frames, fps));
987 mAutoFocusScanStartFrame = -1;
994 public void onReadyStateChanged(boolean readyForCapture) {
995 if (readyForCapture) {
996 mAppController.getCameraAppUI().enableModeOptions();
998 mAppController.setShutterEnabled(readyForCapture);
1002 public String getPeekAccessibilityString() {
1003 return mAppController.getAndroidContext()
1004 .getResources().getString(R.string.photo_accessibility_peek);
1008 public void onThumbnailResult(byte[] jpegData) {
1009 getServices().getRemoteShutterListener().onPictureTaken(jpegData);
1013 public void onPictureTaken(CaptureSession session) {
1014 mAppController.getCameraAppUI().enableModeOptions();
1018 public void onPictureSaved(Uri uri) {
1019 mAppController.notifyNewMedia(uri);
1023 public void onTakePictureProgress(float progress) {
1024 mUI.setPictureTakingProgress((int) (progress * 100));
1028 public void onPictureTakingFailed() {
1029 mAppController.getFatalErrorHandler().onMediaStorageFailure();
1033 * Updates the preview transform matrix to adapt to the current preview
1034 * width, height, and orientation.
1036 public void updatePreviewTransform() {
1039 synchronized (mDimensionLock) {
1040 width = mScreenWidth;
1041 height = mScreenHeight;
1043 updatePreviewTransform(width, height);
1047 * @return Depending on whether we're in sticky-HDR mode or not, return the
1048 * proper callback to be used for when the HDR/HDR+ button is
1051 private ButtonManager.ButtonCallback getHdrButtonCallback() {
1052 if (mStickyGcamCamera) {
1053 return new ButtonManager.ButtonCallback() {
1055 public void onStateChanged(int state) {
1059 if (state == ButtonManager.ON) {
1060 throw new IllegalStateException(
1061 "Can't leave hdr plus mode if switching to hdr plus mode.");
1063 SettingsManager settingsManager = mAppController.getSettingsManager();
1064 settingsManager.set(mAppController.getModuleScope(),
1065 Keys.KEY_REQUEST_RETURN_HDR_PLUS, false);
1066 switchToRegularCapture();
1070 return new ButtonManager.ButtonCallback() {
1072 public void onStateChanged(int hdrEnabled) {
1077 // Only reload the camera if we are toggling HDR+.
1078 if (GcamHelper.hasGcamCapture(mAppController.getCameraFeatureConfig())) {
1079 mHdrPlusEnabled = hdrEnabled == 1;
1082 mHdrSceneEnabled = hdrEnabled == 1;
1090 * @return Depending on whether we're in sticky-HDR mode or not, this
1091 * returns the proper callback to be used for when the camera
1092 * (front/back switch) button is pressed.
1094 private ButtonManager.ButtonCallback getCameraCallback() {
1095 if (mStickyGcamCamera) {
1096 return new ButtonManager.ButtonCallback() {
1098 public void onStateChanged(int state) {
1103 // At the time this callback is fired, the camera id setting
1104 // has changed to the desired camera.
1105 SettingsManager settingsManager = mAppController.getSettingsManager();
1106 if (Keys.isCameraBackFacing(settingsManager,
1107 mAppController.getModuleScope())) {
1108 throw new IllegalStateException(
1109 "Hdr plus should never be switching from front facing camera.");
1112 // Switch to photo mode, but request a return to hdr plus on
1113 // switching to back camera again.
1114 settingsManager.set(mAppController.getModuleScope(),
1115 Keys.KEY_REQUEST_RETURN_HDR_PLUS, true);
1116 switchToRegularCapture();
1120 return new ButtonManager.ButtonCallback() {
1122 public void onStateChanged(int cameraId) {
1127 // At the time this callback is fired, the camera id
1128 // has be set to the desired camera.
1129 mSettingsManager.set(mAppController.getModuleScope(), Keys.KEY_CAMERA_ID,
1132 Log.d(TAG, "Start to switch camera. cameraId=" + cameraId);
1133 mCameraFacing = getFacingFromCameraId(cameraId);
1134 mShowErrorAndFinish = !updateCameraCharacteristics();
1142 * Switches to PhotoModule to do regular photo captures.
1144 * TODO: Remove this once we use CaptureModule for photo taking.
1146 private void switchToRegularCapture() {
1147 // Turn off HDR+ before switching back to normal photo mode.
1148 SettingsManager settingsManager = mAppController.getSettingsManager();
1149 settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR_PLUS, false);
1151 // Disable this button to prevent callbacks from this module from firing
1152 // while we are transitioning modules.
1153 ButtonManager buttonManager = mAppController.getButtonManager();
1154 buttonManager.disableButtonClick(ButtonManager.BUTTON_HDR_PLUS);
1155 mAppController.getCameraAppUI().freezeScreenUntilPreviewReady();
1156 mAppController.onModeSelected(mContext.getResources().getInteger(
1157 R.integer.camera_mode_photo));
1158 buttonManager.enableButtonClick(ButtonManager.BUTTON_HDR_PLUS);
1162 * Called when the preview started. Informs the app controller and queues a
1163 * transform update when the next preview frame arrives.
1165 private void onPreviewStarted() {
1166 if (mState == ModuleState.WATCH_FOR_NEXT_FRAME_AFTER_PREVIEW_STARTED) {
1167 mState = ModuleState.UPDATE_TRANSFORM_ON_NEXT_SURFACE_TEXTURE_UPDATE;
1169 mAppController.onPreviewStarted();
1173 * Update the preview transform based on the new dimensions. Will not force
1174 * an update, if it's not necessary.
1176 private void updatePreviewTransform(int incomingWidth, int incomingHeight) {
1177 updatePreviewTransform(incomingWidth, incomingHeight, false);
1181 * Returns whether it is necessary to apply device-specific fix for b/19271661
1182 * on the AutoTransform Path, i.e. USE_AUTOTRANSFORM_UI_LAYOUT == true
1184 * @return whether to apply workaround fix for b/19271661
1186 private boolean requiresNexus4SpecificFixFor16By9Previews() {
1187 return USE_AUTOTRANSFORM_UI_LAYOUT && ApiHelper.IS_NEXUS_4
1188 && is16by9AspectRatio(mPictureSize);
1192 * Update the preview transform based on the new dimensions. TODO: Make work
1193 * with all: aspect ratios/resolutions x screens/cameras.
1195 private void updatePreviewTransform(int incomingWidth, int incomingHeight,
1196 boolean forceUpdate) {
1197 Log.d(TAG, "updatePreviewTransform: " + incomingWidth + " x " + incomingHeight);
1199 synchronized (mDimensionLock) {
1200 int incomingRotation = CameraUtil.getDisplayRotation();
1201 // Check for an actual change:
1202 if (mScreenHeight == incomingHeight && mScreenWidth == incomingWidth &&
1203 incomingRotation == mDisplayRotation && !forceUpdate) {
1206 // Update display rotation and dimensions
1207 mDisplayRotation = incomingRotation;
1208 mScreenWidth = incomingWidth;
1209 mScreenHeight = incomingHeight;
1210 updatePreviewBufferDimension();
1213 // - Aspect ratio for the sensor buffers is in landscape
1215 // - Dimensions of buffers received are rotated to the natural
1216 // device orientation.
1217 // - The contents of each buffer are rotated by the inverse of
1218 // the display rotation.
1219 // - Surface scales the buffer to fit the current view bounds.
1221 // Get natural orientation and buffer dimensions
1223 if(USE_AUTOTRANSFORM_UI_LAYOUT) {
1224 // Use PhotoUI-based AutoTransformation Interface
1225 if (mPreviewBufferWidth != 0 && mPreviewBufferHeight != 0) {
1226 if (requiresNexus4SpecificFixFor16By9Previews()) {
1227 // Force preview size to be 16:9, even though surface is 4:3
1228 // Surface content is assumed to be 16:9.
1229 mAppController.updatePreviewAspectRatio(16.f / 9.f);
1231 mAppController.updatePreviewAspectRatio(
1232 mPreviewBufferWidth / (float) mPreviewBufferHeight);
1236 Matrix transformMatrix = mPreviewTransformCalculator.toTransformMatrix(
1237 new Size(mScreenWidth, mScreenHeight),
1238 new Size(mPreviewBufferWidth, mPreviewBufferHeight));
1239 mAppController.updatePreviewTransform(transformMatrix);
1246 * Calculates whether a picture size is 16:9 ratio, regardless of its
1249 * @param size the size of the picture to be considered
1250 * @return true, if the picture is 16:9; false if it's invalid or size is null
1252 private boolean is16by9AspectRatio(Size size) {
1253 if (size == null || size.getWidth() == 0 || size.getHeight() == 0) {
1257 // Normalize aspect ratio to be greater than 1.
1258 final float aspectRatio = (size.getHeight() > size.getWidth())
1259 ? (size.getHeight() / (float) size.getWidth())
1260 : (size.getWidth() / (float) size.getHeight());
1262 return Math.abs(aspectRatio - (16.f / 9.f)) < 0.001f;
1266 * Based on the current picture size, selects the best preview dimension and
1267 * stores it in {@link #mPreviewBufferWidth} and
1268 * {@link #mPreviewBufferHeight}.
1270 private void updatePreviewBufferDimension() {
1271 if (mCamera == null) {
1275 Size previewBufferSize = mCamera.pickPreviewSize(mPictureSize, mContext);
1276 mPreviewBufferWidth = previewBufferSize.getWidth();
1277 mPreviewBufferHeight = previewBufferSize.getHeight();
1279 // Workaround for N4 TextureView/HAL issues b/19271661 for 16:9 preview
1281 if (requiresNexus4SpecificFixFor16By9Previews()) {
1282 // Override the preview selection logic to the largest N4 4:3
1283 // preview size but pass in 16:9 aspect ratio in
1284 // UpdatePreviewAspectRatio later.
1285 mPreviewBufferWidth = 1280;
1286 mPreviewBufferHeight = 960;
1288 updatePreviewBufferSize();
1292 * Open camera and start the preview.
1294 private void openCameraAndStartPreview() {
1295 Profile guard = mProfiler.create("CaptureModule.openCameraAndStartPreview()").start();
1297 // TODO Given the current design, we cannot guarantee that one of
1298 // CaptureReadyCallback.onSetupFailed or onReadyForCapture will
1299 // be called (see below), so it's possible that
1300 // mCameraOpenCloseLock.release() is never called under extremely
1301 // rare cases. If we leak the lock, this timeout ensures that we at
1302 // least crash so we don't deadlock the app.
1303 if (!mCameraOpenCloseLock.tryAcquire(CAMERA_OPEN_CLOSE_TIMEOUT_MILLIS,
1304 TimeUnit.MILLISECONDS)) {
1305 throw new RuntimeException("Time out waiting to acquire camera-open lock.");
1307 } catch (InterruptedException e) {
1308 throw new RuntimeException("Interrupted while waiting to acquire camera-open lock.", e);
1311 guard.mark("Acquired mCameraOpenCloseLock");
1313 if (mOneCameraOpener == null) {
1314 Log.e(TAG, "no available OneCameraManager, showing error dialog");
1315 mCameraOpenCloseLock.release();
1316 mAppController.getFatalErrorHandler().onGenericCameraAccessFailure();
1317 guard.stop("No OneCameraManager");
1320 if (mCamera != null) {
1321 // If the camera is already open, do nothing.
1322 Log.d(TAG, "Camera already open, not re-opening.");
1323 mCameraOpenCloseLock.release();
1324 guard.stop("Camera is already open");
1328 // Derive objects necessary for camera creation.
1329 MainThread mainThread = MainThread.create();
1330 ImageRotationCalculator imageRotationCalculator = ImageRotationCalculatorImpl
1331 .from(mAppController.getOrientationManager(), mCameraCharacteristics);
1333 // Only enable GCam on the back camera
1334 boolean useHdr = mHdrPlusEnabled && mCameraFacing == Facing.BACK;
1336 CameraId cameraId = mOneCameraManager.findFirstCameraFacing(mCameraFacing);
1337 final String settingScope = SettingsManager.getCameraSettingScope(cameraId.getValue());
1339 OneCameraCaptureSetting captureSetting;
1340 // Read the preferred picture size from the setting.
1342 mPictureSize = mAppController.getResolutionSetting().getPictureSize(
1343 cameraId, mCameraFacing);
1344 captureSetting = OneCameraCaptureSetting.create(mPictureSize, mSettingsManager,
1345 getHardwareSpec(), settingScope, useHdr);
1346 } catch (OneCameraAccessException ex) {
1347 mAppController.getFatalErrorHandler().onGenericCameraAccessFailure();
1351 mOneCameraOpener.open(cameraId, captureSetting, mCameraHandler, mainThread,
1352 imageRotationCalculator, mBurstController, mSoundPlayer,
1353 new OpenCallback() {
1355 public void onFailure() {
1356 Log.e(TAG, "Could not open camera.");
1358 mCameraOpenCloseLock.release();
1359 mAppController.getFatalErrorHandler().onCameraOpenFailure();
1363 public void onCameraClosed() {
1365 mCameraOpenCloseLock.release();
1369 public void onCameraOpened(final OneCamera camera) {
1370 Log.d(TAG, "onCameraOpened: " + camera);
1373 // When camera is opened, the zoom is implicitly reset to 1.0f
1376 updatePreviewBufferDimension();
1378 // If the surface texture is not destroyed, it may have
1379 // the last frame lingering. We need to hold off setting
1380 // transform until preview is started.
1381 updatePreviewBufferSize();
1382 mState = ModuleState.WATCH_FOR_NEXT_FRAME_AFTER_PREVIEW_STARTED;
1383 Log.d(TAG, "starting preview ...");
1385 // TODO: make mFocusController final and remove null
1387 if (mFocusController != null) {
1388 camera.setFocusDistanceListener(mFocusController);
1391 mMainThread.execute(new Runnable() {
1394 mAppController.getCameraAppUI().onChangeCamera();
1398 // TODO: Consider rolling these two calls into one.
1399 camera.startPreview(new Surface(getPreviewSurfaceTexture()),
1400 new CaptureReadyCallback() {
1402 public void onSetupFailed() {
1403 // We must release this lock here,
1404 // before posting to the main handler
1405 // since we may be blocked in pause(),
1406 // getting ready to close the camera.
1407 mCameraOpenCloseLock.release();
1408 Log.e(TAG, "Could not set up preview.");
1409 mMainThread.execute(new Runnable() {
1412 if (mCamera == null) {
1413 Log.d(TAG, "Camera closed, aborting.");
1418 // TODO: Show an error message
1425 public void onReadyForCapture() {
1426 // We must release this lock here,
1427 // before posting to the main handler
1428 // since we may be blocked in pause(),
1429 // getting ready to close the camera.
1430 mCameraOpenCloseLock.release();
1431 mMainThread.execute(new Runnable() {
1434 Log.d(TAG, "Ready for capture.");
1435 if (mCamera == null) {
1436 Log.d(TAG, "Camera closed, aborting.");
1440 // May be overridden by
1441 // subsequent call to
1442 // onReadyStateChanged().
1443 onReadyStateChanged(true);
1444 mCamera.setReadyStateChangedListener(
1445 CaptureModule.this);
1446 // Enable zooming after preview
1448 mUI.initializeZoom(mCamera.getMaxZoom());
1449 mCamera.setFocusStateListener(CaptureModule.this);
1455 }, mAppController.getFatalErrorHandler());
1456 guard.stop("mOneCameraOpener.open()");
1459 private void closeCamera() {
1460 Profile profile = mProfiler.create("CaptureModule.closeCamera()").start();
1462 mCameraOpenCloseLock.acquire();
1463 } catch (InterruptedException e) {
1464 throw new RuntimeException("Interrupted while waiting to acquire camera-open lock.", e);
1466 profile.mark("mCameraOpenCloseLock.acquire()");
1468 if (mCamera != null) {
1470 profile.mark("mCamera.close()");
1471 mCamera.setFocusStateListener(null);
1475 mCameraOpenCloseLock.release();
1481 * Re-initialize the camera if e.g. the HDR mode or facing property changed.
1483 private void switchCamera() {
1484 if (mShowErrorAndFinish) {
1491 mAppController.freezeScreenUntilPreviewReady();
1492 initSurfaceTextureConsumer();
1496 * Returns which way around the camera is facing, based on it's ID.
1498 * TODO: This needs to change so that we store the direction directly in the
1499 * settings, rather than a Camera ID.
1501 private static Facing getFacingFromCameraId(int cameraId) {
1502 return cameraId == 1 ? Facing.FRONT : Facing.BACK;
1505 private void resetTextureBufferSize() {
1506 // According to the documentation for
1507 // SurfaceTexture.setDefaultBufferSize,
1508 // photo and video based image producers (presumably only Camera 1 api),
1509 // override this buffer size. Any module that uses egl to render to a
1510 // SurfaceTexture must have these buffer sizes reset manually. Otherwise
1511 // the SurfaceTexture cannot be transformed by matrix set on the
1513 updatePreviewBufferSize();