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