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