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