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