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