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