CaptureModule.java revision 0cf4a02c13a7710dc9b26ac39bea15a95ac48baf
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.ContentResolver; 21import android.content.Context; 22import android.content.res.Configuration; 23import android.graphics.Bitmap; 24import android.graphics.Matrix; 25import android.graphics.RectF; 26import android.graphics.SurfaceTexture; 27import android.hardware.Sensor; 28import android.hardware.SensorEvent; 29import android.hardware.SensorEventListener; 30import android.hardware.SensorManager; 31import android.net.Uri; 32import android.os.Handler; 33import android.provider.MediaStore; 34import android.util.Size; 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.MediaSaver; 46import com.android.camera.debug.Log; 47import com.android.camera.debug.Log.Tag; 48import com.android.camera.hardware.HardwareSpec; 49import com.android.camera.module.ModuleController; 50import com.android.camera.one.OneCamera; 51import com.android.camera.one.OneCamera.CaptureReadyCallback; 52import com.android.camera.one.OneCamera.Facing; 53import com.android.camera.one.OneCamera.OpenCallback; 54import com.android.camera.one.OneCamera.PhotoCaptureParameters; 55import com.android.camera.one.OneCamera.PhotoCaptureParameters.Flash; 56import com.android.camera.one.OneCameraManager; 57import com.android.camera.remote.RemoteCameraModule; 58import com.android.camera.session.CaptureSession; 59import com.android.camera.settings.Keys; 60import com.android.camera.settings.ResolutionUtil; 61import com.android.camera.settings.SettingsManager; 62import com.android.camera.ui.PreviewStatusListener; 63import com.android.camera.ui.TouchCoordinate; 64import com.android.camera.util.CameraUtil; 65import com.android.camera2.R; 66import com.android.ex.camera2.portability.CameraAgent.CameraProxy; 67 68/** 69 * New Capture module that is made to support photo and video capture on top of 70 * the OneCamera API, to transparently support GCam. 71 * <p> 72 * This has been a re-write with pieces taken and improved from GCamModule and 73 * PhotoModule, which are to be retired eventually. 74 * <p> 75 * TODO: 76 * <ul> 77 * <li>Server-side logging 78 * <li>Focusing 79 * <li>Show location dialog 80 * <li>Show resolution dialog on certain devices 81 * <li>Store location 82 * <li>Timer 83 * <li>Capture intent 84 * </ul> 85 */ 86public class CaptureModule extends CameraModule 87 implements MediaSaver.QueueListener, 88 ModuleController, 89 OneCamera.PictureCallback, 90 PreviewStatusListener.PreviewAreaChangedListener, 91 RemoteCameraModule, 92 SensorEventListener, 93 SettingsManager.OnSettingChangedListener, 94 TextureView.SurfaceTextureListener { 95 96 /** 97 * Called on layout changes. 98 */ 99 private final OnLayoutChangeListener mLayoutListener = new OnLayoutChangeListener() { 100 @Override 101 public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, 102 int oldTop, int oldRight, int oldBottom) { 103 int width = right - left; 104 int height = bottom - top; 105 updatePreviewTransform(width, height, false); 106 } 107 }; 108 109 /** 110 * Called when the captured media has been saved. 111 */ 112 private final MediaSaver.OnMediaSavedListener mOnMediaSavedListener = 113 new MediaSaver.OnMediaSavedListener() { 114 @Override 115 public void onMediaSaved(Uri uri) { 116 if (uri != null) { 117 mAppController.notifyNewMedia(uri); 118 } 119 } 120 }; 121 122 /** 123 * Called when the user pressed the back/front camera switch button. 124 */ 125 private final ButtonManager.ButtonCallback mCameraSwitchCallback = 126 new ButtonManager.ButtonCallback() { 127 @Override 128 public void onStateChanged(int cameraId) { 129 // At the time this callback is fired, the camera id 130 // has be set to the desired camera. 131 if (mPaused) { 132 return; 133 } 134 135 mSettingsManager.set(mAppController.getModuleScope(), Keys.KEY_CAMERA_ID, 136 cameraId); 137 138 Log.d(TAG, "Start to switch camera. cameraId=" + cameraId); 139 switchCamera(getFacingFromCameraId(cameraId)); 140 } 141 }; 142 143 private static final Tag TAG = new Tag("CaptureModule"); 144 private static final String PHOTO_MODULE_STRING_ID = "PhotoModule"; 145 /** Enable additional debug output. */ 146 private static final boolean DEBUG = true; 147 /** 148 * This is the delay before we execute onResume tasks when coming from the 149 * lock screen, to allow time for onPause to execute. 150 * <p> 151 * TODO: Make sure this value is in sync with what we see on L. 152 */ 153 private static final int ON_RESUME_TASKS_DELAY_MSEC = 20; 154 155 private final Object mDimensionLock = new Object(); 156 /** 157 * Lock for race conditions in the SurfaceTextureListener callbacks. 158 */ 159 private final Object mSurfaceLock = new Object(); 160 /** Controller giving us access to other services. */ 161 private final AppController mAppController; 162 /** The applications settings manager. */ 163 private final SettingsManager mSettingsManager; 164 /** Application context. */ 165 private final Context mContext; 166 private CaptureModuleUI mUI; 167 /** Your standard content resolver. */ 168 private ContentResolver mContentResolver; 169 /** The camera manager used to open cameras. */ 170 private OneCameraManager mCameraManager; 171 /** The currently opened camera device. */ 172 private OneCamera mCamera; 173 /** The direction the currently opened camera is facing to. */ 174 private Facing mCameraFacing = Facing.BACK; 175 /** The texture used to render the preview in. */ 176 private SurfaceTexture mPreviewTexture; 177 178 /** State by the module state machine. */ 179 private static enum ModuleState { 180 IDLE, 181 WATCH_FOR_NEXT_FRAME_AFTER_PREVIEW_STARTED, 182 UPDATE_TRANSFORM_ON_NEXT_SURFACE_TEXTURE_UPDATE, 183 } 184 185 /** The current state of the module. */ 186 private ModuleState mState = ModuleState.IDLE; 187 /** Current orientation of the device. */ 188 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN; 189 190 /** Accelerometer data. */ 191 private final float[] mGData = new float[3]; 192 /** Magnetic sensor data. */ 193 private final float[] mMData = new float[3]; 194 /** Temporary rotation matrix. */ 195 private final float[] mR = new float[16]; 196 /** Current compass heading. */ 197 private int mHeading = -1; 198 199 /** Whether the module is paused right now. */ 200 private boolean mPaused; 201 202 /** Whether this module was resumed from lockscreen capture intent. */ 203 private boolean mIsResumeFromLockScreen = false; 204 205 private final Runnable mResumeTaskRunnable = new Runnable() { 206 @Override 207 public void run() { 208 onResumeTasks(); 209 } 210 }; 211 212 /** Main thread handler. */ 213 private Handler mMainHandler; 214 215 /** Current display rotation in degrees. */ 216 private int mDisplayRotation; 217 /** Current width of the screen, in pixels. */ 218 private int mScreenWidth; 219 /** Current height of the screen, in pixels. */ 220 private int mScreenHeight; 221 /** Current preview width, in pixels. */ 222 private int mPreviewBufferWidth; 223 /** Current preview height, in pixels. */ 224 private int mPreviewBufferHeight; 225 226 // /** Current preview area width. */ 227 // private float mFullPreviewWidth; 228 // /** Current preview area height. */ 229 // private float mFullPreviewHeight; 230 231 /** The current preview transformation matrix. */ 232 private Matrix mPreviewTranformationMatrix = new Matrix(); 233 /** TODO: This is N5 specific. */ 234 public static final float FULLSCREEN_ASPECT_RATIO = 16 / 9f; 235 236 /** 237 * Desires aspect ratio of the final image. 238 * <p> 239 * TODO: Can't we deduct this from the final image's resolution? 240 */ 241 private Float mFinalAspectRatio; 242 243 /** CLEAN UP START */ 244 // private SoundPool mSoundPool; 245 // private int mCaptureStartSoundId; 246 // private static final int NO_SOUND_STREAM = -999; 247 // private final int mCaptureStartSoundStreamId = NO_SOUND_STREAM; 248 // private int mCaptureDoneSoundId; 249 // private SoundClips.Player mSoundPlayer; 250 // private boolean mFirstLayout; 251 // private int[] mTargetFPSRanges; 252 // private float mZoomValue; 253 // private int mSensorOrientation; 254 // private int mLensFacing; 255 // private volatile float mMaxZoomRatio = 1.0f; 256 // private String mFlashMode; 257 /** CLEAN UP END */ 258 259 /** Constructs a new capture module. */ 260 public CaptureModule(AppController appController) { 261 super(appController); 262 mAppController = appController; 263 mContext = mAppController.getAndroidContext(); 264 mSettingsManager = mAppController.getSettingsManager(); 265 mSettingsManager.addListener(this); 266 } 267 268 @Override 269 public void init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent) { 270 Log.d(TAG, "init"); 271 mIsResumeFromLockScreen = isResumeFromLockscreen(activity); 272 mMainHandler = new Handler(activity.getMainLooper()); 273 mCameraManager = mAppController.getCameraManager(); 274 mContentResolver = activity.getContentResolver(); 275 mDisplayRotation = CameraUtil.getDisplayRotation(mContext); 276 mCameraFacing = getFacingFromCameraId(mSettingsManager.getInteger( 277 mAppController.getModuleScope(), 278 Keys.KEY_CAMERA_ID)); 279 mUI = new CaptureModuleUI(activity, this, mAppController.getModuleLayoutRoot(), 280 mLayoutListener); 281 mAppController.setPreviewStatusListener(mUI); 282 mPreviewTexture = mAppController.getCameraAppUI().getSurfaceTexture(); 283 if (mPreviewTexture != null) { 284 initSurface(mPreviewTexture); 285 } 286 } 287 288 @Override 289 public void onShutterButtonFocus(boolean pressed) { 290 // TODO Auto-generated method stub 291 } 292 293 @Override 294 public void onShutterCoordinate(TouchCoordinate coord) { 295 // TODO Auto-generated method stub 296 } 297 298 @Override 299 public void onShutterButtonClick() { 300 // TODO: Add focusing. 301 if (mCamera == null) { 302 return; 303 } 304 mAppController.setShutterEnabled(false); 305 306 // Set up the capture session. 307 long sessionTime = System.currentTimeMillis(); 308 String title = CameraUtil.createJpegName(sessionTime); 309 CaptureSession session = getServices().getCaptureSessionManager() 310 .createNewSession(title, sessionTime, null); 311 312 // TODO: Add location. 313 314 // Set up the parameters for this capture. 315 PhotoCaptureParameters params = new PhotoCaptureParameters(); 316 params.title = title; 317 params.callback = this; 318 params.orientation = getOrientation(); 319 params.flashMode = getFlashModeFromSettings(); 320 params.heading = mHeading; 321 322 // Take the picture. 323 mCamera.takePicture(params, session); 324 } 325 326 @Override 327 public void onPreviewAreaChanged(RectF previewArea) { 328 // mUI.updatePreviewAreaRect(previewArea); 329 // mUI.positionProgressOverlay(previewArea); 330 } 331 332 @Override 333 public void onSensorChanged(SensorEvent event) { 334 // This is literally the same as the GCamModule implementation. 335 int type = event.sensor.getType(); 336 float[] data; 337 if (type == Sensor.TYPE_ACCELEROMETER) { 338 data = mGData; 339 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) { 340 data = mMData; 341 } else { 342 Log.w(TAG, String.format("Unexpected sensor type %s", event.sensor.getName())); 343 return; 344 } 345 for (int i = 0; i < 3; i++) { 346 data[i] = event.values[i]; 347 } 348 float[] orientation = new float[3]; 349 SensorManager.getRotationMatrix(mR, null, mGData, mMData); 350 SensorManager.getOrientation(mR, orientation); 351 mHeading = (int) (orientation[0] * 180f / Math.PI) % 360; 352 if (mHeading < 0) { 353 mHeading += 360; 354 } 355 } 356 357 @Override 358 public void onAccuracyChanged(Sensor sensor, int accuracy) { 359 // TODO Auto-generated method stub 360 } 361 362 @Override 363 public void onQueueStatus(boolean full) { 364 // TODO Auto-generated method stub 365 } 366 367 @Override 368 public void onRemoteShutterPress() { 369 // TODO: Check whether shutter is enabled. 370 onShutterButtonClick(); 371 } 372 373 @Override 374 public void onSurfaceTextureAvailable(final SurfaceTexture surface, int width, int height) { 375 Log.d(TAG, "onSurfaceTextureAvailable"); 376 // Force to re-apply transform matrix here as a workaround for 377 // b/11168275 378 updatePreviewTransform(width, height, true); 379 initSurface(surface); 380 } 381 382 public void initSurface(final SurfaceTexture surface) { 383 mPreviewTexture = surface; 384 closeCamera(); 385 386 mCameraManager.open(mCameraFacing, getPictureSizeFromSettings(), new OpenCallback() { 387 @Override 388 public void onFailure() { 389 Log.e(TAG, "Could not open camera."); 390 mCamera = null; 391 mAppController.showErrorAndFinish(R.string.cannot_connect_camera); 392 } 393 394 @Override 395 public void onCameraOpened(final OneCamera camera) { 396 Log.d(TAG, "onCameraOpened: " + camera); 397 mCamera = camera; 398 updateBufferDimension(); 399 400 // If the surface texture is not destroyed, it may have the last 401 // frame lingering. 402 // We need to hold off setting transform until preview is 403 // started. 404 resetDefaultBufferSize(); 405 mState = ModuleState.WATCH_FOR_NEXT_FRAME_AFTER_PREVIEW_STARTED; 406 407 Log.d(TAG, "starting preview ..."); 408 409 // TODO: Consider rolling these two calls into one. 410 camera.startPreview(new Surface(surface), new CaptureReadyCallback() { 411 412 @Override 413 public void onSetupFailed() { 414 Log.e(TAG, "Could not set up preview."); 415 mCamera.close(null); 416 mCamera = null; 417 // TODO: Show an error message and exit. 418 } 419 420 @Override 421 public void onReadyForCapture() { 422 Log.d(TAG, "Ready for capture."); 423 onPreviewStarted(); 424 } 425 }); 426 } 427 }); 428 } 429 430 @Override 431 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { 432 Log.d(TAG, "onSurfaceTextureSizeChanged"); 433 resetDefaultBufferSize(); 434 } 435 436 @Override 437 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { 438 Log.d(TAG, "onSurfaceTextureDestroyed"); 439 closeCamera(); 440 return true; 441 } 442 443 @Override 444 public void onSurfaceTextureUpdated(SurfaceTexture surface) { 445 if (mState == ModuleState.UPDATE_TRANSFORM_ON_NEXT_SURFACE_TEXTURE_UPDATE) { 446 Log.d(TAG, "onSurfaceTextureUpdated --> updatePreviewTransform"); 447 mState = ModuleState.IDLE; 448 CameraAppUI appUI = mAppController.getCameraAppUI(); 449 updatePreviewTransform(appUI.getSurfaceWidth(), appUI.getSurfaceHeight(), true); 450 } 451 } 452 453 @Override 454 public String getModuleStringIdentifier() { 455 return PHOTO_MODULE_STRING_ID; 456 } 457 458 @Override 459 public void resume() { 460 // Add delay on resume from lock screen only, in order to to speed up 461 // the onResume --> onPause --> onResume cycle from lock screen. 462 // Don't do always because letting go of thread can cause delay. 463 if (mIsResumeFromLockScreen) { 464 Log.v(TAG, "Delayng onResumeTasks from lock screen. " + System.currentTimeMillis()); 465 // Note: onPauseAfterSuper() will delete this runnable, so we will 466 // at most have 1 copy queued up. 467 mMainHandler.postDelayed(mResumeTaskRunnable, ON_RESUME_TASKS_DELAY_MSEC); 468 } else { 469 onResumeTasks(); 470 } 471 } 472 473 private void onResumeTasks() { 474 Log.d(TAG, "onResumeTasks + " + System.currentTimeMillis()); 475 mPaused = false; 476 mAppController.getCameraAppUI().onChangeCamera(); 477 mAppController.addPreviewAreaSizeChangedListener(this); 478 resetDefaultBufferSize(); 479 getServices().getRemoteShutterListener().onModuleReady(this); 480 mAppController.setShutterEnabled(true); 481 } 482 483 @Override 484 public void pause() { 485 mPaused = true; 486 resetTextureBufferSize(); 487 closeCamera(); 488 // Remove delayed resume trigger, if it hasn't been executed yet. 489 mMainHandler.removeCallbacksAndMessages(null); 490 } 491 492 @Override 493 public void destroy() { 494 } 495 496 @Override 497 public void onLayoutOrientationChanged(boolean isLandscape) { 498 Log.d(TAG, "onLayoutOrientationChanged"); 499 } 500 501 @Override 502 public void onOrientationChanged(int orientation) { 503 // We keep the last known orientation. So if the user first orient 504 // the camera then point the camera to floor or sky, we still have 505 // the correct orientation. 506 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) { 507 return; 508 } 509 mOrientation = CameraUtil.roundOrientation(orientation, mOrientation); 510 } 511 512 @Override 513 public void onCameraAvailable(CameraProxy cameraProxy) { 514 // Ignore since we manage the camera ourselves until we remove this. 515 } 516 517 @Override 518 public void hardResetSettings(SettingsManager settingsManager) { 519 // TODO Auto-generated method stub 520 } 521 522 @Override 523 public HardwareSpec getHardwareSpec() { 524 return new HardwareSpec() { 525 @Override 526 public boolean isFrontCameraSupported() { 527 return true; 528 } 529 530 @Override 531 public boolean isHdrSupported() { 532 return false; 533 } 534 535 @Override 536 public boolean isHdrPlusSupported() { 537 // TODO: Enable once we support this. 538 return false; 539 } 540 541 @Override 542 public boolean isFlashSupported() { 543 return true; 544 } 545 }; 546 } 547 548 @Override 549 public BottomBarUISpec getBottomBarSpec() { 550 CameraAppUI.BottomBarUISpec bottomBarSpec = new CameraAppUI.BottomBarUISpec(); 551 bottomBarSpec.enableGridLines = true; 552 bottomBarSpec.enableCamera = true; 553 bottomBarSpec.cameraCallback = mCameraSwitchCallback; 554 // TODO: Enable once we support this. 555 bottomBarSpec.enableHdr = false; 556 // TODO: Enable once we support this. 557 bottomBarSpec.hdrCallback = null; 558 // TODO: Enable once we support this. 559 bottomBarSpec.enableSelfTimer = false; 560 bottomBarSpec.showSelfTimer = false; 561 // TODO: Deal with e.g. HDR+ if it doesn't support it. 562 bottomBarSpec.enableFlash = true; 563 return bottomBarSpec; 564 } 565 566 @Override 567 public boolean isUsingBottomBar() { 568 return true; 569 } 570 571 @Override 572 public boolean onKeyDown(int keyCode, KeyEvent event) { 573 return false; 574 } 575 576 @Override 577 public boolean onKeyUp(int keyCode, KeyEvent event) { 578 return false; 579 } 580 581 @Override 582 public void onSingleTapUp(View view, int x, int y) { 583 } 584 585 @Override 586 public String getPeekAccessibilityString() { 587 return mAppController.getAndroidContext() 588 .getResources().getString(R.string.photo_accessibility_peek); 589 } 590 591 @Override 592 public void onThumbnailResult(Bitmap bitmap) { 593 // TODO 594 } 595 596 @Override 597 public void onPictureTaken(CaptureSession session) { 598 // TODO, enough memory available? ProcessingService status, etc. 599 mAppController.setShutterEnabled(true); 600 } 601 602 @Override 603 public void onPictureSaved(Uri uri) { 604 mAppController.notifyNewMedia(uri); 605 } 606 607 @Override 608 public void onTakePictureProgress(int progressPercent) { 609 // TODO once we have HDR+ hooked up. 610 } 611 612 @Override 613 public void onPictureTakenFailed() { 614 // TODO 615 } 616 617 @Override 618 public void onSettingChanged(SettingsManager settingsManager, String key) { 619 // TODO Auto-generated method stub 620 } 621 622 /** 623 * Updates the preview transform matrix to adapt to the current preview 624 * width, height, and orientation. 625 */ 626 public void updatePreviewTransform() { 627 int width; 628 int height; 629 synchronized (mDimensionLock) { 630 width = mScreenWidth; 631 height = mScreenHeight; 632 } 633 updatePreviewTransform(width, height); 634 } 635 636 /** 637 * Called when the preview started. Informs the app controller and queues a 638 * transform update when the next preview frame arrives. 639 */ 640 private void onPreviewStarted() { 641 if (mState == ModuleState.WATCH_FOR_NEXT_FRAME_AFTER_PREVIEW_STARTED) { 642 mState = ModuleState.UPDATE_TRANSFORM_ON_NEXT_SURFACE_TEXTURE_UPDATE; 643 } 644 mAppController.onPreviewStarted(); 645 } 646 647 /** 648 * Update the preview transform based on the new dimensions. Will not force 649 * an update, if it's not necessary. 650 */ 651 private void updatePreviewTransform(int incomingWidth, int incomingHeight) { 652 updatePreviewTransform(incomingWidth, incomingHeight, false); 653 } 654 655 /*** 656 * Update the preview transform based on the new dimensions. 657 */ 658 private void updatePreviewTransform(int incomingWidth, int incomingHeight, 659 boolean forceUpdate) { 660 Log.d(TAG, "updatePreviewTransform: " + incomingWidth + " x " + incomingHeight); 661 662 synchronized (mDimensionLock) { 663 int incomingRotation = CameraUtil 664 .getDisplayRotation(mContext); 665 // Check for an actual change: 666 if (mScreenHeight == incomingHeight && mScreenWidth == incomingWidth && 667 incomingRotation == mDisplayRotation && !forceUpdate) { 668 return; 669 } 670 // Update display rotation and dimensions 671 mDisplayRotation = incomingRotation; 672 mScreenWidth = incomingWidth; 673 mScreenHeight = incomingHeight; 674 updateBufferDimension(); 675 676 mPreviewTranformationMatrix = mAppController.getCameraAppUI().getPreviewTransform( 677 mPreviewTranformationMatrix); 678 int width = mScreenWidth; 679 int height = mScreenHeight; 680 681 // Assumptions: 682 // - Aspect ratio for the sensor buffers is in landscape 683 // orientation, 684 // - Dimensions of buffers received are rotated to the natural 685 // device orientation. 686 // - The contents of each buffer are rotated by the inverse of 687 // the display rotation. 688 // - Surface scales the buffer to fit the current view bounds. 689 690 // Get natural orientation and buffer dimensions 691 int naturalOrientation = CaptureModuleUtil 692 .getDeviceNaturalOrientation(mContext); 693 int effectiveWidth = mPreviewBufferWidth; 694 int effectiveHeight = mPreviewBufferHeight; 695 696 if (DEBUG) { 697 Log.v(TAG, "Rotation: " + mDisplayRotation); 698 Log.v(TAG, "Screen Width: " + mScreenWidth); 699 Log.v(TAG, "Screen Height: " + mScreenHeight); 700 Log.v(TAG, "Buffer width: " + mPreviewBufferWidth); 701 Log.v(TAG, "Buffer height: " + mPreviewBufferHeight); 702 Log.v(TAG, "Natural orientation: " + naturalOrientation); 703 } 704 705 // If natural orientation is portrait, rotate the buffer 706 // dimensions 707 if (naturalOrientation == Configuration.ORIENTATION_PORTRAIT) { 708 int temp = effectiveWidth; 709 effectiveWidth = effectiveHeight; 710 effectiveHeight = temp; 711 } 712 713 // Find and center view rect and buffer rect 714 RectF viewRect = new RectF(0, 0, width, height); 715 RectF bufRect = new RectF(0, 0, effectiveWidth, effectiveHeight); 716 float centerX = viewRect.centerX(); 717 float centerY = viewRect.centerY(); 718 bufRect.offset(centerX - bufRect.centerX(), centerY - bufRect.centerY()); 719 720 // Undo ScaleToFit.FILL done by the surface 721 mPreviewTranformationMatrix.setRectToRect(viewRect, bufRect, Matrix.ScaleToFit.FILL); 722 723 // Rotate buffer contents to proper orientation 724 mPreviewTranformationMatrix.postRotate(getPreviewOrientation(mDisplayRotation), 725 centerX, centerY); 726 727 // TODO: This is probably only working for the N5. Need to test 728 // on a device like N10 with different sensor orientation. 729 if ((mDisplayRotation % 180) == 90) { 730 int temp = effectiveWidth; 731 effectiveWidth = effectiveHeight; 732 effectiveHeight = temp; 733 } 734 735 boolean is16by9 = false; 736 737 // TODO: BACK/FRONT. 738 Size pictureSize = getPictureSizeFromSettings(); 739 if (pictureSize != null) { 740 pictureSize = ResolutionUtil.getApproximateSize(pictureSize); 741 if (pictureSize.equals(new Size(16, 9))) { 742 is16by9 = true; 743 } 744 } 745 746 float scale; 747 if (is16by9) { 748 // We are going to be clipping off edges to achieve the 16 749 // by 9 aspect ratio so we will choose the max here to fill, 750 // instead of fit. 751 scale = 752 Math.max(width / (float) effectiveWidth, height 753 / (float) effectiveHeight); 754 } else { 755 // Scale to fit view, cropping the longest dimension 756 scale = 757 Math.min(width / (float) effectiveWidth, height 758 / (float) effectiveHeight); 759 } 760 mPreviewTranformationMatrix.postScale(scale, scale, centerX, centerY); 761 762 float previewWidth = effectiveWidth * scale; 763 float previewHeight = effectiveHeight * scale; 764 // mFullPreviewWidth = previewWidth; 765 // mFullPreviewHeight = previewHeight; 766 767 float previewCenterX = previewWidth / 2; 768 float previewCenterY = previewHeight / 2; 769 mPreviewTranformationMatrix.postTranslate(previewCenterX - centerX, previewCenterY 770 - centerY); 771 772 if (is16by9) { 773 float aspectRatio = FULLSCREEN_ASPECT_RATIO; 774 RectF renderedPreviewRect = mAppController.getFullscreenRect(); 775 float desiredPreviewWidth = Math.max(renderedPreviewRect.height(), 776 renderedPreviewRect.width()) * 1 / aspectRatio; 777 int letterBoxWidth = (int) Math.ceil((Math.min(renderedPreviewRect.width(), 778 renderedPreviewRect.height()) - desiredPreviewWidth) / 2.0f); 779 mAppController.getCameraAppUI().addLetterboxing(letterBoxWidth); 780 781 float wOffset = -(previewWidth - renderedPreviewRect.width()) / 2.0f; 782 float hOffset = -(previewHeight - renderedPreviewRect.height()) / 2.0f; 783 mPreviewTranformationMatrix.postTranslate(wOffset, hOffset); 784 mAppController.updatePreviewTransformFullscreen(mPreviewTranformationMatrix, 785 aspectRatio); 786 mFinalAspectRatio = aspectRatio; 787 } else { 788 mAppController.updatePreviewTransform(mPreviewTranformationMatrix); 789 mFinalAspectRatio = null; 790 mAppController.getCameraAppUI().hideLetterboxing(); 791 } 792 // if (mGcamProxy != null) { 793 // mGcamProxy.postSetAspectRatio(mFinalAspectRatio); 794 // } 795 // mUI.updatePreviewAreaRect(new RectF(0, 0, previewWidth, 796 // previewHeight)); 797 798 // TODO: Add face detection. 799 // Characteristics info = 800 // mapp.getCameraProvider().getCharacteristics(0); 801 // mUI.setupFaceDetection(CameraUtil.getDisplayOrientation(incomingRotation, 802 // info), false); 803 // updateCamera2FaceBoundTransform(new 804 // RectF(mEffectiveCropRegion), 805 // new RectF(0, 0, mBufferWidth, mBufferHeight), 806 // new RectF(0, 0, previewWidth, previewHeight), getRotation()); 807 } 808 } 809 810 private void updateBufferDimension() { 811 if (mCamera == null) { 812 return; 813 } 814 815 Size picked = CaptureModuleUtil.pickBufferDimensions( 816 mCamera.getSupportedSizes(), 817 mCamera.getFullSizeAspectRatio(), 818 mContext); 819 mPreviewBufferWidth = picked.getWidth(); 820 mPreviewBufferHeight = picked.getHeight(); 821 } 822 823 /** 824 * Resets the default buffer size to the initially calculated size. 825 */ 826 private void resetDefaultBufferSize() { 827 synchronized (mSurfaceLock) { 828 if (mPreviewTexture != null) { 829 mPreviewTexture.setDefaultBufferSize(mPreviewBufferWidth, mPreviewBufferHeight); 830 } 831 } 832 } 833 834 private void closeCamera() { 835 if (mCamera != null) { 836 mCamera.close(null); 837 mCamera = null; 838 } 839 } 840 841 private int getOrientation() { 842 // We need to be consistent with the framework orientation (i.e. the 843 // orientation of the UI.) when the auto-rotate screen setting is on. 844 if (mAppController.isAutoRotateScreen()) { 845 return (360 - mDisplayRotation) % 360; 846 } else { 847 return mOrientation; 848 } 849 } 850 851 /** 852 * @return Whether we are resuming from within the lockscreen. 853 */ 854 private static boolean isResumeFromLockscreen(Activity activity) { 855 String action = activity.getIntent().getAction(); 856 return (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action) 857 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)); 858 } 859 860 private void switchCamera(Facing switchTo) { 861 if (mPaused || mCameraFacing == switchTo) { 862 return; 863 } 864 // TODO: Un-comment once we have timer back. 865 // cancelCountDown(); 866 867 mAppController.freezeScreenUntilPreviewReady(); 868 869 mCameraFacing = switchTo; 870 initSurface(mPreviewTexture); 871 872 // TODO: Un-comment once we have focus back. 873 // if (mFocusManager != null) { 874 // mFocusManager.removeMessages(); 875 // } 876 // mFocusManager.setMirror(mMirror); 877 } 878 879 private Size getPictureSizeFromSettings() { 880 String pictureSizeKey = mCameraFacing == Facing.FRONT ? Keys.KEY_PICTURE_SIZE_FRONT 881 : Keys.KEY_PICTURE_SIZE_BACK; 882 return mSettingsManager.getSize(SettingsManager.SCOPE_GLOBAL, pictureSizeKey); 883 } 884 885 private int getPreviewOrientation(int deviceOrientationDegrees) { 886 // Important: Camera2 buffers are already rotated to the natural 887 // orientation of the device (at least for the back-camera). 888 889 // TODO: Remove this hack for the front camera as soon as b/16637957 is 890 // fixed. 891 if (mCameraFacing == Facing.FRONT) { 892 deviceOrientationDegrees += 180; 893 } 894 return (360 - deviceOrientationDegrees) % 360; 895 } 896 897 /** 898 * Returns which way around the camera is facing, based on it's ID. 899 * <p> 900 * TODO: This needs to change so that we store the direction directly in the 901 * settings, rather than a Camera ID. 902 */ 903 private static Facing getFacingFromCameraId(int cameraId) { 904 return cameraId == 1 ? Facing.FRONT : Facing.BACK; 905 } 906 907 private void resetTextureBufferSize() { 908 // Reset the default buffer sizes on the shared SurfaceTexture 909 // so they are not scaled for gcam. 910 // 911 // According to the documentation for 912 // SurfaceTexture.setDefaultBufferSize, 913 // photo and video based image producers (presumably only Camera 1 api), 914 // override this buffer size. Any module that uses egl to render to a 915 // SurfaceTexture must have these buffer sizes reset manually. Otherwise 916 // the SurfaceTexture cannot be transformed by matrix set on the 917 // TextureView. 918 if (mPreviewTexture != null) { 919 mPreviewTexture.setDefaultBufferSize(mAppController.getCameraAppUI().getSurfaceWidth(), 920 mAppController.getCameraAppUI().getSurfaceHeight()); 921 } 922 } 923 924 /** 925 * @return The currently set Flash settings. Defaults to AUTO if the setting 926 * could not be parsed. 927 */ 928 private Flash getFlashModeFromSettings() { 929 String flashSetting = mSettingsManager.getString(mAppController.getCameraScope(), 930 Keys.KEY_FLASH_MODE); 931 try { 932 return Flash.valueOf(flashSetting.toUpperCase()); 933 } catch (IllegalArgumentException ex) { 934 Log.w(TAG, "Could not parse Flash Setting. Defaulting to AUTO."); 935 return Flash.AUTO; 936 } 937 } 938} 939