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