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