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