1 /* 2 * Copyright (C) 2012 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.annotation.TargetApi; 20 import android.app.Activity; 21 import android.content.ContentResolver; 22 import android.content.Intent; 23 import android.graphics.Bitmap; 24 import android.graphics.BitmapFactory; 25 import android.graphics.SurfaceTexture; 26 import android.location.Location; 27 import android.media.CameraProfile; 28 import android.net.Uri; 29 import android.os.AsyncTask; 30 import android.os.Build; 31 import android.os.Bundle; 32 import android.os.Handler; 33 import android.os.Looper; 34 import android.os.Message; 35 import android.os.MessageQueue; 36 import android.os.SystemClock; 37 import android.provider.MediaStore; 38 import android.view.KeyEvent; 39 import android.view.View; 40 41 import com.android.camera.PhotoModule.NamedImages.NamedEntity; 42 import com.android.camera.app.AppController; 43 import com.android.camera.app.CameraAppUI; 44 import com.android.camera.app.CameraProvider; 45 import com.android.camera.app.MediaSaver; 46 import com.android.camera.app.MemoryManager; 47 import com.android.camera.app.MemoryManager.MemoryListener; 48 import com.android.camera.app.MotionManager; 49 import com.android.camera.debug.Log; 50 import com.android.camera.exif.ExifInterface; 51 import com.android.camera.exif.ExifTag; 52 import com.android.camera.exif.Rational; 53 import com.android.camera.hardware.HardwareSpec; 54 import com.android.camera.hardware.HardwareSpecImpl; 55 import com.android.camera.hardware.HeadingSensor; 56 import com.android.camera.module.ModuleController; 57 import com.android.camera.one.OneCamera; 58 import com.android.camera.one.OneCameraAccessException; 59 import com.android.camera.one.OneCameraException; 60 import com.android.camera.one.OneCameraManager; 61 import com.android.camera.one.OneCameraModule; 62 import com.android.camera.remote.RemoteCameraModule; 63 import com.android.camera.settings.CameraPictureSizesCacher; 64 import com.android.camera.settings.Keys; 65 import com.android.camera.settings.ResolutionUtil; 66 import com.android.camera.settings.SettingsManager; 67 import com.android.camera.stats.SessionStatsCollector; 68 import com.android.camera.stats.UsageStatistics; 69 import com.android.camera.ui.CountDownView; 70 import com.android.camera.ui.TouchCoordinate; 71 import com.android.camera.util.AndroidServices; 72 import com.android.camera.util.ApiHelper; 73 import com.android.camera.util.CameraUtil; 74 import com.android.camera.util.GcamHelper; 75 import com.android.camera.util.GservicesHelper; 76 import com.android.camera.util.Size; 77 import com.android.camera2.R; 78 import com.android.ex.camera2.portability.CameraAgent; 79 import com.android.ex.camera2.portability.CameraAgent.CameraAFCallback; 80 import com.android.ex.camera2.portability.CameraAgent.CameraAFMoveCallback; 81 import com.android.ex.camera2.portability.CameraAgent.CameraPictureCallback; 82 import com.android.ex.camera2.portability.CameraAgent.CameraProxy; 83 import com.android.ex.camera2.portability.CameraAgent.CameraShutterCallback; 84 import com.android.ex.camera2.portability.CameraCapabilities; 85 import com.android.ex.camera2.portability.CameraDeviceInfo.Characteristics; 86 import com.android.ex.camera2.portability.CameraSettings; 87 import com.google.common.logging.eventprotos; 88 89 import java.io.ByteArrayOutputStream; 90 import java.io.File; 91 import java.io.FileNotFoundException; 92 import java.io.FileOutputStream; 93 import java.io.IOException; 94 import java.io.OutputStream; 95 import java.lang.ref.WeakReference; 96 import java.util.ArrayList; 97 import java.util.List; 98 import java.util.Vector; 99 100 101 public class PhotoModule 102 extends CameraModule 103 implements PhotoController, 104 ModuleController, 105 MemoryListener, 106 FocusOverlayManager.Listener, 107 SettingsManager.OnSettingChangedListener, 108 RemoteCameraModule, 109 CountDownView.OnCountDownStatusListener { 110 111 private static final Log.Tag TAG = new Log.Tag("PhotoModule"); 112 113 // We number the request code from 1000 to avoid collision with Gallery. 114 private static final int REQUEST_CROP = 1000; 115 116 // Messages defined for the UI thread handler. 117 private static final int MSG_FIRST_TIME_INIT = 1; 118 private static final int MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE = 2; 119 120 // The subset of parameters we need to update in setCameraParameters(). 121 private static final int UPDATE_PARAM_INITIALIZE = 1; 122 private static final int UPDATE_PARAM_ZOOM = 2; 123 private static final int UPDATE_PARAM_PREFERENCE = 4; 124 private static final int UPDATE_PARAM_ALL = -1; 125 126 private static final String DEBUG_IMAGE_PREFIX = "DEBUG_"; 127 128 private CameraActivity mActivity; 129 private CameraProxy mCameraDevice; 130 private int mCameraId; 131 private CameraCapabilities mCameraCapabilities; 132 private CameraSettings mCameraSettings; 133 private HardwareSpec mHardwareSpec; 134 private boolean mPaused; 135 136 private PhotoUI mUI; 137 138 // The activity is going to switch to the specified camera id. This is 139 // needed because texture copy is done in GL thread. -1 means camera is not 140 // switching. 141 protected int mPendingSwitchCameraId = -1; 142 143 // When setCameraParametersWhenIdle() is called, we accumulate the subsets 144 // needed to be updated in mUpdateSet. 145 private int mUpdateSet; 146 147 private float mZoomValue; // The current zoom ratio. 148 private int mTimerDuration; 149 /** Set when a volume button is clicked to take photo */ 150 private boolean mVolumeButtonClickedFlag = false; 151 152 private boolean mFocusAreaSupported; 153 private boolean mMeteringAreaSupported; 154 private boolean mAeLockSupported; 155 private boolean mAwbLockSupported; 156 private boolean mContinuousFocusSupported; 157 158 private static final String sTempCropFilename = "crop-temp"; 159 160 private boolean mFaceDetectionStarted = false; 161 162 // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true. 163 private String mCropValue; 164 private Uri mSaveUri; 165 166 private Uri mDebugUri; 167 168 // We use a queue to generated names of the images to be used later 169 // when the image is ready to be saved. 170 private NamedImages mNamedImages; 171 172 private final Runnable mDoSnapRunnable = new Runnable() { 173 @Override 174 public void run() { 175 onShutterButtonClick(); 176 } 177 }; 178 179 /** 180 * An unpublished intent flag requesting to return as soon as capturing is 181 * completed. TODO: consider publishing by moving into MediaStore. 182 */ 183 private static final String EXTRA_QUICK_CAPTURE = 184 "android.intent.extra.quickCapture"; 185 186 // The display rotation in degrees. This is only valid when mCameraState is 187 // not PREVIEW_STOPPED. 188 private int mDisplayRotation; 189 // The value for UI components like indicators. 190 private int mDisplayOrientation; 191 // The value for cameradevice.CameraSettings.setPhotoRotationDegrees. 192 private int mJpegRotation; 193 // Indicates whether we are using front camera 194 private boolean mMirror; 195 private boolean mFirstTimeInitialized; 196 private boolean mIsImageCaptureIntent; 197 198 private int mCameraState = PREVIEW_STOPPED; 199 private boolean mSnapshotOnIdle = false; 200 201 private ContentResolver mContentResolver; 202 203 private AppController mAppController; 204 private OneCameraManager mOneCameraManager; 205 206 private final PostViewPictureCallback mPostViewPictureCallback = 207 new PostViewPictureCallback(); 208 private final RawPictureCallback mRawPictureCallback = 209 new RawPictureCallback(); 210 private final AutoFocusCallback mAutoFocusCallback = 211 new AutoFocusCallback(); 212 private final Object mAutoFocusMoveCallback = 213 ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK 214 ? new AutoFocusMoveCallback() 215 : null; 216 217 private long mFocusStartTime; 218 private long mShutterCallbackTime; 219 private long mPostViewPictureCallbackTime; 220 private long mRawPictureCallbackTime; 221 private long mJpegPictureCallbackTime; 222 private long mOnResumeTime; 223 private byte[] mJpegImageData; 224 /** Touch coordinate for shutter button press. */ 225 private TouchCoordinate mShutterTouchCoordinate; 226 227 228 // These latency time are for the CameraLatency test. 229 public long mAutoFocusTime; 230 public long mShutterLag; 231 public long mShutterToPictureDisplayedTime; 232 public long mPictureDisplayedToJpegCallbackTime; 233 public long mJpegCallbackFinishTime; 234 public long mCaptureStartTime; 235 236 // This handles everything about focus. 237 private FocusOverlayManager mFocusManager; 238 239 private final int mGcamModeIndex; 240 private SoundPlayer mCountdownSoundPlayer; 241 242 private CameraCapabilities.SceneMode mSceneMode; 243 244 private final Handler mHandler = new MainHandler(this); 245 246 private boolean mQuickCapture; 247 248 /** Used to detect motion. We use this to release focus lock early. */ 249 private MotionManager mMotionManager; 250 251 private HeadingSensor mHeadingSensor; 252 253 /** True if all the parameters needed to start preview is ready. */ 254 private boolean mCameraPreviewParamsReady = false; 255 256 private final MediaSaver.OnMediaSavedListener mOnMediaSavedListener = 257 new MediaSaver.OnMediaSavedListener() { 258 259 @Override 260 public void onMediaSaved(Uri uri) { 261 if (uri != null) { 262 mActivity.notifyNewMedia(uri); 263 } else { 264 onError(); 265 } 266 } 267 }; 268 269 /** 270 * Displays error dialog and allows use to enter feedback. Does not shut 271 * down the app. 272 */ onError()273 private void onError() { 274 mAppController.getFatalErrorHandler().onMediaStorageFailure(); 275 } 276 277 private boolean mShouldResizeTo16x9 = false; 278 279 /** 280 * We keep the flash setting before entering scene modes (HDR) 281 * and restore it after HDR is off. 282 */ 283 private String mFlashModeBeforeSceneMode; 284 checkDisplayRotation()285 private void checkDisplayRotation() { 286 // Need to just be a no-op for the quick resume-pause scenario. 287 if (mPaused) { 288 return; 289 } 290 // Set the display orientation if display rotation has changed. 291 // Sometimes this happens when the device is held upside 292 // down and camera app is opened. Rotation animation will 293 // take some time and the rotation value we have got may be 294 // wrong. Framework does not have a callback for this now. 295 if (CameraUtil.getDisplayRotation() != mDisplayRotation) { 296 setDisplayOrientation(); 297 } 298 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) { 299 mHandler.postDelayed(new Runnable() { 300 @Override 301 public void run() { 302 checkDisplayRotation(); 303 } 304 }, 100); 305 } 306 } 307 308 /** 309 * This Handler is used to post message back onto the main thread of the 310 * application 311 */ 312 private static class MainHandler extends Handler { 313 private final WeakReference<PhotoModule> mModule; 314 MainHandler(PhotoModule module)315 public MainHandler(PhotoModule module) { 316 super(Looper.getMainLooper()); 317 mModule = new WeakReference<PhotoModule>(module); 318 } 319 320 @Override handleMessage(Message msg)321 public void handleMessage(Message msg) { 322 PhotoModule module = mModule.get(); 323 if (module == null) { 324 return; 325 } 326 switch (msg.what) { 327 case MSG_FIRST_TIME_INIT: { 328 module.initializeFirstTime(); 329 break; 330 } 331 332 case MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE: { 333 module.setCameraParametersWhenIdle(0); 334 break; 335 } 336 } 337 } 338 } 339 switchToGcamCapture()340 private void switchToGcamCapture() { 341 if (mActivity != null && mGcamModeIndex != 0) { 342 SettingsManager settingsManager = mActivity.getSettingsManager(); 343 settingsManager.set(SettingsManager.SCOPE_GLOBAL, 344 Keys.KEY_CAMERA_HDR_PLUS, true); 345 346 // Disable the HDR+ button to prevent callbacks from being 347 // queued before the correct callback is attached to the button 348 // in the new module. The new module will set the enabled/disabled 349 // of this button when the module's preferred camera becomes available. 350 ButtonManager buttonManager = mActivity.getButtonManager(); 351 352 buttonManager.disableButtonClick(ButtonManager.BUTTON_HDR_PLUS); 353 354 mAppController.getCameraAppUI().freezeScreenUntilPreviewReady(); 355 356 // Do not post this to avoid this module switch getting interleaved with 357 // other button callbacks. 358 mActivity.onModeSelected(mGcamModeIndex); 359 360 buttonManager.enableButtonClick(ButtonManager.BUTTON_HDR_PLUS); 361 } 362 } 363 364 /** 365 * Constructs a new photo module. 366 */ PhotoModule(AppController app)367 public PhotoModule(AppController app) { 368 super(app); 369 mGcamModeIndex = app.getAndroidContext().getResources() 370 .getInteger(R.integer.camera_mode_gcam); 371 } 372 373 @Override getPeekAccessibilityString()374 public String getPeekAccessibilityString() { 375 return mAppController.getAndroidContext() 376 .getResources().getString(R.string.photo_accessibility_peek); 377 } 378 379 @Override init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent)380 public void init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent) { 381 mActivity = activity; 382 // TODO: Need to look at the controller interface to see if we can get 383 // rid of passing in the activity directly. 384 mAppController = mActivity; 385 386 mUI = new PhotoUI(mActivity, this, mActivity.getModuleLayoutRoot()); 387 mActivity.setPreviewStatusListener(mUI); 388 389 SettingsManager settingsManager = mActivity.getSettingsManager(); 390 // TODO: Move this to SettingsManager as a part of upgrade procedure. 391 // Aspect Ratio selection dialog is only shown for Nexus 4, 5 and 6. 392 if (mAppController.getCameraAppUI().shouldShowAspectRatioDialog()) { 393 // Switch to back camera to set aspect ratio. 394 settingsManager.setToDefault(mAppController.getModuleScope(), Keys.KEY_CAMERA_ID); 395 } 396 mCameraId = settingsManager.getInteger(mAppController.getModuleScope(), 397 Keys.KEY_CAMERA_ID); 398 399 mContentResolver = mActivity.getContentResolver(); 400 401 // Surface texture is from camera screen nail and startPreview needs it. 402 // This must be done before startPreview. 403 mIsImageCaptureIntent = isImageCaptureIntent(); 404 mUI.setCountdownFinishedListener(this); 405 406 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false); 407 mHeadingSensor = new HeadingSensor(AndroidServices.instance().provideSensorManager()); 408 mCountdownSoundPlayer = new SoundPlayer(mAppController.getAndroidContext()); 409 410 try { 411 mOneCameraManager = OneCameraModule.provideOneCameraManager(); 412 } catch (OneCameraException e) { 413 Log.e(TAG, "Hardware manager failed to open."); 414 } 415 416 // TODO: Make this a part of app controller API. 417 View cancelButton = mActivity.findViewById(R.id.shutter_cancel_button); 418 cancelButton.setOnClickListener(new View.OnClickListener() { 419 @Override 420 public void onClick(View view) { 421 cancelCountDown(); 422 } 423 }); 424 } 425 cancelCountDown()426 private void cancelCountDown() { 427 if (mUI.isCountingDown()) { 428 // Cancel on-going countdown. 429 mUI.cancelCountDown(); 430 } 431 mAppController.getCameraAppUI().transitionToCapture(); 432 mAppController.getCameraAppUI().showModeOptions(); 433 mAppController.setShutterEnabled(true); 434 } 435 436 @Override isUsingBottomBar()437 public boolean isUsingBottomBar() { 438 return true; 439 } 440 initializeControlByIntent()441 private void initializeControlByIntent() { 442 if (mIsImageCaptureIntent) { 443 mActivity.getCameraAppUI().transitionToIntentCaptureLayout(); 444 setupCaptureParams(); 445 } 446 } 447 onPreviewStarted()448 private void onPreviewStarted() { 449 mAppController.onPreviewStarted(); 450 mAppController.setShutterEnabled(true); 451 setCameraState(IDLE); 452 startFaceDetection(); 453 } 454 455 @Override onPreviewUIReady()456 public void onPreviewUIReady() { 457 Log.i(TAG, "onPreviewUIReady"); 458 startPreview(); 459 } 460 461 @Override onPreviewUIDestroyed()462 public void onPreviewUIDestroyed() { 463 if (mCameraDevice == null) { 464 return; 465 } 466 mCameraDevice.setPreviewTexture(null); 467 stopPreview(); 468 } 469 470 @Override startPreCaptureAnimation()471 public void startPreCaptureAnimation() { 472 mAppController.startFlashAnimation(false); 473 } 474 onCameraOpened()475 private void onCameraOpened() { 476 openCameraCommon(); 477 initializeControlByIntent(); 478 } 479 switchCamera()480 private void switchCamera() { 481 if (mPaused) { 482 return; 483 } 484 cancelCountDown(); 485 486 mAppController.freezeScreenUntilPreviewReady(); 487 SettingsManager settingsManager = mActivity.getSettingsManager(); 488 489 Log.i(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId); 490 closeCamera(); 491 mCameraId = mPendingSwitchCameraId; 492 493 settingsManager.set(mAppController.getModuleScope(), Keys.KEY_CAMERA_ID, mCameraId); 494 requestCameraOpen(); 495 mUI.clearFaces(); 496 if (mFocusManager != null) { 497 mFocusManager.removeMessages(); 498 } 499 500 mMirror = isCameraFrontFacing(); 501 mFocusManager.setMirror(mMirror); 502 // Start switch camera animation. Post a message because 503 // onFrameAvailable from the old camera may already exist. 504 } 505 506 /** 507 * Uses the {@link CameraProvider} to open the currently-selected camera 508 * device, using {@link GservicesHelper} to choose between API-1 and API-2. 509 */ requestCameraOpen()510 private void requestCameraOpen() { 511 Log.v(TAG, "requestCameraOpen"); 512 mActivity.getCameraProvider().requestCamera(mCameraId, 513 GservicesHelper.useCamera2ApiThroughPortabilityLayer(mActivity 514 .getContentResolver())); 515 } 516 517 private final ButtonManager.ButtonCallback mCameraCallback = 518 new ButtonManager.ButtonCallback() { 519 @Override 520 public void onStateChanged(int state) { 521 // At the time this callback is fired, the camera id 522 // has be set to the desired camera. 523 524 if (mPaused || mAppController.getCameraProvider().waitingForCamera()) { 525 return; 526 } 527 // If switching to back camera, and HDR+ is still on, 528 // switch back to gcam, otherwise handle callback normally. 529 SettingsManager settingsManager = mActivity.getSettingsManager(); 530 if (Keys.isCameraBackFacing(settingsManager, 531 mAppController.getModuleScope())) { 532 if (Keys.requestsReturnToHdrPlus(settingsManager, 533 mAppController.getModuleScope())) { 534 switchToGcamCapture(); 535 return; 536 } 537 } 538 539 ButtonManager buttonManager = mActivity.getButtonManager(); 540 buttonManager.disableCameraButtonAndBlock(); 541 542 mPendingSwitchCameraId = state; 543 544 Log.d(TAG, "Start to switch camera. cameraId=" + state); 545 // We need to keep a preview frame for the animation before 546 // releasing the camera. This will trigger 547 // onPreviewTextureCopied. 548 // TODO: Need to animate the camera switch 549 switchCamera(); 550 } 551 }; 552 553 private final ButtonManager.ButtonCallback mHdrPlusCallback = 554 new ButtonManager.ButtonCallback() { 555 @Override 556 public void onStateChanged(int state) { 557 SettingsManager settingsManager = mActivity.getSettingsManager(); 558 if (GcamHelper.hasGcamAsSeparateModule( 559 mAppController.getCameraFeatureConfig())) { 560 // Set the camera setting to default backfacing. 561 settingsManager.setToDefault(mAppController.getModuleScope(), 562 Keys.KEY_CAMERA_ID); 563 switchToGcamCapture(); 564 } else { 565 if (Keys.isHdrOn(settingsManager)) { 566 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_SCENE_MODE, 567 mCameraCapabilities.getStringifier().stringify( 568 CameraCapabilities.SceneMode.HDR)); 569 } else { 570 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_SCENE_MODE, 571 mCameraCapabilities.getStringifier().stringify( 572 CameraCapabilities.SceneMode.AUTO)); 573 } 574 updateParametersSceneMode(); 575 if (mCameraDevice != null) { 576 mCameraDevice.applySettings(mCameraSettings); 577 } 578 updateSceneMode(); 579 } 580 } 581 }; 582 583 private final View.OnClickListener mCancelCallback = new View.OnClickListener() { 584 @Override 585 public void onClick(View v) { 586 onCaptureCancelled(); 587 } 588 }; 589 590 private final View.OnClickListener mDoneCallback = new View.OnClickListener() { 591 @Override 592 public void onClick(View v) { 593 onCaptureDone(); 594 } 595 }; 596 597 private final View.OnClickListener mRetakeCallback = new View.OnClickListener() { 598 @Override 599 public void onClick(View v) { 600 mActivity.getCameraAppUI().transitionToIntentCaptureLayout(); 601 onCaptureRetake(); 602 } 603 }; 604 605 @Override hardResetSettings(SettingsManager settingsManager)606 public void hardResetSettings(SettingsManager settingsManager) { 607 // PhotoModule should hard reset HDR+ to off, 608 // and HDR to off if HDR+ is supported. 609 settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR_PLUS, false); 610 if (GcamHelper.hasGcamAsSeparateModule(mAppController.getCameraFeatureConfig())) { 611 settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR, false); 612 } 613 } 614 615 @Override getHardwareSpec()616 public HardwareSpec getHardwareSpec() { 617 if (mHardwareSpec == null) { 618 mHardwareSpec = (mCameraSettings != null ? 619 new HardwareSpecImpl(getCameraProvider(), mCameraCapabilities, 620 mAppController.getCameraFeatureConfig(), isCameraFrontFacing()) : null); 621 } 622 return mHardwareSpec; 623 } 624 625 @Override getBottomBarSpec()626 public CameraAppUI.BottomBarUISpec getBottomBarSpec() { 627 CameraAppUI.BottomBarUISpec bottomBarSpec = new CameraAppUI.BottomBarUISpec(); 628 629 bottomBarSpec.enableCamera = true; 630 bottomBarSpec.cameraCallback = mCameraCallback; 631 bottomBarSpec.enableFlash = !mAppController.getSettingsManager() 632 .getBoolean(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR); 633 bottomBarSpec.enableHdr = true; 634 bottomBarSpec.hdrCallback = mHdrPlusCallback; 635 bottomBarSpec.enableGridLines = true; 636 if (mCameraCapabilities != null) { 637 bottomBarSpec.enableExposureCompensation = true; 638 bottomBarSpec.exposureCompensationSetCallback = 639 new CameraAppUI.BottomBarUISpec.ExposureCompensationSetCallback() { 640 @Override 641 public void setExposure(int value) { 642 setExposureCompensation(value); 643 } 644 }; 645 bottomBarSpec.minExposureCompensation = 646 mCameraCapabilities.getMinExposureCompensation(); 647 bottomBarSpec.maxExposureCompensation = 648 mCameraCapabilities.getMaxExposureCompensation(); 649 bottomBarSpec.exposureCompensationStep = 650 mCameraCapabilities.getExposureCompensationStep(); 651 } 652 653 bottomBarSpec.enableSelfTimer = true; 654 bottomBarSpec.showSelfTimer = true; 655 656 if (isImageCaptureIntent()) { 657 bottomBarSpec.showCancel = true; 658 bottomBarSpec.cancelCallback = mCancelCallback; 659 bottomBarSpec.showDone = true; 660 bottomBarSpec.doneCallback = mDoneCallback; 661 bottomBarSpec.showRetake = true; 662 bottomBarSpec.retakeCallback = mRetakeCallback; 663 } 664 665 return bottomBarSpec; 666 } 667 668 // either open a new camera or switch cameras openCameraCommon()669 private void openCameraCommon() { 670 mUI.onCameraOpened(mCameraCapabilities, mCameraSettings); 671 if (mIsImageCaptureIntent) { 672 // Set hdr plus to default: off. 673 SettingsManager settingsManager = mActivity.getSettingsManager(); 674 settingsManager.setToDefault(SettingsManager.SCOPE_GLOBAL, 675 Keys.KEY_CAMERA_HDR_PLUS); 676 } 677 updateSceneMode(); 678 } 679 680 @Override updatePreviewAspectRatio(float aspectRatio)681 public void updatePreviewAspectRatio(float aspectRatio) { 682 mAppController.updatePreviewAspectRatio(aspectRatio); 683 } 684 resetExposureCompensation()685 private void resetExposureCompensation() { 686 SettingsManager settingsManager = mActivity.getSettingsManager(); 687 if (settingsManager == null) { 688 Log.e(TAG, "Settings manager is null!"); 689 return; 690 } 691 settingsManager.setToDefault(mAppController.getCameraScope(), 692 Keys.KEY_EXPOSURE); 693 } 694 695 // Snapshots can only be taken after this is called. It should be called 696 // once only. We could have done these things in onCreate() but we want to 697 // make preview screen appear as soon as possible. initializeFirstTime()698 private void initializeFirstTime() { 699 if (mFirstTimeInitialized || mPaused) { 700 return; 701 } 702 703 mUI.initializeFirstTime(); 704 705 // We set the listener only when both service and shutterbutton 706 // are initialized. 707 getServices().getMemoryManager().addListener(this); 708 709 mNamedImages = new NamedImages(); 710 711 mFirstTimeInitialized = true; 712 addIdleHandler(); 713 714 mActivity.updateStorageSpaceAndHint(null); 715 } 716 717 // If the activity is paused and resumed, this method will be called in 718 // onResume. initializeSecondTime()719 private void initializeSecondTime() { 720 getServices().getMemoryManager().addListener(this); 721 mNamedImages = new NamedImages(); 722 mUI.initializeSecondTime(mCameraCapabilities, mCameraSettings); 723 } 724 addIdleHandler()725 private void addIdleHandler() { 726 MessageQueue queue = Looper.myQueue(); 727 queue.addIdleHandler(new MessageQueue.IdleHandler() { 728 @Override 729 public boolean queueIdle() { 730 Storage.instance().ensureOSXCompatible(); 731 return false; 732 } 733 }); 734 } 735 736 @Override startFaceDetection()737 public void startFaceDetection() { 738 if (mFaceDetectionStarted || mCameraDevice == null) { 739 return; 740 } 741 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) { 742 mFaceDetectionStarted = true; 743 mUI.onStartFaceDetection(mDisplayOrientation, isCameraFrontFacing()); 744 mCameraDevice.setFaceDetectionCallback(mHandler, mUI); 745 mCameraDevice.startFaceDetection(); 746 SessionStatsCollector.instance().faceScanActive(true); 747 } 748 } 749 750 @Override stopFaceDetection()751 public void stopFaceDetection() { 752 if (!mFaceDetectionStarted || mCameraDevice == null) { 753 return; 754 } 755 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) { 756 mFaceDetectionStarted = false; 757 mCameraDevice.setFaceDetectionCallback(null, null); 758 mCameraDevice.stopFaceDetection(); 759 mUI.clearFaces(); 760 SessionStatsCollector.instance().faceScanActive(false); 761 } 762 } 763 764 private final class ShutterCallback 765 implements CameraShutterCallback { 766 767 private final boolean mNeedsAnimation; 768 ShutterCallback(boolean needsAnimation)769 public ShutterCallback(boolean needsAnimation) { 770 mNeedsAnimation = needsAnimation; 771 } 772 773 @Override onShutter(CameraProxy camera)774 public void onShutter(CameraProxy camera) { 775 mShutterCallbackTime = System.currentTimeMillis(); 776 mShutterLag = mShutterCallbackTime - mCaptureStartTime; 777 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms"); 778 if (mNeedsAnimation) { 779 mActivity.runOnUiThread(new Runnable() { 780 @Override 781 public void run() { 782 animateAfterShutter(); 783 } 784 }); 785 } 786 } 787 } 788 789 private final class PostViewPictureCallback 790 implements CameraPictureCallback { 791 @Override onPictureTaken(byte[] data, CameraProxy camera)792 public void onPictureTaken(byte[] data, CameraProxy camera) { 793 mPostViewPictureCallbackTime = System.currentTimeMillis(); 794 Log.v(TAG, "mShutterToPostViewCallbackTime = " 795 + (mPostViewPictureCallbackTime - mShutterCallbackTime) 796 + "ms"); 797 } 798 } 799 800 private final class RawPictureCallback 801 implements CameraPictureCallback { 802 @Override onPictureTaken(byte[] rawData, CameraProxy camera)803 public void onPictureTaken(byte[] rawData, CameraProxy camera) { 804 mRawPictureCallbackTime = System.currentTimeMillis(); 805 Log.v(TAG, "mShutterToRawCallbackTime = " 806 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms"); 807 } 808 } 809 810 private static class ResizeBundle { 811 byte[] jpegData; 812 float targetAspectRatio; 813 ExifInterface exif; 814 } 815 816 /** 817 * @return Cropped image if the target aspect ratio is larger than the jpeg 818 * aspect ratio on the long axis. The original jpeg otherwise. 819 */ cropJpegDataToAspectRatio(ResizeBundle dataBundle)820 private ResizeBundle cropJpegDataToAspectRatio(ResizeBundle dataBundle) { 821 822 final byte[] jpegData = dataBundle.jpegData; 823 final ExifInterface exif = dataBundle.exif; 824 float targetAspectRatio = dataBundle.targetAspectRatio; 825 826 Bitmap original = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length); 827 int originalWidth = original.getWidth(); 828 int originalHeight = original.getHeight(); 829 int newWidth; 830 int newHeight; 831 832 if (originalWidth > originalHeight) { 833 newHeight = (int) (originalWidth / targetAspectRatio); 834 newWidth = originalWidth; 835 } else { 836 newWidth = (int) (originalHeight / targetAspectRatio); 837 newHeight = originalHeight; 838 } 839 int xOffset = (originalWidth - newWidth)/2; 840 int yOffset = (originalHeight - newHeight)/2; 841 842 if (xOffset < 0 || yOffset < 0) { 843 return dataBundle; 844 } 845 846 Bitmap resized = Bitmap.createBitmap(original,xOffset,yOffset,newWidth, newHeight); 847 exif.setTagValue(ExifInterface.TAG_PIXEL_X_DIMENSION, new Integer(newWidth)); 848 exif.setTagValue(ExifInterface.TAG_PIXEL_Y_DIMENSION, new Integer(newHeight)); 849 850 ByteArrayOutputStream stream = new ByteArrayOutputStream(); 851 852 resized.compress(Bitmap.CompressFormat.JPEG, 90, stream); 853 dataBundle.jpegData = stream.toByteArray(); 854 return dataBundle; 855 } 856 857 private final class JpegPictureCallback 858 implements CameraPictureCallback { 859 Location mLocation; 860 JpegPictureCallback(Location loc)861 public JpegPictureCallback(Location loc) { 862 mLocation = loc; 863 } 864 865 @Override onPictureTaken(final byte[] originalJpegData, final CameraProxy camera)866 public void onPictureTaken(final byte[] originalJpegData, final CameraProxy camera) { 867 Log.i(TAG, "onPictureTaken"); 868 mAppController.setShutterEnabled(true); 869 if (mPaused) { 870 return; 871 } 872 if (mIsImageCaptureIntent) { 873 stopPreview(); 874 } 875 if (mSceneMode == CameraCapabilities.SceneMode.HDR) { 876 mUI.setSwipingEnabled(true); 877 } 878 879 mJpegPictureCallbackTime = System.currentTimeMillis(); 880 // If postview callback has arrived, the captured image is displayed 881 // in postview callback. If not, the captured image is displayed in 882 // raw picture callback. 883 if (mPostViewPictureCallbackTime != 0) { 884 mShutterToPictureDisplayedTime = 885 mPostViewPictureCallbackTime - mShutterCallbackTime; 886 mPictureDisplayedToJpegCallbackTime = 887 mJpegPictureCallbackTime - mPostViewPictureCallbackTime; 888 } else { 889 mShutterToPictureDisplayedTime = 890 mRawPictureCallbackTime - mShutterCallbackTime; 891 mPictureDisplayedToJpegCallbackTime = 892 mJpegPictureCallbackTime - mRawPictureCallbackTime; 893 } 894 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = " 895 + mPictureDisplayedToJpegCallbackTime + "ms"); 896 897 if (!mIsImageCaptureIntent) { 898 setupPreview(); 899 } 900 901 long now = System.currentTimeMillis(); 902 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime; 903 Log.v(TAG, "mJpegCallbackFinishTime = " + mJpegCallbackFinishTime + "ms"); 904 mJpegPictureCallbackTime = 0; 905 906 final ExifInterface exif = Exif.getExif(originalJpegData); 907 final NamedEntity name = mNamedImages.getNextNameEntity(); 908 if (mShouldResizeTo16x9) { 909 final ResizeBundle dataBundle = new ResizeBundle(); 910 dataBundle.jpegData = originalJpegData; 911 dataBundle.targetAspectRatio = ResolutionUtil.NEXUS_5_LARGE_16_BY_9_ASPECT_RATIO; 912 dataBundle.exif = exif; 913 new AsyncTask<ResizeBundle, Void, ResizeBundle>() { 914 915 @Override 916 protected ResizeBundle doInBackground(ResizeBundle... resizeBundles) { 917 return cropJpegDataToAspectRatio(resizeBundles[0]); 918 } 919 920 @Override 921 protected void onPostExecute(ResizeBundle result) { 922 saveFinalPhoto(result.jpegData, name, result.exif, camera); 923 } 924 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, dataBundle); 925 926 } else { 927 saveFinalPhoto(originalJpegData, name, exif, camera); 928 } 929 } 930 saveFinalPhoto(final byte[] jpegData, NamedEntity name, final ExifInterface exif, CameraProxy camera)931 void saveFinalPhoto(final byte[] jpegData, NamedEntity name, final ExifInterface exif, 932 CameraProxy camera) { 933 int orientation = Exif.getOrientation(exif); 934 935 float zoomValue = 1.0f; 936 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) { 937 zoomValue = mCameraSettings.getCurrentZoomRatio(); 938 } 939 boolean hdrOn = CameraCapabilities.SceneMode.HDR == mSceneMode; 940 String flashSetting = 941 mActivity.getSettingsManager().getString(mAppController.getCameraScope(), 942 Keys.KEY_FLASH_MODE); 943 boolean gridLinesOn = Keys.areGridLinesOn(mActivity.getSettingsManager()); 944 UsageStatistics.instance().photoCaptureDoneEvent( 945 eventprotos.NavigationChange.Mode.PHOTO_CAPTURE, 946 name.title + ".jpg", exif, 947 isCameraFrontFacing(), hdrOn, zoomValue, flashSetting, gridLinesOn, 948 (float) mTimerDuration, null, mShutterTouchCoordinate, mVolumeButtonClickedFlag, 949 null, null, null); 950 mShutterTouchCoordinate = null; 951 mVolumeButtonClickedFlag = false; 952 953 if (!mIsImageCaptureIntent) { 954 // Calculate the width and the height of the jpeg. 955 Integer exifWidth = exif.getTagIntValue(ExifInterface.TAG_PIXEL_X_DIMENSION); 956 Integer exifHeight = exif.getTagIntValue(ExifInterface.TAG_PIXEL_Y_DIMENSION); 957 int width, height; 958 if (mShouldResizeTo16x9 && exifWidth != null && exifHeight != null) { 959 width = exifWidth; 960 height = exifHeight; 961 } else { 962 Size s = new Size(mCameraSettings.getCurrentPhotoSize()); 963 if ((mJpegRotation + orientation) % 180 == 0) { 964 width = s.width(); 965 height = s.height(); 966 } else { 967 width = s.height(); 968 height = s.width(); 969 } 970 } 971 String title = (name == null) ? null : name.title; 972 long date = (name == null) ? -1 : name.date; 973 974 // Handle debug mode outputs 975 if (mDebugUri != null) { 976 // If using a debug uri, save jpeg there. 977 saveToDebugUri(jpegData); 978 979 // Adjust the title of the debug image shown in mediastore. 980 if (title != null) { 981 title = DEBUG_IMAGE_PREFIX + title; 982 } 983 } 984 985 if (title == null) { 986 Log.e(TAG, "Unbalanced name/data pair"); 987 } else { 988 if (date == -1) { 989 date = mCaptureStartTime; 990 } 991 int heading = mHeadingSensor.getCurrentHeading(); 992 if (heading != HeadingSensor.INVALID_HEADING) { 993 // heading direction has been updated by the sensor. 994 ExifTag directionRefTag = exif.buildTag( 995 ExifInterface.TAG_GPS_IMG_DIRECTION_REF, 996 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION); 997 ExifTag directionTag = exif.buildTag( 998 ExifInterface.TAG_GPS_IMG_DIRECTION, 999 new Rational(heading, 1)); 1000 exif.setTag(directionRefTag); 1001 exif.setTag(directionTag); 1002 } 1003 getServices().getMediaSaver().addImage( 1004 jpegData, title, date, mLocation, width, height, 1005 orientation, exif, mOnMediaSavedListener); 1006 } 1007 // Animate capture with real jpeg data instead of a preview 1008 // frame. 1009 mUI.animateCapture(jpegData, orientation, mMirror); 1010 } else { 1011 mJpegImageData = jpegData; 1012 if (!mQuickCapture) { 1013 Log.v(TAG, "showing UI"); 1014 mUI.showCapturedImageForReview(jpegData, orientation, mMirror); 1015 } else { 1016 onCaptureDone(); 1017 } 1018 } 1019 1020 // Send the taken photo to remote shutter listeners, if any are 1021 // registered. 1022 getServices().getRemoteShutterListener().onPictureTaken(jpegData); 1023 1024 // Check this in advance of each shot so we don't add to shutter 1025 // latency. It's true that someone else could write to the SD card 1026 // in the mean time and fill it, but that could have happened 1027 // between the shutter press and saving the JPEG too. 1028 mActivity.updateStorageSpaceAndHint(null); 1029 } 1030 } 1031 1032 private final class AutoFocusCallback implements CameraAFCallback { 1033 @Override onAutoFocus(boolean focused, CameraProxy camera)1034 public void onAutoFocus(boolean focused, CameraProxy camera) { 1035 SessionStatsCollector.instance().autofocusResult(focused); 1036 if (mPaused) { 1037 return; 1038 } 1039 1040 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime; 1041 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms focused = "+focused); 1042 setCameraState(IDLE); 1043 mFocusManager.onAutoFocus(focused, false); 1044 } 1045 } 1046 1047 private final class AutoFocusMoveCallback 1048 implements CameraAFMoveCallback { 1049 @Override onAutoFocusMoving( boolean moving, CameraProxy camera)1050 public void onAutoFocusMoving( 1051 boolean moving, CameraProxy camera) { 1052 mFocusManager.onAutoFocusMoving(moving); 1053 SessionStatsCollector.instance().autofocusMoving(moving); 1054 } 1055 } 1056 1057 /** 1058 * This class is just a thread-safe queue for name,date holder objects. 1059 */ 1060 public static class NamedImages { 1061 private final Vector<NamedEntity> mQueue; 1062 NamedImages()1063 public NamedImages() { 1064 mQueue = new Vector<NamedEntity>(); 1065 } 1066 nameNewImage(long date)1067 public void nameNewImage(long date) { 1068 NamedEntity r = new NamedEntity(); 1069 r.title = CameraUtil.instance().createJpegName(date); 1070 r.date = date; 1071 mQueue.add(r); 1072 } 1073 getNextNameEntity()1074 public NamedEntity getNextNameEntity() { 1075 synchronized (mQueue) { 1076 if (!mQueue.isEmpty()) { 1077 return mQueue.remove(0); 1078 } 1079 } 1080 return null; 1081 } 1082 1083 public static class NamedEntity { 1084 public String title; 1085 public long date; 1086 } 1087 } 1088 setCameraState(int state)1089 private void setCameraState(int state) { 1090 mCameraState = state; 1091 switch (state) { 1092 case PREVIEW_STOPPED: 1093 case SNAPSHOT_IN_PROGRESS: 1094 case SWITCHING_CAMERA: 1095 // TODO: Tell app UI to disable swipe 1096 break; 1097 case PhotoController.IDLE: 1098 // TODO: Tell app UI to enable swipe 1099 break; 1100 } 1101 } 1102 animateAfterShutter()1103 private void animateAfterShutter() { 1104 // Only animate when in full screen capture mode 1105 // i.e. If monkey/a user swipes to the gallery during picture taking, 1106 // don't show animation 1107 if (!mIsImageCaptureIntent) { 1108 mUI.animateFlash(); 1109 } 1110 } 1111 1112 @Override capture()1113 public boolean capture() { 1114 Log.i(TAG, "capture"); 1115 // If we are already in the middle of taking a snapshot or the image 1116 // save request is full then ignore. 1117 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS 1118 || mCameraState == SWITCHING_CAMERA) { 1119 return false; 1120 } 1121 setCameraState(SNAPSHOT_IN_PROGRESS); 1122 1123 mCaptureStartTime = System.currentTimeMillis(); 1124 1125 mPostViewPictureCallbackTime = 0; 1126 mJpegImageData = null; 1127 1128 final boolean animateBefore = (mSceneMode == CameraCapabilities.SceneMode.HDR); 1129 1130 if (animateBefore) { 1131 animateAfterShutter(); 1132 } 1133 1134 Location loc = mActivity.getLocationManager().getCurrentLocation(); 1135 CameraUtil.setGpsParameters(mCameraSettings, loc); 1136 mCameraDevice.applySettings(mCameraSettings); 1137 1138 // Set JPEG orientation. Even if screen UI is locked in portrait, camera orientation should 1139 // still match device orientation (e.g., users should always get landscape photos while 1140 // capturing by putting device in landscape.) 1141 Characteristics info = mActivity.getCameraProvider().getCharacteristics(mCameraId); 1142 int sensorOrientation = info.getSensorOrientation(); 1143 int deviceOrientation = 1144 mAppController.getOrientationManager().getDeviceOrientation().getDegrees(); 1145 boolean isFrontCamera = info.isFacingFront(); 1146 mJpegRotation = 1147 CameraUtil.getImageRotation(sensorOrientation, deviceOrientation, isFrontCamera); 1148 mCameraDevice.setJpegOrientation(mJpegRotation); 1149 1150 mCameraDevice.takePicture(mHandler, 1151 new ShutterCallback(!animateBefore), 1152 mRawPictureCallback, mPostViewPictureCallback, 1153 new JpegPictureCallback(loc)); 1154 1155 mNamedImages.nameNewImage(mCaptureStartTime); 1156 1157 mFaceDetectionStarted = false; 1158 return true; 1159 } 1160 1161 @Override setFocusParameters()1162 public void setFocusParameters() { 1163 setCameraParameters(UPDATE_PARAM_PREFERENCE); 1164 } 1165 updateSceneMode()1166 private void updateSceneMode() { 1167 // If scene mode is set, we cannot set flash mode, white balance, and 1168 // focus mode, instead, we read it from driver. Some devices don't have 1169 // any scene modes, so we must check both NO_SCENE_MODE in addition to 1170 // AUTO to check where there is no actual scene mode set. 1171 if (!(CameraCapabilities.SceneMode.AUTO == mSceneMode || 1172 CameraCapabilities.SceneMode.NO_SCENE_MODE == mSceneMode)) { 1173 overrideCameraSettings(mCameraSettings.getCurrentFlashMode(), 1174 mCameraSettings.getCurrentFocusMode()); 1175 } 1176 } 1177 overrideCameraSettings(CameraCapabilities.FlashMode flashMode, CameraCapabilities.FocusMode focusMode)1178 private void overrideCameraSettings(CameraCapabilities.FlashMode flashMode, 1179 CameraCapabilities.FocusMode focusMode) { 1180 CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier(); 1181 SettingsManager settingsManager = mActivity.getSettingsManager(); 1182 if ((flashMode != null) && (!CameraCapabilities.FlashMode.NO_FLASH.equals(flashMode))) { 1183 String flashModeString = stringifier.stringify(flashMode); 1184 Log.v(TAG, "override flash setting to: " + flashModeString); 1185 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_FLASH_MODE, 1186 flashModeString); 1187 } else { 1188 Log.v(TAG, "skip setting flash mode on override due to NO_FLASH"); 1189 } 1190 if (focusMode != null) { 1191 String focusModeString = stringifier.stringify(focusMode); 1192 Log.v(TAG, "override focus setting to: " + focusModeString); 1193 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_FOCUS_MODE, 1194 focusModeString); 1195 } 1196 } 1197 1198 @Override onCameraAvailable(CameraProxy cameraProxy)1199 public void onCameraAvailable(CameraProxy cameraProxy) { 1200 Log.i(TAG, "onCameraAvailable"); 1201 if (mPaused) { 1202 return; 1203 } 1204 mCameraDevice = cameraProxy; 1205 1206 initializeCapabilities(); 1207 // mCameraCapabilities is guaranteed to initialized at this point. 1208 mAppController.getCameraAppUI().showAccessibilityZoomUI( 1209 mCameraCapabilities.getMaxZoomRatio()); 1210 1211 1212 // Reset zoom value index. 1213 mZoomValue = 1.0f; 1214 if (mFocusManager == null) { 1215 initializeFocusManager(); 1216 } 1217 mFocusManager.updateCapabilities(mCameraCapabilities); 1218 1219 // Do camera parameter dependent initialization. 1220 mCameraSettings = mCameraDevice.getSettings(); 1221 // Set a default flash mode and focus mode 1222 if (mCameraSettings.getCurrentFlashMode() == null) { 1223 mCameraSettings.setFlashMode(CameraCapabilities.FlashMode.NO_FLASH); 1224 } 1225 if (mCameraSettings.getCurrentFocusMode() == null) { 1226 mCameraSettings.setFocusMode(CameraCapabilities.FocusMode.AUTO); 1227 } 1228 1229 setCameraParameters(UPDATE_PARAM_ALL); 1230 // Set a listener which updates camera parameters based 1231 // on changed settings. 1232 SettingsManager settingsManager = mActivity.getSettingsManager(); 1233 settingsManager.addListener(this); 1234 mCameraPreviewParamsReady = true; 1235 1236 startPreview(); 1237 1238 onCameraOpened(); 1239 1240 mHardwareSpec = new HardwareSpecImpl(getCameraProvider(), mCameraCapabilities, 1241 mAppController.getCameraFeatureConfig(), isCameraFrontFacing()); 1242 1243 ButtonManager buttonManager = mActivity.getButtonManager(); 1244 buttonManager.enableCameraButton(); 1245 } 1246 1247 @Override onCaptureCancelled()1248 public void onCaptureCancelled() { 1249 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent()); 1250 mActivity.finish(); 1251 } 1252 1253 @Override onCaptureRetake()1254 public void onCaptureRetake() { 1255 Log.i(TAG, "onCaptureRetake"); 1256 if (mPaused) { 1257 return; 1258 } 1259 mUI.hidePostCaptureAlert(); 1260 mUI.hideIntentReviewImageView(); 1261 setupPreview(); 1262 } 1263 1264 @Override onCaptureDone()1265 public void onCaptureDone() { 1266 Log.i(TAG, "onCaptureDone"); 1267 if (mPaused) { 1268 return; 1269 } 1270 1271 byte[] data = mJpegImageData; 1272 1273 if (mCropValue == null) { 1274 // First handle the no crop case -- just return the value. If the 1275 // caller specifies a "save uri" then write the data to its 1276 // stream. Otherwise, pass back a scaled down version of the bitmap 1277 // directly in the extras. 1278 if (mSaveUri != null) { 1279 OutputStream outputStream = null; 1280 try { 1281 outputStream = mContentResolver.openOutputStream(mSaveUri); 1282 outputStream.write(data); 1283 outputStream.close(); 1284 1285 Log.v(TAG, "saved result to URI: " + mSaveUri); 1286 mActivity.setResultEx(Activity.RESULT_OK); 1287 mActivity.finish(); 1288 } catch (IOException ex) { 1289 onError(); 1290 } finally { 1291 CameraUtil.closeSilently(outputStream); 1292 } 1293 } else { 1294 ExifInterface exif = Exif.getExif(data); 1295 int orientation = Exif.getOrientation(exif); 1296 Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024); 1297 bitmap = CameraUtil.rotate(bitmap, orientation); 1298 Log.v(TAG, "inlined bitmap into capture intent result"); 1299 mActivity.setResultEx(Activity.RESULT_OK, 1300 new Intent("inline-data").putExtra("data", bitmap)); 1301 mActivity.finish(); 1302 } 1303 } else { 1304 // Save the image to a temp file and invoke the cropper 1305 Uri tempUri = null; 1306 FileOutputStream tempStream = null; 1307 try { 1308 File path = mActivity.getFileStreamPath(sTempCropFilename); 1309 path.delete(); 1310 tempStream = mActivity.openFileOutput(sTempCropFilename, 0); 1311 tempStream.write(data); 1312 tempStream.close(); 1313 tempUri = Uri.fromFile(path); 1314 Log.v(TAG, "wrote temp file for cropping to: " + sTempCropFilename); 1315 } catch (FileNotFoundException ex) { 1316 Log.w(TAG, "error writing temp cropping file to: " + sTempCropFilename, ex); 1317 mActivity.setResultEx(Activity.RESULT_CANCELED); 1318 onError(); 1319 return; 1320 } catch (IOException ex) { 1321 Log.w(TAG, "error writing temp cropping file to: " + sTempCropFilename, ex); 1322 mActivity.setResultEx(Activity.RESULT_CANCELED); 1323 onError(); 1324 return; 1325 } finally { 1326 CameraUtil.closeSilently(tempStream); 1327 } 1328 1329 Bundle newExtras = new Bundle(); 1330 if (mCropValue.equals("circle")) { 1331 newExtras.putString("circleCrop", "true"); 1332 } 1333 if (mSaveUri != null) { 1334 Log.v(TAG, "setting output of cropped file to: " + mSaveUri); 1335 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri); 1336 } else { 1337 newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true); 1338 } 1339 if (mActivity.isSecureCamera()) { 1340 newExtras.putBoolean(CameraUtil.KEY_SHOW_WHEN_LOCKED, true); 1341 } 1342 1343 // TODO: Share this constant. 1344 final String CROP_ACTION = "com.android.camera.action.CROP"; 1345 Intent cropIntent = new Intent(CROP_ACTION); 1346 1347 cropIntent.setData(tempUri); 1348 cropIntent.putExtras(newExtras); 1349 Log.v(TAG, "starting CROP intent for capture"); 1350 mActivity.startActivityForResult(cropIntent, REQUEST_CROP); 1351 } 1352 } 1353 1354 @Override onShutterCoordinate(TouchCoordinate coord)1355 public void onShutterCoordinate(TouchCoordinate coord) { 1356 mShutterTouchCoordinate = coord; 1357 } 1358 1359 @Override onShutterButtonFocus(boolean pressed)1360 public void onShutterButtonFocus(boolean pressed) { 1361 // Do nothing. We don't support half-press to focus anymore. 1362 } 1363 1364 @Override onShutterButtonClick()1365 public void onShutterButtonClick() { 1366 if (mPaused || (mCameraState == SWITCHING_CAMERA) 1367 || (mCameraState == PREVIEW_STOPPED) 1368 || !mAppController.isShutterEnabled()) { 1369 mVolumeButtonClickedFlag = false; 1370 return; 1371 } 1372 1373 // Do not take the picture if there is not enough storage. 1374 if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) { 1375 Log.i(TAG, "Not enough space or storage not ready. remaining=" 1376 + mActivity.getStorageSpaceBytes()); 1377 mVolumeButtonClickedFlag = false; 1378 return; 1379 } 1380 Log.d(TAG, "onShutterButtonClick: mCameraState=" + mCameraState + 1381 " mVolumeButtonClickedFlag=" + mVolumeButtonClickedFlag); 1382 1383 mAppController.setShutterEnabled(false); 1384 1385 int countDownDuration = mActivity.getSettingsManager() 1386 .getInteger(SettingsManager.SCOPE_GLOBAL, Keys.KEY_COUNTDOWN_DURATION); 1387 mTimerDuration = countDownDuration; 1388 if (countDownDuration > 0) { 1389 // Start count down. 1390 mAppController.getCameraAppUI().transitionToCancel(); 1391 mAppController.getCameraAppUI().hideModeOptions(); 1392 mUI.startCountdown(countDownDuration); 1393 return; 1394 } else { 1395 focusAndCapture(); 1396 } 1397 } 1398 focusAndCapture()1399 private void focusAndCapture() { 1400 if (mSceneMode == CameraCapabilities.SceneMode.HDR) { 1401 mUI.setSwipingEnabled(false); 1402 } 1403 // If the user wants to do a snapshot while the previous one is still 1404 // in progress, remember the fact and do it after we finish the previous 1405 // one and re-start the preview. Snapshot in progress also includes the 1406 // state that autofocus is focusing and a picture will be taken when 1407 // focus callback arrives. 1408 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)) { 1409 if (!mIsImageCaptureIntent) { 1410 mSnapshotOnIdle = true; 1411 } 1412 return; 1413 } 1414 1415 mSnapshotOnIdle = false; 1416 mFocusManager.focusAndCapture(mCameraSettings.getCurrentFocusMode()); 1417 } 1418 1419 @Override onRemainingSecondsChanged(int remainingSeconds)1420 public void onRemainingSecondsChanged(int remainingSeconds) { 1421 if (remainingSeconds == 1) { 1422 mCountdownSoundPlayer.play(R.raw.timer_final_second, 0.6f); 1423 } else if (remainingSeconds == 2 || remainingSeconds == 3) { 1424 mCountdownSoundPlayer.play(R.raw.timer_increment, 0.6f); 1425 } 1426 } 1427 1428 @Override onCountDownFinished()1429 public void onCountDownFinished() { 1430 mAppController.getCameraAppUI().transitionToCapture(); 1431 mAppController.getCameraAppUI().showModeOptions(); 1432 if (mPaused) { 1433 return; 1434 } 1435 focusAndCapture(); 1436 } 1437 1438 @Override resume()1439 public void resume() { 1440 mPaused = false; 1441 1442 mCountdownSoundPlayer.loadSound(R.raw.timer_final_second); 1443 mCountdownSoundPlayer.loadSound(R.raw.timer_increment); 1444 if (mFocusManager != null) { 1445 // If camera is not open when resume is called, focus manager will 1446 // not be initialized yet, in which case it will start listening to 1447 // preview area size change later in the initialization. 1448 mAppController.addPreviewAreaSizeChangedListener(mFocusManager); 1449 } 1450 mAppController.addPreviewAreaSizeChangedListener(mUI); 1451 1452 CameraProvider camProvider = mActivity.getCameraProvider(); 1453 if (camProvider == null) { 1454 // No camera provider, the Activity is destroyed already. 1455 return; 1456 } 1457 1458 // Close the review UI if it's currently visible. 1459 mUI.hidePostCaptureAlert(); 1460 mUI.hideIntentReviewImageView(); 1461 1462 requestCameraOpen(); 1463 1464 mJpegPictureCallbackTime = 0; 1465 mZoomValue = 1.0f; 1466 1467 mOnResumeTime = SystemClock.uptimeMillis(); 1468 checkDisplayRotation(); 1469 1470 // If first time initialization is not finished, put it in the 1471 // message queue. 1472 if (!mFirstTimeInitialized) { 1473 mHandler.sendEmptyMessage(MSG_FIRST_TIME_INIT); 1474 } else { 1475 initializeSecondTime(); 1476 } 1477 1478 mHeadingSensor.activate(); 1479 1480 getServices().getRemoteShutterListener().onModuleReady(this); 1481 SessionStatsCollector.instance().sessionActive(true); 1482 } 1483 1484 /** 1485 * @return Whether the currently active camera is front-facing. 1486 */ isCameraFrontFacing()1487 private boolean isCameraFrontFacing() { 1488 return mAppController.getCameraProvider().getCharacteristics(mCameraId) 1489 .isFacingFront(); 1490 } 1491 1492 /** 1493 * The focus manager is the first UI related element to get initialized, and 1494 * it requires the RenderOverlay, so initialize it here 1495 */ initializeFocusManager()1496 private void initializeFocusManager() { 1497 // Create FocusManager object. startPreview needs it. 1498 // if mFocusManager not null, reuse it 1499 // otherwise create a new instance 1500 if (mFocusManager != null) { 1501 mFocusManager.removeMessages(); 1502 } else { 1503 mMirror = isCameraFrontFacing(); 1504 String[] defaultFocusModesStrings = mActivity.getResources().getStringArray( 1505 R.array.pref_camera_focusmode_default_array); 1506 ArrayList<CameraCapabilities.FocusMode> defaultFocusModes = 1507 new ArrayList<CameraCapabilities.FocusMode>(); 1508 CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier(); 1509 for (String modeString : defaultFocusModesStrings) { 1510 CameraCapabilities.FocusMode mode = stringifier.focusModeFromString(modeString); 1511 if (mode != null) { 1512 defaultFocusModes.add(mode); 1513 } 1514 } 1515 mFocusManager = 1516 new FocusOverlayManager(mAppController, defaultFocusModes, 1517 mCameraCapabilities, this, mMirror, mActivity.getMainLooper(), 1518 mUI.getFocusRing()); 1519 mMotionManager = getServices().getMotionManager(); 1520 if (mMotionManager != null) { 1521 mMotionManager.addListener(mFocusManager); 1522 } 1523 } 1524 mAppController.addPreviewAreaSizeChangedListener(mFocusManager); 1525 } 1526 1527 /** 1528 * @return Whether we are resuming from within the lockscreen. 1529 */ isResumeFromLockscreen()1530 private boolean isResumeFromLockscreen() { 1531 String action = mActivity.getIntent().getAction(); 1532 return (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action) 1533 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)); 1534 } 1535 1536 @Override pause()1537 public void pause() { 1538 Log.v(TAG, "pause"); 1539 mPaused = true; 1540 getServices().getRemoteShutterListener().onModuleExit(); 1541 SessionStatsCollector.instance().sessionActive(false); 1542 1543 mHeadingSensor.deactivate(); 1544 1545 // Reset the focus first. Camera CTS does not guarantee that 1546 // cancelAutoFocus is allowed after preview stops. 1547 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) { 1548 mCameraDevice.cancelAutoFocus(); 1549 } 1550 1551 // If the camera has not been opened asynchronously yet, 1552 // and startPreview hasn't been called, then this is a no-op. 1553 // (e.g. onResume -> onPause -> onResume). 1554 stopPreview(); 1555 cancelCountDown(); 1556 mCountdownSoundPlayer.unloadSound(R.raw.timer_final_second); 1557 mCountdownSoundPlayer.unloadSound(R.raw.timer_increment); 1558 1559 mNamedImages = null; 1560 // If we are in an image capture intent and has taken 1561 // a picture, we just clear it in onPause. 1562 mJpegImageData = null; 1563 1564 // Remove the messages and runnables in the queue. 1565 mHandler.removeCallbacksAndMessages(null); 1566 1567 if (mMotionManager != null) { 1568 mMotionManager.removeListener(mFocusManager); 1569 mMotionManager = null; 1570 } 1571 1572 closeCamera(); 1573 mActivity.enableKeepScreenOn(false); 1574 mUI.onPause(); 1575 1576 mPendingSwitchCameraId = -1; 1577 if (mFocusManager != null) { 1578 mFocusManager.removeMessages(); 1579 } 1580 getServices().getMemoryManager().removeListener(this); 1581 mAppController.removePreviewAreaSizeChangedListener(mFocusManager); 1582 mAppController.removePreviewAreaSizeChangedListener(mUI); 1583 1584 SettingsManager settingsManager = mActivity.getSettingsManager(); 1585 settingsManager.removeListener(this); 1586 } 1587 1588 @Override destroy()1589 public void destroy() { 1590 mCountdownSoundPlayer.release(); 1591 } 1592 1593 @Override onLayoutOrientationChanged(boolean isLandscape)1594 public void onLayoutOrientationChanged(boolean isLandscape) { 1595 setDisplayOrientation(); 1596 } 1597 1598 @Override updateCameraOrientation()1599 public void updateCameraOrientation() { 1600 if (mDisplayRotation != CameraUtil.getDisplayRotation()) { 1601 setDisplayOrientation(); 1602 } 1603 } 1604 canTakePicture()1605 private boolean canTakePicture() { 1606 return isCameraIdle() 1607 && (mActivity.getStorageSpaceBytes() > Storage.LOW_STORAGE_THRESHOLD_BYTES); 1608 } 1609 1610 @Override autoFocus()1611 public void autoFocus() { 1612 if (mCameraDevice == null) { 1613 return; 1614 } 1615 Log.v(TAG,"Starting auto focus"); 1616 mFocusStartTime = System.currentTimeMillis(); 1617 mCameraDevice.autoFocus(mHandler, mAutoFocusCallback); 1618 SessionStatsCollector.instance().autofocusManualTrigger(); 1619 setCameraState(FOCUSING); 1620 } 1621 1622 @Override cancelAutoFocus()1623 public void cancelAutoFocus() { 1624 if (mCameraDevice == null) { 1625 return; 1626 } 1627 mCameraDevice.cancelAutoFocus(); 1628 setCameraState(IDLE); 1629 setCameraParameters(UPDATE_PARAM_PREFERENCE); 1630 } 1631 1632 @Override onSingleTapUp(View view, int x, int y)1633 public void onSingleTapUp(View view, int x, int y) { 1634 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized 1635 || mCameraState == SNAPSHOT_IN_PROGRESS 1636 || mCameraState == SWITCHING_CAMERA 1637 || mCameraState == PREVIEW_STOPPED) { 1638 return; 1639 } 1640 1641 // Check if metering area or focus area is supported. 1642 if (!mFocusAreaSupported && !mMeteringAreaSupported) { 1643 return; 1644 } 1645 mFocusManager.onSingleTapUp(x, y); 1646 } 1647 1648 @Override onBackPressed()1649 public boolean onBackPressed() { 1650 return mUI.onBackPressed(); 1651 } 1652 1653 @Override onKeyDown(int keyCode, KeyEvent event)1654 public boolean onKeyDown(int keyCode, KeyEvent event) { 1655 switch (keyCode) { 1656 case KeyEvent.KEYCODE_VOLUME_UP: 1657 case KeyEvent.KEYCODE_VOLUME_DOWN: 1658 case KeyEvent.KEYCODE_FOCUS: 1659 if (/* TODO: mActivity.isInCameraApp() && */mFirstTimeInitialized && 1660 !mActivity.getCameraAppUI().isInIntentReview()) { 1661 if (event.getRepeatCount() == 0) { 1662 onShutterButtonFocus(true); 1663 } 1664 return true; 1665 } 1666 return false; 1667 case KeyEvent.KEYCODE_CAMERA: 1668 if (mFirstTimeInitialized && event.getRepeatCount() == 0) { 1669 onShutterButtonClick(); 1670 } 1671 return true; 1672 case KeyEvent.KEYCODE_DPAD_CENTER: 1673 // If we get a dpad center event without any focused view, move 1674 // the focus to the shutter button and press it. 1675 if (mFirstTimeInitialized && event.getRepeatCount() == 0) { 1676 // Start auto-focus immediately to reduce shutter lag. After 1677 // the shutter button gets the focus, onShutterButtonFocus() 1678 // will be called again but it is fine. 1679 onShutterButtonFocus(true); 1680 } 1681 return true; 1682 } 1683 return false; 1684 } 1685 1686 @Override onKeyUp(int keyCode, KeyEvent event)1687 public boolean onKeyUp(int keyCode, KeyEvent event) { 1688 switch (keyCode) { 1689 case KeyEvent.KEYCODE_VOLUME_UP: 1690 case KeyEvent.KEYCODE_VOLUME_DOWN: 1691 if (/* mActivity.isInCameraApp() && */mFirstTimeInitialized && 1692 !mActivity.getCameraAppUI().isInIntentReview()) { 1693 if (mUI.isCountingDown()) { 1694 cancelCountDown(); 1695 } else { 1696 mVolumeButtonClickedFlag = true; 1697 onShutterButtonClick(); 1698 } 1699 return true; 1700 } 1701 return false; 1702 case KeyEvent.KEYCODE_FOCUS: 1703 if (mFirstTimeInitialized) { 1704 onShutterButtonFocus(false); 1705 } 1706 return true; 1707 } 1708 return false; 1709 } 1710 closeCamera()1711 private void closeCamera() { 1712 if (mCameraDevice != null) { 1713 stopFaceDetection(); 1714 mCameraDevice.setZoomChangeListener(null); 1715 mCameraDevice.setFaceDetectionCallback(null, null); 1716 1717 mFaceDetectionStarted = false; 1718 mActivity.getCameraProvider().releaseCamera(mCameraDevice.getCameraId()); 1719 mCameraDevice = null; 1720 setCameraState(PREVIEW_STOPPED); 1721 mFocusManager.onCameraReleased(); 1722 } 1723 } 1724 setDisplayOrientation()1725 private void setDisplayOrientation() { 1726 mDisplayRotation = CameraUtil.getDisplayRotation(); 1727 Characteristics info = 1728 mActivity.getCameraProvider().getCharacteristics(mCameraId); 1729 mDisplayOrientation = info.getPreviewOrientation(mDisplayRotation); 1730 mUI.setDisplayOrientation(mDisplayOrientation); 1731 if (mFocusManager != null) { 1732 mFocusManager.setDisplayOrientation(mDisplayOrientation); 1733 } 1734 // Change the camera display orientation 1735 if (mCameraDevice != null) { 1736 mCameraDevice.setDisplayOrientation(mDisplayRotation); 1737 } 1738 Log.v(TAG, "setDisplayOrientation (screen:preview) " + 1739 mDisplayRotation + ":" + mDisplayOrientation); 1740 } 1741 1742 /** Only called by UI thread. */ setupPreview()1743 private void setupPreview() { 1744 Log.i(TAG, "setupPreview"); 1745 mFocusManager.resetTouchFocus(); 1746 startPreview(); 1747 } 1748 1749 /** 1750 * Returns whether we can/should start the preview or not. 1751 */ checkPreviewPreconditions()1752 private boolean checkPreviewPreconditions() { 1753 if (mPaused) { 1754 return false; 1755 } 1756 1757 if (mCameraDevice == null) { 1758 Log.w(TAG, "startPreview: camera device not ready yet."); 1759 return false; 1760 } 1761 1762 SurfaceTexture st = mActivity.getCameraAppUI().getSurfaceTexture(); 1763 if (st == null) { 1764 Log.w(TAG, "startPreview: surfaceTexture is not ready."); 1765 return false; 1766 } 1767 1768 if (!mCameraPreviewParamsReady) { 1769 Log.w(TAG, "startPreview: parameters for preview is not ready."); 1770 return false; 1771 } 1772 return true; 1773 } 1774 1775 /** 1776 * The start/stop preview should only run on the UI thread. 1777 */ startPreview()1778 private void startPreview() { 1779 if (mCameraDevice == null) { 1780 Log.i(TAG, "attempted to start preview before camera device"); 1781 // do nothing 1782 return; 1783 } 1784 1785 if (!checkPreviewPreconditions()) { 1786 return; 1787 } 1788 1789 setDisplayOrientation(); 1790 1791 if (!mSnapshotOnIdle) { 1792 // If the focus mode is continuous autofocus, call cancelAutoFocus 1793 // to resume it because it may have been paused by autoFocus call. 1794 if (mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()) == 1795 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE) { 1796 mCameraDevice.cancelAutoFocus(); 1797 } 1798 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB. 1799 } 1800 1801 // Nexus 4 must have picture size set to > 640x480 before other 1802 // parameters are set in setCameraParameters, b/18227551. This call to 1803 // updateParametersPictureSize should occur before setCameraParameters 1804 // to address the issue. 1805 updateParametersPictureSize(); 1806 1807 setCameraParameters(UPDATE_PARAM_ALL); 1808 1809 mCameraDevice.setPreviewTexture(mActivity.getCameraAppUI().getSurfaceTexture()); 1810 1811 Log.i(TAG, "startPreview"); 1812 // If we're using API2 in portability layers, don't use startPreviewWithCallback() 1813 // b/17576554 1814 CameraAgent.CameraStartPreviewCallback startPreviewCallback = 1815 new CameraAgent.CameraStartPreviewCallback() { 1816 @Override 1817 public void onPreviewStarted() { 1818 mFocusManager.onPreviewStarted(); 1819 PhotoModule.this.onPreviewStarted(); 1820 SessionStatsCollector.instance().previewActive(true); 1821 if (mSnapshotOnIdle) { 1822 mHandler.post(mDoSnapRunnable); 1823 } 1824 } 1825 }; 1826 if (GservicesHelper.useCamera2ApiThroughPortabilityLayer(mActivity.getContentResolver())) { 1827 mCameraDevice.startPreview(); 1828 startPreviewCallback.onPreviewStarted(); 1829 } else { 1830 mCameraDevice.startPreviewWithCallback(new Handler(Looper.getMainLooper()), 1831 startPreviewCallback); 1832 } 1833 } 1834 1835 @Override stopPreview()1836 public void stopPreview() { 1837 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) { 1838 Log.i(TAG, "stopPreview"); 1839 mCameraDevice.stopPreview(); 1840 mFaceDetectionStarted = false; 1841 } 1842 setCameraState(PREVIEW_STOPPED); 1843 if (mFocusManager != null) { 1844 mFocusManager.onPreviewStopped(); 1845 } 1846 SessionStatsCollector.instance().previewActive(false); 1847 } 1848 1849 @Override onSettingChanged(SettingsManager settingsManager, String key)1850 public void onSettingChanged(SettingsManager settingsManager, String key) { 1851 if (key.equals(Keys.KEY_FLASH_MODE)) { 1852 updateParametersFlashMode(); 1853 } 1854 if (key.equals(Keys.KEY_CAMERA_HDR)) { 1855 if (settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL, 1856 Keys.KEY_CAMERA_HDR)) { 1857 // HDR is on. 1858 mAppController.getButtonManager().disableButton(ButtonManager.BUTTON_FLASH); 1859 mFlashModeBeforeSceneMode = settingsManager.getString( 1860 mAppController.getCameraScope(), Keys.KEY_FLASH_MODE); 1861 } else { 1862 if (mFlashModeBeforeSceneMode != null) { 1863 settingsManager.set(mAppController.getCameraScope(), 1864 Keys.KEY_FLASH_MODE, 1865 mFlashModeBeforeSceneMode); 1866 updateParametersFlashMode(); 1867 mFlashModeBeforeSceneMode = null; 1868 } 1869 mAppController.getButtonManager().enableButton(ButtonManager.BUTTON_FLASH); 1870 } 1871 } 1872 1873 if (mCameraDevice != null) { 1874 mCameraDevice.applySettings(mCameraSettings); 1875 } 1876 } 1877 updateCameraParametersInitialize()1878 private void updateCameraParametersInitialize() { 1879 // Reset preview frame rate to the maximum because it may be lowered by 1880 // video camera application. 1881 int[] fpsRange = CameraUtil.getPhotoPreviewFpsRange(mCameraCapabilities); 1882 if (fpsRange != null && fpsRange.length > 0) { 1883 mCameraSettings.setPreviewFpsRange(fpsRange[0], fpsRange[1]); 1884 } 1885 1886 mCameraSettings.setRecordingHintEnabled(false); 1887 1888 if (mCameraCapabilities.supports(CameraCapabilities.Feature.VIDEO_STABILIZATION)) { 1889 mCameraSettings.setVideoStabilization(false); 1890 } 1891 } 1892 updateCameraParametersZoom()1893 private void updateCameraParametersZoom() { 1894 // Set zoom. 1895 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) { 1896 mCameraSettings.setZoomRatio(mZoomValue); 1897 } 1898 } 1899 1900 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) setAutoExposureLockIfSupported()1901 private void setAutoExposureLockIfSupported() { 1902 if (mAeLockSupported) { 1903 mCameraSettings.setAutoExposureLock(mFocusManager.getAeAwbLock()); 1904 } 1905 } 1906 1907 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) setAutoWhiteBalanceLockIfSupported()1908 private void setAutoWhiteBalanceLockIfSupported() { 1909 if (mAwbLockSupported) { 1910 mCameraSettings.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock()); 1911 } 1912 } 1913 setFocusAreasIfSupported()1914 private void setFocusAreasIfSupported() { 1915 if (mFocusAreaSupported) { 1916 mCameraSettings.setFocusAreas(mFocusManager.getFocusAreas()); 1917 } 1918 } 1919 setMeteringAreasIfSupported()1920 private void setMeteringAreasIfSupported() { 1921 if (mMeteringAreaSupported) { 1922 mCameraSettings.setMeteringAreas(mFocusManager.getMeteringAreas()); 1923 } 1924 } 1925 updateCameraParametersPreference()1926 private void updateCameraParametersPreference() { 1927 // some monkey tests can get here when shutting the app down 1928 // make sure mCameraDevice is still valid, b/17580046 1929 if (mCameraDevice == null) { 1930 return; 1931 } 1932 1933 setAutoExposureLockIfSupported(); 1934 setAutoWhiteBalanceLockIfSupported(); 1935 setFocusAreasIfSupported(); 1936 setMeteringAreasIfSupported(); 1937 1938 // Initialize focus mode. 1939 mFocusManager.overrideFocusMode(null); 1940 mCameraSettings 1941 .setFocusMode(mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode())); 1942 SessionStatsCollector.instance().autofocusActive( 1943 mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()) == 1944 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE 1945 ); 1946 1947 // Set JPEG quality. 1948 updateParametersPictureQuality(); 1949 1950 // For the following settings, we need to check if the settings are 1951 // still supported by latest driver, if not, ignore the settings. 1952 1953 // Set exposure compensation 1954 updateParametersExposureCompensation(); 1955 1956 // Set the scene mode: also sets flash and white balance. 1957 updateParametersSceneMode(); 1958 1959 if (mContinuousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) { 1960 updateAutoFocusMoveCallback(); 1961 } 1962 } 1963 1964 /** 1965 * This method sets picture size parameters. Size parameters should only be 1966 * set when the preview is stopped, and so this method is only invoked in 1967 * {@link #startPreview()} just before starting the preview. 1968 */ updateParametersPictureSize()1969 private void updateParametersPictureSize() { 1970 if (mCameraDevice == null) { 1971 Log.w(TAG, "attempting to set picture size without caemra device"); 1972 return; 1973 } 1974 1975 List<Size> supported = Size.convert(mCameraCapabilities.getSupportedPhotoSizes()); 1976 CameraPictureSizesCacher.updateSizesForCamera(mAppController.getAndroidContext(), 1977 mCameraDevice.getCameraId(), supported); 1978 1979 OneCamera.Facing cameraFacing = 1980 isCameraFrontFacing() ? OneCamera.Facing.FRONT : OneCamera.Facing.BACK; 1981 Size pictureSize; 1982 try { 1983 pictureSize = mAppController.getResolutionSetting().getPictureSize( 1984 mAppController.getCameraProvider().getCurrentCameraId(), 1985 cameraFacing); 1986 } catch (OneCameraAccessException ex) { 1987 mAppController.getFatalErrorHandler().onGenericCameraAccessFailure(); 1988 return; 1989 } 1990 1991 mCameraSettings.setPhotoSize(pictureSize.toPortabilitySize()); 1992 1993 if (ApiHelper.IS_NEXUS_5) { 1994 if (ResolutionUtil.NEXUS_5_LARGE_16_BY_9.equals(pictureSize)) { 1995 mShouldResizeTo16x9 = true; 1996 } else { 1997 mShouldResizeTo16x9 = false; 1998 } 1999 } 2000 2001 // Set a preview size that is closest to the viewfinder height and has 2002 // the right aspect ratio. 2003 List<Size> sizes = Size.convert(mCameraCapabilities.getSupportedPreviewSizes()); 2004 Size optimalSize = CameraUtil.getOptimalPreviewSize(sizes, 2005 (double) pictureSize.width() / pictureSize.height()); 2006 Size original = new Size(mCameraSettings.getCurrentPreviewSize()); 2007 if (!optimalSize.equals(original)) { 2008 Log.v(TAG, "setting preview size. optimal: " + optimalSize + "original: " + original); 2009 mCameraSettings.setPreviewSize(optimalSize.toPortabilitySize()); 2010 2011 mCameraDevice.applySettings(mCameraSettings); 2012 mCameraSettings = mCameraDevice.getSettings(); 2013 } 2014 2015 if (optimalSize.width() != 0 && optimalSize.height() != 0) { 2016 Log.v(TAG, "updating aspect ratio"); 2017 mUI.updatePreviewAspectRatio((float) optimalSize.width() 2018 / (float) optimalSize.height()); 2019 } 2020 Log.d(TAG, "Preview size is " + optimalSize); 2021 } 2022 updateParametersPictureQuality()2023 private void updateParametersPictureQuality() { 2024 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId, 2025 CameraProfile.QUALITY_HIGH); 2026 mCameraSettings.setPhotoJpegCompressionQuality(jpegQuality); 2027 } 2028 updateParametersExposureCompensation()2029 private void updateParametersExposureCompensation() { 2030 SettingsManager settingsManager = mActivity.getSettingsManager(); 2031 if (settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL, 2032 Keys.KEY_EXPOSURE_COMPENSATION_ENABLED)) { 2033 int value = settingsManager.getInteger(mAppController.getCameraScope(), 2034 Keys.KEY_EXPOSURE); 2035 int max = mCameraCapabilities.getMaxExposureCompensation(); 2036 int min = mCameraCapabilities.getMinExposureCompensation(); 2037 if (value >= min && value <= max) { 2038 mCameraSettings.setExposureCompensationIndex(value); 2039 } else { 2040 Log.w(TAG, "invalid exposure range: " + value); 2041 } 2042 } else { 2043 // If exposure compensation is not enabled, reset the exposure compensation value. 2044 setExposureCompensation(0); 2045 } 2046 } 2047 updateParametersSceneMode()2048 private void updateParametersSceneMode() { 2049 CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier(); 2050 SettingsManager settingsManager = mActivity.getSettingsManager(); 2051 2052 mSceneMode = stringifier. 2053 sceneModeFromString(settingsManager.getString(mAppController.getCameraScope(), 2054 Keys.KEY_SCENE_MODE)); 2055 if (mCameraCapabilities.supports(mSceneMode)) { 2056 if (mCameraSettings.getCurrentSceneMode() != mSceneMode) { 2057 mCameraSettings.setSceneMode(mSceneMode); 2058 2059 // Setting scene mode will change the settings of flash mode, 2060 // white balance, and focus mode. Here we read back the 2061 // parameters, so we can know those settings. 2062 mCameraDevice.applySettings(mCameraSettings); 2063 mCameraSettings = mCameraDevice.getSettings(); 2064 } 2065 } else { 2066 mSceneMode = mCameraSettings.getCurrentSceneMode(); 2067 if (mSceneMode == null) { 2068 mSceneMode = CameraCapabilities.SceneMode.AUTO; 2069 } 2070 } 2071 2072 if (CameraCapabilities.SceneMode.AUTO == mSceneMode) { 2073 // Set flash mode. 2074 updateParametersFlashMode(); 2075 2076 // Set focus mode. 2077 mFocusManager.overrideFocusMode(null); 2078 mCameraSettings.setFocusMode( 2079 mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode())); 2080 } else { 2081 mFocusManager.overrideFocusMode(mCameraSettings.getCurrentFocusMode()); 2082 } 2083 } 2084 updateParametersFlashMode()2085 private void updateParametersFlashMode() { 2086 SettingsManager settingsManager = mActivity.getSettingsManager(); 2087 2088 CameraCapabilities.FlashMode flashMode = mCameraCapabilities.getStringifier() 2089 .flashModeFromString(settingsManager.getString(mAppController.getCameraScope(), 2090 Keys.KEY_FLASH_MODE)); 2091 if (mCameraCapabilities.supports(flashMode)) { 2092 mCameraSettings.setFlashMode(flashMode); 2093 } 2094 } 2095 2096 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) updateAutoFocusMoveCallback()2097 private void updateAutoFocusMoveCallback() { 2098 if (mCameraDevice == null) { 2099 return; 2100 } 2101 if (mCameraSettings.getCurrentFocusMode() == 2102 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE) { 2103 mCameraDevice.setAutoFocusMoveCallback(mHandler, 2104 (CameraAFMoveCallback) mAutoFocusMoveCallback); 2105 } else { 2106 mCameraDevice.setAutoFocusMoveCallback(null, null); 2107 } 2108 } 2109 2110 /** 2111 * Sets the exposure compensation to the given value and also updates settings. 2112 * 2113 * @param value exposure compensation value to be set 2114 */ setExposureCompensation(int value)2115 public void setExposureCompensation(int value) { 2116 int max = mCameraCapabilities.getMaxExposureCompensation(); 2117 int min = mCameraCapabilities.getMinExposureCompensation(); 2118 if (value >= min && value <= max) { 2119 mCameraSettings.setExposureCompensationIndex(value); 2120 SettingsManager settingsManager = mActivity.getSettingsManager(); 2121 settingsManager.set(mAppController.getCameraScope(), 2122 Keys.KEY_EXPOSURE, value); 2123 } else { 2124 Log.w(TAG, "invalid exposure range: " + value); 2125 } 2126 } 2127 2128 // We separate the parameters into several subsets, so we can update only 2129 // the subsets actually need updating. The PREFERENCE set needs extra 2130 // locking because the preference can be changed from GLThread as well. setCameraParameters(int updateSet)2131 private void setCameraParameters(int updateSet) { 2132 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) { 2133 updateCameraParametersInitialize(); 2134 } 2135 2136 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) { 2137 updateCameraParametersZoom(); 2138 } 2139 2140 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) { 2141 updateCameraParametersPreference(); 2142 } 2143 2144 if (mCameraDevice != null) { 2145 mCameraDevice.applySettings(mCameraSettings); 2146 } 2147 } 2148 2149 // If the Camera is idle, update the parameters immediately, otherwise 2150 // accumulate them in mUpdateSet and update later. setCameraParametersWhenIdle(int additionalUpdateSet)2151 private void setCameraParametersWhenIdle(int additionalUpdateSet) { 2152 mUpdateSet |= additionalUpdateSet; 2153 if (mCameraDevice == null) { 2154 // We will update all the parameters when we open the device, so 2155 // we don't need to do anything now. 2156 mUpdateSet = 0; 2157 return; 2158 } else if (isCameraIdle()) { 2159 setCameraParameters(mUpdateSet); 2160 updateSceneMode(); 2161 mUpdateSet = 0; 2162 } else { 2163 if (!mHandler.hasMessages(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE)) { 2164 mHandler.sendEmptyMessageDelayed(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000); 2165 } 2166 } 2167 } 2168 2169 @Override isCameraIdle()2170 public boolean isCameraIdle() { 2171 return (mCameraState == IDLE) || 2172 (mCameraState == PREVIEW_STOPPED) || 2173 ((mFocusManager != null) && mFocusManager.isFocusCompleted() 2174 && (mCameraState != SWITCHING_CAMERA)); 2175 } 2176 2177 @Override isImageCaptureIntent()2178 public boolean isImageCaptureIntent() { 2179 String action = mActivity.getIntent().getAction(); 2180 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action) 2181 || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action)); 2182 } 2183 setupCaptureParams()2184 private void setupCaptureParams() { 2185 Bundle myExtras = mActivity.getIntent().getExtras(); 2186 if (myExtras != null) { 2187 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT); 2188 mCropValue = myExtras.getString("crop"); 2189 } 2190 } 2191 initializeCapabilities()2192 private void initializeCapabilities() { 2193 mCameraCapabilities = mCameraDevice.getCapabilities(); 2194 mFocusAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA); 2195 mMeteringAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.METERING_AREA); 2196 mAeLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK); 2197 mAwbLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK); 2198 mContinuousFocusSupported = 2199 mCameraCapabilities.supports(CameraCapabilities.FocusMode.CONTINUOUS_PICTURE); 2200 } 2201 2202 @Override onZoomChanged(float ratio)2203 public void onZoomChanged(float ratio) { 2204 // Not useful to change zoom value when the activity is paused. 2205 if (mPaused) { 2206 return; 2207 } 2208 mZoomValue = ratio; 2209 if (mCameraSettings == null || mCameraDevice == null) { 2210 return; 2211 } 2212 // Set zoom parameters asynchronously 2213 mCameraSettings.setZoomRatio(mZoomValue); 2214 mCameraDevice.applySettings(mCameraSettings); 2215 } 2216 2217 @Override getCameraState()2218 public int getCameraState() { 2219 return mCameraState; 2220 } 2221 2222 @Override onMemoryStateChanged(int state)2223 public void onMemoryStateChanged(int state) { 2224 mAppController.setShutterEnabled(state == MemoryManager.STATE_OK); 2225 } 2226 2227 @Override onLowMemory()2228 public void onLowMemory() { 2229 // Not much we can do in the photo module. 2230 } 2231 2232 // For debugging only. setDebugUri(Uri uri)2233 public void setDebugUri(Uri uri) { 2234 mDebugUri = uri; 2235 } 2236 2237 // For debugging only. saveToDebugUri(byte[] data)2238 private void saveToDebugUri(byte[] data) { 2239 if (mDebugUri != null) { 2240 OutputStream outputStream = null; 2241 try { 2242 outputStream = mContentResolver.openOutputStream(mDebugUri); 2243 outputStream.write(data); 2244 outputStream.close(); 2245 } catch (IOException e) { 2246 Log.e(TAG, "Exception while writing debug jpeg file", e); 2247 } finally { 2248 CameraUtil.closeSilently(outputStream); 2249 } 2250 } 2251 } 2252 2253 @Override onRemoteShutterPress()2254 public void onRemoteShutterPress() { 2255 mHandler.post(new Runnable() { 2256 @Override 2257 public void run() { 2258 focusAndCapture(); 2259 } 2260 }); 2261 } 2262 } 2263