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