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