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