PhotoModule.java revision e6374b76bd10c7e9120b8530b2dae0f4b321ceea
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.SurfaceTexture; 26import android.hardware.Camera.CameraInfo; 27import android.hardware.Camera.Parameters; 28import android.hardware.Camera.Size; 29import android.hardware.Sensor; 30import android.hardware.SensorEvent; 31import android.hardware.SensorEventListener; 32import android.hardware.SensorManager; 33import android.location.Location; 34import android.media.CameraProfile; 35import android.net.Uri; 36import android.os.Build; 37import android.os.Bundle; 38import android.os.Handler; 39import android.os.Looper; 40import android.os.Message; 41import android.os.MessageQueue; 42import android.os.SystemClock; 43import android.provider.MediaStore; 44import android.view.KeyEvent; 45import android.view.OrientationEventListener; 46import android.view.View; 47 48import com.android.camera.PhotoModule.NamedImages.NamedEntity; 49import com.android.camera.app.AppController; 50import com.android.camera.app.CameraAppUI; 51import com.android.camera.app.CameraManager.CameraAFCallback; 52import com.android.camera.app.CameraManager.CameraAFMoveCallback; 53import com.android.camera.app.CameraManager.CameraPictureCallback; 54import com.android.camera.app.CameraManager.CameraProxy; 55import com.android.camera.app.CameraManager.CameraShutterCallback; 56import com.android.camera.app.LocationManager; 57import com.android.camera.app.MediaSaver; 58import com.android.camera.app.MemoryManager; 59import com.android.camera.app.MemoryManager.MemoryListener; 60import com.android.camera.debug.Log; 61import com.android.camera.exif.ExifInterface; 62import com.android.camera.exif.ExifTag; 63import com.android.camera.exif.Rational; 64import com.android.camera.hardware.HardwareSpec; 65import com.android.camera.hardware.HardwareSpecImpl; 66import com.android.camera.module.ModuleController; 67import com.android.camera.settings.SettingsManager; 68import com.android.camera.settings.SettingsUtil; 69import com.android.camera.util.ApiHelper; 70import com.android.camera.util.CameraUtil; 71import com.android.camera.util.GcamHelper; 72import com.android.camera.util.SessionStatsCollector; 73import com.android.camera.util.UsageStatistics; 74import com.android.camera2.R; 75import com.google.common.logging.eventprotos; 76 77import java.io.File; 78import java.io.FileNotFoundException; 79import java.io.FileOutputStream; 80import java.io.IOException; 81import java.io.OutputStream; 82import java.lang.ref.WeakReference; 83import java.util.List; 84import java.util.Vector; 85 86public class PhotoModule 87 extends CameraModule 88 implements PhotoController, 89 ModuleController, 90 MemoryListener, 91 FocusOverlayManager.Listener, 92 SensorEventListener, 93 SettingsManager.OnSettingChangedListener { 94 95 private static final Log.Tag TAG = new Log.Tag("PhotoModule"); 96 97 // We number the request code from 1000 to avoid collision with Gallery. 98 private static final int REQUEST_CROP = 1000; 99 100 // Messages defined for the UI thread handler. 101 private static final int MSG_FIRST_TIME_INIT = 1; 102 private static final int MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE = 2; 103 104 // The subset of parameters we need to update in setCameraParameters(). 105 private static final int UPDATE_PARAM_INITIALIZE = 1; 106 private static final int UPDATE_PARAM_ZOOM = 2; 107 private static final int UPDATE_PARAM_PREFERENCE = 4; 108 private static final int UPDATE_PARAM_ALL = -1; 109 110 // This is the delay before we execute onResume tasks when coming 111 // from the lock screen, to allow time for onPause to execute. 112 private static final int ON_RESUME_TASKS_DELAY_MSEC = 20; 113 114 private static final String DEBUG_IMAGE_PREFIX = "DEBUG_"; 115 116 private static final boolean NO_STOP_PREVIEW_ICS_WORKAROUND = 117 shouldNotCallStopPreviewAfterTakingPicture(); 118 119 private CameraActivity mActivity; 120 private CameraProxy mCameraDevice; 121 private int mCameraId; 122 private Parameters mParameters; 123 private boolean mPaused; 124 125 private PhotoUI mUI; 126 127 // The activity is going to switch to the specified camera id. This is 128 // needed because texture copy is done in GL thread. -1 means camera is not 129 // switching. 130 protected int mPendingSwitchCameraId = -1; 131 132 // When setCameraParametersWhenIdle() is called, we accumulate the subsets 133 // needed to be updated in mUpdateSet. 134 private int mUpdateSet; 135 136 private static final int SCREEN_DELAY = 2 * 60 * 1000; 137 138 private int mZoomValue; // The current zoom value. 139 140 private Parameters mInitialParams; 141 private boolean mFocusAreaSupported; 142 private boolean mMeteringAreaSupported; 143 private boolean mAeLockSupported; 144 private boolean mAwbLockSupported; 145 private boolean mContinuousFocusSupported; 146 147 // The degrees of the device rotated clockwise from its natural orientation. 148 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN; 149 150 private static final String sTempCropFilename = "crop-temp"; 151 152 private boolean mFaceDetectionStarted = false; 153 154 // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true. 155 private String mCropValue; 156 private Uri mSaveUri; 157 158 private Uri mDebugUri; 159 160 // We use a queue to generated names of the images to be used later 161 // when the image is ready to be saved. 162 private NamedImages mNamedImages; 163 164 private final Runnable mDoSnapRunnable = new Runnable() { 165 @Override 166 public void run() { 167 onShutterButtonClick(); 168 } 169 }; 170 171 /** 172 * An unpublished intent flag requesting to return as soon as capturing is 173 * completed. TODO: consider publishing by moving into MediaStore. 174 */ 175 private static final String EXTRA_QUICK_CAPTURE = 176 "android.intent.extra.quickCapture"; 177 178 // The display rotation in degrees. This is only valid when mCameraState is 179 // not PREVIEW_STOPPED. 180 private int mDisplayRotation; 181 // The value for android.hardware.Camera.setDisplayOrientation. 182 private int mCameraDisplayOrientation; 183 // The value for UI components like indicators. 184 private int mDisplayOrientation; 185 // The value for android.hardware.Camera.Parameters.setRotation. 186 private int mJpegRotation; 187 // Indicates whether we are using front camera 188 private boolean mMirror; 189 private boolean mFirstTimeInitialized; 190 private boolean mIsImageCaptureIntent; 191 192 private int mCameraState = PREVIEW_STOPPED; 193 private boolean mSnapshotOnIdle = false; 194 195 private ContentResolver mContentResolver; 196 197 private LocationManager mLocationManager; 198 private AppController mAppController; 199 200 private final PostViewPictureCallback mPostViewPictureCallback = 201 new PostViewPictureCallback(); 202 private final RawPictureCallback mRawPictureCallback = 203 new RawPictureCallback(); 204 private final AutoFocusCallback mAutoFocusCallback = 205 new AutoFocusCallback(); 206 private final Object mAutoFocusMoveCallback = 207 ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK 208 ? new AutoFocusMoveCallback() 209 : null; 210 211 private final CameraErrorCallback mErrorCallback = new CameraErrorCallback(); 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 221 // These latency time are for the CameraLatency test. 222 public long mAutoFocusTime; 223 public long mShutterLag; 224 public long mShutterToPictureDisplayedTime; 225 public long mPictureDisplayedToJpegCallbackTime; 226 public long mJpegCallbackFinishTime; 227 public long mCaptureStartTime; 228 229 // This handles everything about focus. 230 private FocusOverlayManager mFocusManager; 231 232 private final int mGcamModeIndex; 233 234 private String mSceneMode; 235 236 private final Handler mHandler = new MainHandler(this); 237 238 private boolean mQuickCapture; 239 private SensorManager mSensorManager; 240 private final float[] mGData = new float[3]; 241 private final float[] mMData = new float[3]; 242 private final float[] mR = new float[16]; 243 private int mHeading = -1; 244 245 /** True if all the parameters needed to start preview is ready. */ 246 private boolean mCameraPreviewParamsReady = false; 247 248 private final MediaSaver.OnMediaSavedListener mOnMediaSavedListener = 249 new MediaSaver.OnMediaSavedListener() { 250 @Override 251 public void onMediaSaved(Uri uri) { 252 if (uri != null) { 253 mActivity.notifyNewMedia(uri); 254 } 255 } 256 }; 257 258 private void checkDisplayRotation() { 259 // Set the display orientation if display rotation has changed. 260 // Sometimes this happens when the device is held upside 261 // down and camera app is opened. Rotation animation will 262 // take some time and the rotation value we have got may be 263 // wrong. Framework does not have a callback for this now. 264 if (CameraUtil.getDisplayRotation(mActivity) != mDisplayRotation) { 265 setDisplayOrientation(); 266 } 267 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) { 268 mHandler.postDelayed(new Runnable() { 269 @Override 270 public void run() { 271 checkDisplayRotation(); 272 } 273 }, 100); 274 } 275 } 276 277 /** 278 * This Handler is used to post message back onto the main thread of the 279 * application 280 */ 281 private static class MainHandler extends Handler { 282 private final WeakReference<PhotoModule> mModule; 283 284 public MainHandler(PhotoModule module) { 285 super(Looper.getMainLooper()); 286 mModule = new WeakReference<PhotoModule>(module); 287 } 288 289 @Override 290 public void handleMessage(Message msg) { 291 PhotoModule module = mModule.get(); 292 if (module == null) { 293 return; 294 } 295 switch (msg.what) { 296 case MSG_FIRST_TIME_INIT: { 297 module.initializeFirstTime(); 298 break; 299 } 300 301 case MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE: { 302 module.setCameraParametersWhenIdle(0); 303 break; 304 } 305 } 306 } 307 } 308 309 private void switchToGcamCapture() { 310 if (mActivity != null && mGcamModeIndex != 0) { 311 SettingsManager settingsManager = mActivity.getSettingsManager(); 312 settingsManager.set(SettingsManager.SETTING_CAMERA_HDR_PLUS, 313 SettingsManager.VALUE_ON); 314 315 // Disable the HDR+ button to prevent callbacks from being 316 // queued before the correct callback is attached to the button 317 // in the new module. The new module will set the enabled/disabled 318 // of this button when the module's preferred camera becomes available. 319 ButtonManager buttonManager = mActivity.getButtonManager(); 320 buttonManager.disableButton(ButtonManager.BUTTON_HDRPLUS); 321 322 // Do not post this to avoid this module switch getting interleaved with 323 // other button callbacks. 324 mActivity.onModeSelected(mGcamModeIndex); 325 } 326 } 327 328 /** 329 * Constructs a new photo module. 330 */ 331 public PhotoModule(AppController app) { 332 super(app); 333 mGcamModeIndex = app.getAndroidContext().getResources() 334 .getInteger(R.integer.camera_mode_gcam); 335 } 336 337 @Override 338 public void init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent) { 339 mActivity = activity; 340 // TODO: Need to look at the controller interface to see if we can get 341 // rid of passing in the activity directly. 342 mAppController = mActivity; 343 344 mUI = new PhotoUI(mActivity, this, mActivity.getModuleLayoutRoot()); 345 mActivity.setPreviewStatusListener(mUI); 346 347 SettingsManager settingsManager = mActivity.getSettingsManager(); 348 mCameraId = Integer.parseInt(settingsManager.get(SettingsManager.SETTING_CAMERA_ID)); 349 350 mContentResolver = mActivity.getContentResolver(); 351 352 // Surface texture is from camera screen nail and startPreview needs it. 353 // This must be done before startPreview. 354 mIsImageCaptureIntent = isImageCaptureIntent(); 355 356 mActivity.getCameraProvider().requestCamera(mCameraId); 357 358 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false); 359 mLocationManager = mActivity.getLocationManager(); 360 mSensorManager = (SensorManager) (mActivity.getSystemService(Context.SENSOR_SERVICE)); 361 } 362 363 @Override 364 public boolean isUsingBottomBar() { 365 return true; 366 } 367 368 private void initializeControlByIntent() { 369 if (mIsImageCaptureIntent) { 370 mActivity.getCameraAppUI().transitionToIntentCaptureLayout(); 371 setupCaptureParams(); 372 } 373 } 374 375 private void onPreviewStarted() { 376 mAppController.onPreviewStarted(); 377 setCameraState(IDLE); 378 startFaceDetection(); 379 locationFirstRun(); 380 } 381 382 @Override 383 public void onPreviewInitialDataReceived() { 384 } 385 386 // Prompt the user to pick to record location for the very first run of 387 // camera only 388 private void locationFirstRun() { 389 SettingsManager settingsManager = mActivity.getSettingsManager(); 390 391 if (settingsManager.isSet(SettingsManager.SETTING_RECORD_LOCATION)) { 392 return; 393 } 394 if (mActivity.isSecureCamera()) { 395 return; 396 } 397 // Check if the back camera exists 398 int backCameraId = mAppController.getCameraProvider().getFirstBackCameraId(); 399 if (backCameraId == -1) { 400 // If there is no back camera, do not show the prompt. 401 return; 402 } 403 mUI.showLocationDialog(); 404 } 405 406 @Override 407 public void onPreviewUIReady() { 408 startPreview(); 409 } 410 411 @Override 412 public void onPreviewUIDestroyed() { 413 if (mCameraDevice == null) { 414 return; 415 } 416 mCameraDevice.setPreviewTexture(null); 417 stopPreview(); 418 } 419 420 @Override 421 public void startPreCaptureAnimation() { 422 mAppController.startPreCaptureAnimation(); 423 } 424 425 private void onCameraOpened() { 426 openCameraCommon(); 427 initializeControlByIntent(); 428 } 429 430 private void switchCamera() { 431 if (mPaused) { 432 return; 433 } 434 SettingsManager settingsManager = mActivity.getSettingsManager(); 435 436 Log.i(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId); 437 closeCamera(); 438 mCameraId = mPendingSwitchCameraId; 439 settingsManager.set(SettingsManager.SETTING_CAMERA_ID, "" + mCameraId); 440 mActivity.getCameraProvider().requestCamera(mCameraId); 441 mUI.clearFaces(); 442 if (mFocusManager != null) { 443 mFocusManager.removeMessages(); 444 } 445 446 mMirror = isCameraFrontFacing(); 447 mFocusManager.setMirror(mMirror); 448 // Start switch camera animation. Post a message because 449 // onFrameAvailable from the old camera may already exist. 450 } 451 452 private final ButtonManager.ButtonCallback mCameraCallback = 453 new ButtonManager.ButtonCallback() { 454 @Override 455 public void onStateChanged(int state) { 456 // At the time this callback is fired, the camera id 457 // has be set to the desired camera. 458 459 if (mPaused || mAppController.getCameraProvider().waitingForCamera()) { 460 return; 461 } 462 // If switching to back camera, and HDR+ is still on, 463 // switch back to gcam, otherwise handle callback normally. 464 SettingsManager settingsManager = mActivity.getSettingsManager(); 465 if (settingsManager.isCameraBackFacing()) { 466 if (settingsManager.requestsReturnToHdrPlus()) { 467 switchToGcamCapture(); 468 return; 469 } 470 } 471 472 mPendingSwitchCameraId = state; 473 474 Log.d(TAG, "Start to switch camera. cameraId=" + state); 475 // We need to keep a preview frame for the animation before 476 // releasing the camera. This will trigger 477 // onPreviewTextureCopied. 478 // TODO: Need to animate the camera switch 479 switchCamera(); 480 } 481 }; 482 483 private final ButtonManager.ButtonCallback mHdrPlusCallback = 484 new ButtonManager.ButtonCallback() { 485 @Override 486 public void onStateChanged(int state) { 487 SettingsManager settingsManager = mActivity.getSettingsManager(); 488 if (GcamHelper.hasGcamCapture()) { 489 // Set the camera setting to default backfacing. 490 settingsManager.setDefault(SettingsManager.SETTING_CAMERA_ID); 491 switchToGcamCapture(); 492 } else { 493 if (settingsManager.isHdrOn()) { 494 settingsManager.set(SettingsManager.SETTING_SCENE_MODE, 495 CameraUtil.SCENE_MODE_HDR); 496 } else { 497 settingsManager.set(SettingsManager.SETTING_SCENE_MODE, 498 Parameters.SCENE_MODE_AUTO); 499 } 500 updateParametersSceneMode(); 501 mCameraDevice.setParameters(mParameters); 502 updateSceneMode(); 503 } 504 } 505 }; 506 507 private final View.OnClickListener mCancelCallback = new View.OnClickListener() { 508 @Override 509 public void onClick(View v) { 510 onCaptureCancelled(); 511 } 512 }; 513 514 private final View.OnClickListener mDoneCallback = new View.OnClickListener() { 515 @Override 516 public void onClick(View v) { 517 onCaptureDone(); 518 } 519 }; 520 521 private final View.OnClickListener mRetakeCallback = new View.OnClickListener() { 522 @Override 523 public void onClick(View v) { 524 mActivity.getCameraAppUI().transitionToIntentCaptureLayout(); 525 onCaptureRetake(); 526 } 527 }; 528 529 @Override 530 public void hardResetSettings(SettingsManager settingsManager) { 531 // PhotoModule should hard reset HDR+ to off. 532 settingsManager.set(SettingsManager.SETTING_CAMERA_HDR_PLUS, SettingsManager.VALUE_OFF); 533 } 534 535 @Override 536 public HardwareSpec getHardwareSpec() { 537 return (mParameters != null ? new HardwareSpecImpl(mParameters) : null); 538 } 539 540 @Override 541 public CameraAppUI.BottomBarUISpec getBottomBarSpec() { 542 CameraAppUI.BottomBarUISpec bottomBarSpec = new CameraAppUI.BottomBarUISpec(); 543 544 bottomBarSpec.enableCamera = true; 545 bottomBarSpec.cameraCallback = mCameraCallback; 546 bottomBarSpec.enableFlash = true; 547 bottomBarSpec.enableHdr = true; 548 bottomBarSpec.hdrCallback = mHdrPlusCallback; 549 bottomBarSpec.enableGridLines = true; 550 bottomBarSpec.enableExposureCompensation = true; 551 bottomBarSpec.exposureCompensationSetCallback = 552 new CameraAppUI.BottomBarUISpec.ExposureCompensationSetCallback() { 553 @Override 554 public void setExposure(int value) { 555 setExposureCompensation(value); 556 } 557 }; 558 559 if (isImageCaptureIntent()) { 560 bottomBarSpec.showCancel = true; 561 bottomBarSpec.cancelCallback = mCancelCallback; 562 bottomBarSpec.showDone = true; 563 bottomBarSpec.doneCallback = mDoneCallback; 564 bottomBarSpec.showRetake = true; 565 bottomBarSpec.retakeCallback = mRetakeCallback; 566 } 567 568 return bottomBarSpec; 569 } 570 571 // either open a new camera or switch cameras 572 private void openCameraCommon() { 573 mUI.onCameraOpened(mParameters); 574 if (mIsImageCaptureIntent) { 575 // Set hdr plus to default: off. 576 SettingsManager settingsManager = mActivity.getSettingsManager(); 577 settingsManager.setDefault(SettingsManager.SETTING_CAMERA_HDR_PLUS); 578 } 579 updateSceneMode(); 580 } 581 582 @Override 583 public void updatePreviewAspectRatio(float aspectRatio) { 584 mAppController.updatePreviewAspectRatio(aspectRatio); 585 } 586 587 private void resetExposureCompensation() { 588 SettingsManager settingsManager = mActivity.getSettingsManager(); 589 if (settingsManager == null) { 590 Log.e(TAG, "Settings manager is null!"); 591 return; 592 } 593 settingsManager.setDefault(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE); 594 } 595 596 // Snapshots can only be taken after this is called. It should be called 597 // once only. We could have done these things in onCreate() but we want to 598 // make preview screen appear as soon as possible. 599 private void initializeFirstTime() { 600 if (mFirstTimeInitialized || mPaused) { 601 return; 602 } 603 604 mUI.initializeFirstTime(); 605 606 // We set the listener only when both service and shutterbutton 607 // are initialized. 608 getServices().getMemoryManager().addListener(this); 609 610 mNamedImages = new NamedImages(); 611 612 mFirstTimeInitialized = true; 613 addIdleHandler(); 614 615 mActivity.updateStorageSpaceAndHint(null); 616 } 617 618 // If the activity is paused and resumed, this method will be called in 619 // onResume. 620 private void initializeSecondTime() { 621 getServices().getMemoryManager().addListener(this); 622 mNamedImages = new NamedImages(); 623 mUI.initializeSecondTime(mParameters); 624 } 625 626 private void addIdleHandler() { 627 MessageQueue queue = Looper.myQueue(); 628 queue.addIdleHandler(new MessageQueue.IdleHandler() { 629 @Override 630 public boolean queueIdle() { 631 Storage.ensureOSXCompatible(); 632 return false; 633 } 634 }); 635 } 636 637 @Override 638 public void startFaceDetection() { 639 if (mFaceDetectionStarted) { 640 return; 641 } 642 if (mParameters.getMaxNumDetectedFaces() > 0) { 643 mFaceDetectionStarted = true; 644 mUI.onStartFaceDetection(mDisplayOrientation, isCameraFrontFacing()); 645 mCameraDevice.setFaceDetectionCallback(mHandler, mUI); 646 mCameraDevice.startFaceDetection(); 647 SessionStatsCollector.instance().faceScanActive(true); 648 } 649 } 650 651 @Override 652 public void stopFaceDetection() { 653 if (!mFaceDetectionStarted) { 654 return; 655 } 656 if (mParameters.getMaxNumDetectedFaces() > 0) { 657 mFaceDetectionStarted = false; 658 mCameraDevice.setFaceDetectionCallback(null, null); 659 mCameraDevice.stopFaceDetection(); 660 mUI.clearFaces(); 661 SessionStatsCollector.instance().faceScanActive(false); 662 } 663 } 664 665 private final class ShutterCallback 666 implements CameraShutterCallback { 667 668 private final boolean mNeedsAnimation; 669 670 public ShutterCallback(boolean needsAnimation) { 671 mNeedsAnimation = needsAnimation; 672 } 673 674 @Override 675 public void onShutter(CameraProxy camera) { 676 mShutterCallbackTime = System.currentTimeMillis(); 677 mShutterLag = mShutterCallbackTime - mCaptureStartTime; 678 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms"); 679 if (mNeedsAnimation) { 680 mActivity.runOnUiThread(new Runnable() { 681 @Override 682 public void run() { 683 animateAfterShutter(); 684 } 685 }); 686 } 687 } 688 } 689 690 private final class PostViewPictureCallback 691 implements CameraPictureCallback { 692 @Override 693 public void onPictureTaken(byte[] data, CameraProxy camera) { 694 mPostViewPictureCallbackTime = System.currentTimeMillis(); 695 Log.v(TAG, "mShutterToPostViewCallbackTime = " 696 + (mPostViewPictureCallbackTime - mShutterCallbackTime) 697 + "ms"); 698 } 699 } 700 701 private final class RawPictureCallback 702 implements CameraPictureCallback { 703 @Override 704 public void onPictureTaken(byte[] rawData, CameraProxy camera) { 705 mRawPictureCallbackTime = System.currentTimeMillis(); 706 Log.v(TAG, "mShutterToRawCallbackTime = " 707 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms"); 708 } 709 } 710 711 private final class JpegPictureCallback 712 implements CameraPictureCallback { 713 Location mLocation; 714 715 public JpegPictureCallback(Location loc) { 716 mLocation = loc; 717 } 718 719 @Override 720 public void onPictureTaken(final byte[] jpegData, CameraProxy camera) { 721 mAppController.setShutterEnabled(true); 722 if (mPaused) { 723 return; 724 } 725 if (mIsImageCaptureIntent) { 726 stopPreview(); 727 } 728 if (mSceneMode == CameraUtil.SCENE_MODE_HDR) { 729 mUI.setSwipingEnabled(true); 730 } 731 732 mJpegPictureCallbackTime = System.currentTimeMillis(); 733 // If postview callback has arrived, the captured image is displayed 734 // in postview callback. If not, the captured image is displayed in 735 // raw picture callback. 736 if (mPostViewPictureCallbackTime != 0) { 737 mShutterToPictureDisplayedTime = 738 mPostViewPictureCallbackTime - mShutterCallbackTime; 739 mPictureDisplayedToJpegCallbackTime = 740 mJpegPictureCallbackTime - mPostViewPictureCallbackTime; 741 } else { 742 mShutterToPictureDisplayedTime = 743 mRawPictureCallbackTime - mShutterCallbackTime; 744 mPictureDisplayedToJpegCallbackTime = 745 mJpegPictureCallbackTime - mRawPictureCallbackTime; 746 } 747 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = " 748 + mPictureDisplayedToJpegCallbackTime + "ms"); 749 750 mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden. 751 if (!mIsImageCaptureIntent) { 752 setupPreview(); 753 } 754 755 ExifInterface exif = Exif.getExif(jpegData); 756 int orientation = Exif.getOrientation(exif); 757 758 float zoomValue = 0f; 759 if (mParameters.isZoomSupported()) { 760 int zoomIndex = mParameters.getZoom(); 761 List<Integer> zoomRatios = mParameters.getZoomRatios(); 762 if (zoomRatios != null && zoomIndex < zoomRatios.size()) { 763 zoomValue = 0.01f * zoomRatios.get(zoomIndex); 764 } 765 } 766 boolean hdrOn = CameraUtil.SCENE_MODE_HDR.equals(mSceneMode); 767 UsageStatistics.instance().photoCaptureDoneEvent( 768 eventprotos.NavigationChange.Mode.PHOTO_CAPTURE, 769 mNamedImages.mQueue.lastElement().title + ".jpg", exif, 770 isCameraFrontFacing(), hdrOn, zoomValue); 771 772 if (!mIsImageCaptureIntent) { 773 // Calculate the width and the height of the jpeg. 774 Size s = mParameters.getPictureSize(); 775 int width, height; 776 if ((mJpegRotation + orientation) % 180 == 0) { 777 width = s.width; 778 height = s.height; 779 } else { 780 width = s.height; 781 height = s.width; 782 } 783 NamedEntity name = mNamedImages.getNextNameEntity(); 784 String title = (name == null) ? null : name.title; 785 long date = (name == null) ? -1 : name.date; 786 787 // Handle debug mode outputs 788 if (mDebugUri != null) { 789 // If using a debug uri, save jpeg there. 790 saveToDebugUri(jpegData); 791 792 // Adjust the title of the debug image shown in mediastore. 793 if (title != null) { 794 title = DEBUG_IMAGE_PREFIX + title; 795 } 796 } 797 798 if (title == null) { 799 Log.e(TAG, "Unbalanced name/data pair"); 800 } else { 801 if (date == -1) { 802 date = mCaptureStartTime; 803 } 804 if (mHeading >= 0) { 805 // heading direction has been updated by the sensor. 806 ExifTag directionRefTag = exif.buildTag( 807 ExifInterface.TAG_GPS_IMG_DIRECTION_REF, 808 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION); 809 ExifTag directionTag = exif.buildTag( 810 ExifInterface.TAG_GPS_IMG_DIRECTION, 811 new Rational(mHeading, 1)); 812 exif.setTag(directionRefTag); 813 exif.setTag(directionTag); 814 } 815 getServices().getMediaSaver().addImage( 816 jpegData, title, date, mLocation, width, height, 817 orientation, exif, mOnMediaSavedListener, mContentResolver); 818 } 819 // Animate capture with real jpeg data instead of a preview 820 // frame. 821 mUI.animateCapture(jpegData, orientation, mMirror); 822 } else { 823 mJpegImageData = jpegData; 824 if (!mQuickCapture) { 825 mUI.showCapturedImageForReview(jpegData, orientation, mMirror); 826 } else { 827 onCaptureDone(); 828 } 829 } 830 831 // Check this in advance of each shot so we don't add to shutter 832 // latency. It's true that someone else could write to the SD card 833 // in the mean time and fill it, but that could have happened 834 // between the shutter press and saving the JPEG too. 835 mActivity.updateStorageSpaceAndHint(null); 836 837 long now = System.currentTimeMillis(); 838 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime; 839 Log.v(TAG, "mJpegCallbackFinishTime = " + mJpegCallbackFinishTime + "ms"); 840 mJpegPictureCallbackTime = 0; 841 } 842 } 843 844 private final class AutoFocusCallback implements CameraAFCallback { 845 @Override 846 public void onAutoFocus(boolean focused, CameraProxy camera) { 847 SessionStatsCollector.instance().autofocusResult(focused); 848 if (mPaused) { 849 return; 850 } 851 852 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime; 853 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms focused = "+focused); 854 setCameraState(IDLE); 855 mFocusManager.onAutoFocus(focused, false); 856 } 857 } 858 859 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 860 private final class AutoFocusMoveCallback 861 implements CameraAFMoveCallback { 862 @Override 863 public void onAutoFocusMoving( 864 boolean moving, CameraProxy camera) { 865 mFocusManager.onAutoFocusMoving(moving); 866 SessionStatsCollector.instance().autofocusMoving(moving); 867 } 868 } 869 870 /** 871 * This class is just a thread-safe queue for name,date holder objects. 872 */ 873 public static class NamedImages { 874 private final Vector<NamedEntity> mQueue; 875 876 public NamedImages() { 877 mQueue = new Vector<NamedEntity>(); 878 } 879 880 public void nameNewImage(long date) { 881 NamedEntity r = new NamedEntity(); 882 r.title = CameraUtil.createJpegName(date); 883 r.date = date; 884 mQueue.add(r); 885 } 886 887 public NamedEntity getNextNameEntity() { 888 synchronized (mQueue) { 889 if (!mQueue.isEmpty()) { 890 return mQueue.remove(0); 891 } 892 } 893 return null; 894 } 895 896 public static class NamedEntity { 897 public String title; 898 public long date; 899 } 900 } 901 902 private void setCameraState(int state) { 903 mCameraState = state; 904 switch (state) { 905 case PREVIEW_STOPPED: 906 case SNAPSHOT_IN_PROGRESS: 907 case SWITCHING_CAMERA: 908 // TODO: Tell app UI to disable swipe 909 break; 910 case PhotoController.IDLE: 911 // TODO: Tell app UI to enable swipe 912 break; 913 } 914 } 915 916 private void animateAfterShutter() { 917 // Only animate when in full screen capture mode 918 // i.e. If monkey/a user swipes to the gallery during picture taking, 919 // don't show animation 920 if (!mIsImageCaptureIntent) { 921 mUI.animateFlash(); 922 } 923 } 924 925 @Override 926 public boolean capture() { 927 // If we are already in the middle of taking a snapshot or the image 928 // save request is full then ignore. 929 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS 930 || mCameraState == SWITCHING_CAMERA || !mAppController.isShutterEnabled()) { 931 return false; 932 } 933 mCaptureStartTime = System.currentTimeMillis(); 934 mPostViewPictureCallbackTime = 0; 935 mJpegImageData = null; 936 937 final boolean animateBefore = (mSceneMode == CameraUtil.SCENE_MODE_HDR); 938 939 if (animateBefore) { 940 animateAfterShutter(); 941 } 942 943 // Set rotation and gps data. 944 int orientation; 945 946 // We need to be consistent with the framework orientation (i.e. the 947 // orientation of the UI.) when the auto-rotate screen setting is on. 948 if (mActivity.isAutoRotateScreen()) { 949 orientation = (360 - mDisplayRotation) % 360; 950 } else { 951 orientation = mOrientation; 952 } 953 CameraInfo info = mActivity.getCameraProvider().getCameraInfo()[mCameraId]; 954 mJpegRotation = CameraUtil.getJpegRotation(info, orientation); 955 mParameters.setRotation(mJpegRotation); 956 Location loc = mActivity.getLocationManager().getCurrentLocation(); 957 CameraUtil.setGpsParameters(mParameters, loc); 958 mCameraDevice.setParameters(mParameters); 959 960 // We don't want user to press the button again while taking a 961 // multi-second HDR photo. 962 mAppController.setShutterEnabled(false); 963 mCameraDevice.takePicture(mHandler, 964 new ShutterCallback(!animateBefore), 965 mRawPictureCallback, mPostViewPictureCallback, 966 new JpegPictureCallback(loc)); 967 968 mNamedImages.nameNewImage(mCaptureStartTime); 969 970 mFaceDetectionStarted = false; 971 setCameraState(SNAPSHOT_IN_PROGRESS); 972 return true; 973 } 974 975 @Override 976 public void setFocusParameters() { 977 setCameraParameters(UPDATE_PARAM_PREFERENCE); 978 } 979 980 private void updateSceneMode() { 981 // If scene mode is set, we cannot set flash mode, white balance, and 982 // focus mode, instead, we read it from driver 983 if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) { 984 overrideCameraSettings(mParameters.getFlashMode(), mParameters.getFocusMode()); 985 } 986 } 987 988 private void overrideCameraSettings(final String flashMode, final String focusMode) { 989 SettingsManager settingsManager = mActivity.getSettingsManager(); 990 settingsManager.set(SettingsManager.SETTING_FLASH_MODE, flashMode); 991 settingsManager.set(SettingsManager.SETTING_FOCUS_MODE, focusMode); 992 } 993 994 @Override 995 public void onOrientationChanged(int orientation) { 996 // We keep the last known orientation. So if the user first orient 997 // the camera then point the camera to floor or sky, we still have 998 // the correct orientation. 999 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) { 1000 return; 1001 } 1002 mOrientation = CameraUtil.roundOrientation(orientation, mOrientation); 1003 } 1004 1005 @Override 1006 public void onCameraAvailable(CameraProxy cameraProxy) { 1007 if (mPaused) { 1008 return; 1009 } 1010 mCameraDevice = cameraProxy; 1011 1012 initializeCapabilities(); 1013 1014 // Reset zoom value index. 1015 mZoomValue = 0; 1016 if (mFocusManager == null) { 1017 initializeFocusManager(); 1018 } 1019 mFocusManager.setParameters(mInitialParams); 1020 1021 // Do camera parameter dependent initialization. 1022 mParameters = mCameraDevice.getParameters(); 1023 setCameraParameters(UPDATE_PARAM_ALL); 1024 // Set a listener which updates camera parameters based 1025 // on changed settings. 1026 SettingsManager settingsManager = mActivity.getSettingsManager(); 1027 settingsManager.addListener(this); 1028 mCameraPreviewParamsReady = true; 1029 1030 startPreview(); 1031 1032 onCameraOpened(); 1033 } 1034 1035 @Override 1036 public void onCaptureCancelled() { 1037 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent()); 1038 mActivity.finish(); 1039 } 1040 1041 @Override 1042 public void onCaptureRetake() { 1043 if (mPaused) { 1044 return; 1045 } 1046 mUI.hidePostCaptureAlert(); 1047 setupPreview(); 1048 } 1049 1050 @Override 1051 public void onCaptureDone() { 1052 if (mPaused) { 1053 return; 1054 } 1055 1056 byte[] data = mJpegImageData; 1057 1058 if (mCropValue == null) { 1059 // First handle the no crop case -- just return the value. If the 1060 // caller specifies a "save uri" then write the data to its 1061 // stream. Otherwise, pass back a scaled down version of the bitmap 1062 // directly in the extras. 1063 if (mSaveUri != null) { 1064 OutputStream outputStream = null; 1065 try { 1066 outputStream = mContentResolver.openOutputStream(mSaveUri); 1067 outputStream.write(data); 1068 outputStream.close(); 1069 1070 mActivity.setResultEx(Activity.RESULT_OK); 1071 mActivity.finish(); 1072 } catch (IOException ex) { 1073 // ignore exception 1074 } finally { 1075 CameraUtil.closeSilently(outputStream); 1076 } 1077 } else { 1078 ExifInterface exif = Exif.getExif(data); 1079 int orientation = Exif.getOrientation(exif); 1080 Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024); 1081 bitmap = CameraUtil.rotate(bitmap, orientation); 1082 mActivity.setResultEx(Activity.RESULT_OK, 1083 new Intent("inline-data").putExtra("data", bitmap)); 1084 mActivity.finish(); 1085 } 1086 } else { 1087 // Save the image to a temp file and invoke the cropper 1088 Uri tempUri = null; 1089 FileOutputStream tempStream = null; 1090 try { 1091 File path = mActivity.getFileStreamPath(sTempCropFilename); 1092 path.delete(); 1093 tempStream = mActivity.openFileOutput(sTempCropFilename, 0); 1094 tempStream.write(data); 1095 tempStream.close(); 1096 tempUri = Uri.fromFile(path); 1097 } catch (FileNotFoundException ex) { 1098 mActivity.setResultEx(Activity.RESULT_CANCELED); 1099 mActivity.finish(); 1100 return; 1101 } catch (IOException ex) { 1102 mActivity.setResultEx(Activity.RESULT_CANCELED); 1103 mActivity.finish(); 1104 return; 1105 } finally { 1106 CameraUtil.closeSilently(tempStream); 1107 } 1108 1109 Bundle newExtras = new Bundle(); 1110 if (mCropValue.equals("circle")) { 1111 newExtras.putString("circleCrop", "true"); 1112 } 1113 if (mSaveUri != null) { 1114 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri); 1115 } else { 1116 newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true); 1117 } 1118 if (mActivity.isSecureCamera()) { 1119 newExtras.putBoolean(CameraUtil.KEY_SHOW_WHEN_LOCKED, true); 1120 } 1121 1122 // TODO: Share this constant. 1123 final String CROP_ACTION = "com.android.camera.action.CROP"; 1124 Intent cropIntent = new Intent(CROP_ACTION); 1125 1126 cropIntent.setData(tempUri); 1127 cropIntent.putExtras(newExtras); 1128 1129 mActivity.startActivityForResult(cropIntent, REQUEST_CROP); 1130 } 1131 } 1132 1133 @Override 1134 public void onShutterButtonFocus(boolean pressed) { 1135 // Do nothing. We don't support half-press to focus anymore. 1136 } 1137 1138 @Override 1139 public void onShutterButtonClick() { 1140 if (mPaused || (mCameraState == SWITCHING_CAMERA) 1141 || (mCameraState == PREVIEW_STOPPED)) { 1142 return; 1143 } 1144 1145 // Do not take the picture if there is not enough storage. 1146 if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) { 1147 Log.i(TAG, "Not enough space or storage not ready. remaining=" 1148 + mActivity.getStorageSpaceBytes()); 1149 return; 1150 } 1151 Log.d(TAG, "onShutterButtonClick: mCameraState=" + mCameraState); 1152 1153 if (mSceneMode == CameraUtil.SCENE_MODE_HDR) { 1154 mUI.setSwipingEnabled(false); 1155 } 1156 // If the user wants to do a snapshot while the previous one is still 1157 // in progress, remember the fact and do it after we finish the previous 1158 // one and re-start the preview. Snapshot in progress also includes the 1159 // state that autofocus is focusing and a picture will be taken when 1160 // focus callback arrives. 1161 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)) { 1162 if (!mIsImageCaptureIntent) { 1163 mSnapshotOnIdle = true; 1164 } 1165 return; 1166 } 1167 1168 mSnapshotOnIdle = false; 1169 mFocusManager.focusAndCapture(); 1170 } 1171 1172 private void onResumeTasks() { 1173 Log.v(TAG, "Executing onResumeTasks."); 1174 mActivity.getCameraProvider().requestCamera(mCameraId); 1175 1176 mJpegPictureCallbackTime = 0; 1177 mZoomValue = 0; 1178 1179 mOnResumeTime = SystemClock.uptimeMillis(); 1180 checkDisplayRotation(); 1181 1182 // If first time initialization is not finished, put it in the 1183 // message queue. 1184 if (!mFirstTimeInitialized) { 1185 mHandler.sendEmptyMessage(MSG_FIRST_TIME_INIT); 1186 } else { 1187 initializeSecondTime(); 1188 } 1189 1190 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); 1191 if (gsensor != null) { 1192 mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL); 1193 } 1194 1195 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); 1196 if (msensor != null) { 1197 mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL); 1198 } 1199 } 1200 1201 /** 1202 * @return Whether the currently active camera is front-facing. 1203 */ 1204 private boolean isCameraFrontFacing() { 1205 CameraInfo info = mAppController.getCameraProvider().getCameraInfo()[mCameraId]; 1206 return info.facing == CameraInfo.CAMERA_FACING_FRONT; 1207 } 1208 1209 /** 1210 * The focus manager is the first UI related element to get initialized, and 1211 * it requires the RenderOverlay, so initialize it here 1212 */ 1213 private void initializeFocusManager() { 1214 // Create FocusManager object. startPreview needs it. 1215 // if mFocusManager not null, reuse it 1216 // otherwise create a new instance 1217 if (mFocusManager != null) { 1218 mFocusManager.removeMessages(); 1219 } else { 1220 mMirror = isCameraFrontFacing(); 1221 String[] defaultFocusModes = mActivity.getResources().getStringArray( 1222 R.array.pref_camera_focusmode_default_array); 1223 mFocusManager = new FocusOverlayManager(mActivity.getSettingsManager(), 1224 defaultFocusModes, 1225 mInitialParams, this, mMirror, 1226 mActivity.getMainLooper(), mUI.getFocusUI()); 1227 } 1228 mAppController.addPreviewAreaSizeChangedListener(mFocusManager); 1229 } 1230 1231 @Override 1232 public void resume() { 1233 mPaused = false; 1234 if (mFocusManager != null) { 1235 // If camera is not open when resume is called, focus manager will 1236 // not 1237 // be initialized yet, in which case it will start listening to 1238 // preview area size change later in the initialization. 1239 mAppController.addPreviewAreaSizeChangedListener(mFocusManager); 1240 } 1241 1242 if (mUI.getPreviewAreaSizeChangedListener() != null) { 1243 mAppController.addPreviewAreaSizeChangedListener( 1244 mUI.getPreviewAreaSizeChangedListener()); 1245 } 1246 1247 // Add delay on resume from lock screen only, in order to to speed up 1248 // the onResume --> onPause --> onResume cycle from lock screen. 1249 // Don't do always because letting go of thread can cause delay. 1250 String action = mActivity.getIntent().getAction(); 1251 if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action) 1252 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)) { 1253 Log.v(TAG, "On resume, from lock screen."); 1254 // Note: onPauseAfterSuper() will delete this runnable, so we will 1255 // at most have 1 copy queued up. 1256 mHandler.postDelayed(new Runnable() { 1257 @Override 1258 public void run() { 1259 onResumeTasks(); 1260 } 1261 }, ON_RESUME_TASKS_DELAY_MSEC); 1262 } else { 1263 Log.v(TAG, "On resume."); 1264 onResumeTasks(); 1265 } 1266 SessionStatsCollector.instance().sessionActive(true); 1267 } 1268 1269 @Override 1270 public void pause() { 1271 mPaused = true; 1272 SessionStatsCollector.instance().sessionActive(false); 1273 1274 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); 1275 if (gsensor != null) { 1276 mSensorManager.unregisterListener(this, gsensor); 1277 } 1278 1279 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); 1280 if (msensor != null) { 1281 mSensorManager.unregisterListener(this, msensor); 1282 } 1283 1284 // Reset the focus first. Camera CTS does not guarantee that 1285 // cancelAutoFocus is allowed after preview stops. 1286 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) { 1287 mCameraDevice.cancelAutoFocus(); 1288 } 1289 1290 // If the camera has not been opened asynchronously yet, 1291 // and startPreview hasn't been called, then this is a no-op. 1292 // (e.g. onResume -> onPause -> onResume). 1293 stopPreview(); 1294 1295 mNamedImages = null; 1296 1297 // If we are in an image capture intent and has taken 1298 // a picture, we just clear it in onPause. 1299 mJpegImageData = null; 1300 1301 // Remove the messages and runnables in the queue. 1302 mHandler.removeCallbacksAndMessages(null); 1303 1304 closeCamera(); 1305 mActivity.enableKeepScreenOn(false); 1306 mUI.onPause(); 1307 1308 mPendingSwitchCameraId = -1; 1309 if (mFocusManager != null) { 1310 mFocusManager.removeMessages(); 1311 } 1312 getServices().getMemoryManager().removeListener(this); 1313 mAppController.removePreviewAreaSizeChangedListener(mFocusManager); 1314 if (mUI.getPreviewAreaSizeChangedListener() != null) { 1315 mAppController.removePreviewAreaSizeChangedListener( 1316 mUI.getPreviewAreaSizeChangedListener()); 1317 } 1318 1319 SettingsManager settingsManager = mActivity.getSettingsManager(); 1320 settingsManager.removeListener(this); 1321 } 1322 1323 @Override 1324 public void destroy() { 1325 // TODO: implement this. 1326 } 1327 1328 @Override 1329 public void onLayoutOrientationChanged(boolean isLandscape) { 1330 setDisplayOrientation(); 1331 } 1332 1333 @Override 1334 public void updateCameraOrientation() { 1335 if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) { 1336 setDisplayOrientation(); 1337 } 1338 } 1339 1340 private boolean canTakePicture() { 1341 return isCameraIdle() 1342 && (mActivity.getStorageSpaceBytes() > Storage.LOW_STORAGE_THRESHOLD_BYTES); 1343 } 1344 1345 @Override 1346 public void autoFocus() { 1347 Log.v(TAG,"Starting auto focus"); 1348 mFocusStartTime = System.currentTimeMillis(); 1349 mCameraDevice.autoFocus(mHandler, mAutoFocusCallback); 1350 SessionStatsCollector.instance().autofocusManualTrigger(); 1351 setCameraState(FOCUSING); 1352 } 1353 1354 @Override 1355 public void cancelAutoFocus() { 1356 mCameraDevice.cancelAutoFocus(); 1357 setCameraState(IDLE); 1358 setCameraParameters(UPDATE_PARAM_PREFERENCE); 1359 } 1360 1361 @Override 1362 public void onSingleTapUp(View view, int x, int y) { 1363 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized 1364 || mCameraState == SNAPSHOT_IN_PROGRESS 1365 || mCameraState == SWITCHING_CAMERA 1366 || mCameraState == PREVIEW_STOPPED) { 1367 return; 1368 } 1369 1370 // Check if metering area or focus area is supported. 1371 if (!mFocusAreaSupported && !mMeteringAreaSupported) { 1372 return; 1373 } 1374 mFocusManager.onSingleTapUp(x, y); 1375 } 1376 1377 @Override 1378 public boolean onBackPressed() { 1379 return mUI.onBackPressed(); 1380 } 1381 1382 @Override 1383 public boolean onKeyDown(int keyCode, KeyEvent event) { 1384 switch (keyCode) { 1385 case KeyEvent.KEYCODE_VOLUME_UP: 1386 case KeyEvent.KEYCODE_VOLUME_DOWN: 1387 case KeyEvent.KEYCODE_FOCUS: 1388 if (/* TODO: mActivity.isInCameraApp() && */mFirstTimeInitialized) { 1389 if (event.getRepeatCount() == 0) { 1390 onShutterButtonFocus(true); 1391 } 1392 return true; 1393 } 1394 return false; 1395 case KeyEvent.KEYCODE_CAMERA: 1396 if (mFirstTimeInitialized && event.getRepeatCount() == 0) { 1397 onShutterButtonClick(); 1398 } 1399 return true; 1400 case KeyEvent.KEYCODE_DPAD_CENTER: 1401 // If we get a dpad center event without any focused view, move 1402 // the focus to the shutter button and press it. 1403 if (mFirstTimeInitialized && event.getRepeatCount() == 0) { 1404 // Start auto-focus immediately to reduce shutter lag. After 1405 // the shutter button gets the focus, onShutterButtonFocus() 1406 // will be called again but it is fine. 1407 onShutterButtonFocus(true); 1408 } 1409 return true; 1410 } 1411 return false; 1412 } 1413 1414 @Override 1415 public boolean onKeyUp(int keyCode, KeyEvent event) { 1416 switch (keyCode) { 1417 case KeyEvent.KEYCODE_VOLUME_UP: 1418 case KeyEvent.KEYCODE_VOLUME_DOWN: 1419 if (/* mActivity.isInCameraApp() && */mFirstTimeInitialized) { 1420 onShutterButtonClick(); 1421 return true; 1422 } 1423 return false; 1424 case KeyEvent.KEYCODE_FOCUS: 1425 if (mFirstTimeInitialized) { 1426 onShutterButtonFocus(false); 1427 } 1428 return true; 1429 } 1430 return false; 1431 } 1432 1433 private void closeCamera() { 1434 if (mCameraDevice != null) { 1435 stopFaceDetection(); 1436 mCameraDevice.setZoomChangeListener(null); 1437 mCameraDevice.setFaceDetectionCallback(null, null); 1438 mCameraDevice.setErrorCallback(null, null); 1439 1440 mFaceDetectionStarted = false; 1441 mActivity.getCameraProvider().releaseCamera(mCameraDevice.getCameraId()); 1442 mCameraDevice = null; 1443 setCameraState(PREVIEW_STOPPED); 1444 mFocusManager.onCameraReleased(); 1445 } 1446 } 1447 1448 private void setDisplayOrientation() { 1449 mDisplayRotation = CameraUtil.getDisplayRotation(mActivity); 1450 mDisplayOrientation = CameraUtil.getDisplayOrientation(mDisplayRotation, mCameraId); 1451 mCameraDisplayOrientation = mDisplayOrientation; 1452 mUI.setDisplayOrientation(mDisplayOrientation); 1453 if (mFocusManager != null) { 1454 mFocusManager.setDisplayOrientation(mDisplayOrientation); 1455 } 1456 // Change the camera display orientation 1457 if (mCameraDevice != null) { 1458 mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation); 1459 } 1460 } 1461 1462 /** Only called by UI thread. */ 1463 private void setupPreview() { 1464 mFocusManager.resetTouchFocus(); 1465 startPreview(); 1466 } 1467 1468 /** 1469 * Returns whether we can/should start the preview or not. 1470 */ 1471 private boolean checkPreviewPreconditions() { 1472 if (mPaused) { 1473 return false; 1474 } 1475 1476 if (mCameraDevice == null) { 1477 Log.w(TAG, "startPreview: camera device not ready yet."); 1478 return false; 1479 } 1480 1481 SurfaceTexture st = mActivity.getCameraAppUI().getSurfaceTexture(); 1482 if (st == null) { 1483 Log.w(TAG, "startPreview: surfaceTexture is not ready."); 1484 return false; 1485 } 1486 1487 if (!mCameraPreviewParamsReady) { 1488 Log.w(TAG, "startPreview: parameters for preview is not ready."); 1489 return false; 1490 } 1491 return true; 1492 } 1493 1494 /** 1495 * The start/stop preview should only run on the UI thread. 1496 */ 1497 private void startPreview() { 1498 if (!checkPreviewPreconditions()) { 1499 return; 1500 } 1501 1502 mCameraDevice.setErrorCallback(mHandler, mErrorCallback); 1503 // ICS camera frameworks has a bug. Face detection state is not cleared 1504 // after taking a picture. Stop the preview to work around it. The bug 1505 // was fixed in JB. 1506 // TODO: Remove all of this for 'E'. 1507 if (!NO_STOP_PREVIEW_ICS_WORKAROUND) { 1508 if (mCameraState != PREVIEW_STOPPED) { 1509 stopPreview(); 1510 } 1511 } 1512 1513 setDisplayOrientation(); 1514 1515 if (!mSnapshotOnIdle) { 1516 // If the focus mode is continuous autofocus, call cancelAutoFocus 1517 // to resume it because it may have been paused by autoFocus call. 1518 String focusMode = mFocusManager.getFocusMode(); 1519 if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(focusMode)) { 1520 mCameraDevice.cancelAutoFocus(); 1521 } 1522 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB. 1523 } 1524 setCameraParameters(UPDATE_PARAM_ALL); 1525 // Let UI set its expected aspect ratio 1526 mCameraDevice.setPreviewTexture(mActivity.getCameraAppUI().getSurfaceTexture()); 1527 1528 Log.i(TAG, "startPreview"); 1529 mCameraDevice.startPreview(); 1530 1531 mFocusManager.onPreviewStarted(); 1532 onPreviewStarted(); 1533 SessionStatsCollector.instance().previewActive(true); 1534 if (mSnapshotOnIdle) { 1535 mHandler.post(mDoSnapRunnable); 1536 } 1537 } 1538 1539 @Override 1540 public void stopPreview() { 1541 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) { 1542 Log.i(TAG, "stopPreview"); 1543 mCameraDevice.stopPreview(); 1544 mFaceDetectionStarted = false; 1545 } 1546 setCameraState(PREVIEW_STOPPED); 1547 if (mFocusManager != null) { 1548 mFocusManager.onPreviewStopped(); 1549 } 1550 SessionStatsCollector.instance().previewActive(false); 1551 } 1552 1553 @Override 1554 public void onSettingChanged(SettingsManager settingsManager, int id) { 1555 switch (id) { 1556 case SettingsManager.SETTING_FLASH_MODE: { 1557 updateParametersFlashMode(); 1558 break; 1559 } 1560 default: { 1561 // Do nothing. 1562 } 1563 } 1564 1565 if (mCameraDevice != null) { 1566 mCameraDevice.setParameters(mParameters); 1567 } 1568 } 1569 1570 private void updateCameraParametersInitialize() { 1571 // Reset preview frame rate to the maximum because it may be lowered by 1572 // video camera application. 1573 int[] fpsRange = CameraUtil.getPhotoPreviewFpsRange(mParameters); 1574 if (fpsRange != null && fpsRange.length > 0) { 1575 mParameters.setPreviewFpsRange( 1576 fpsRange[Parameters.PREVIEW_FPS_MIN_INDEX], 1577 fpsRange[Parameters.PREVIEW_FPS_MAX_INDEX]); 1578 } 1579 1580 mParameters.set(CameraUtil.RECORDING_HINT, CameraUtil.FALSE); 1581 1582 // Disable video stabilization. Convenience methods not available in API 1583 // level <= 14 1584 String vstabSupported = mParameters.get("video-stabilization-supported"); 1585 if ("true".equals(vstabSupported)) { 1586 mParameters.set("video-stabilization", "false"); 1587 } 1588 } 1589 1590 private void updateCameraParametersZoom() { 1591 // Set zoom. 1592 if (mParameters.isZoomSupported()) { 1593 mParameters.setZoom(mZoomValue); 1594 } 1595 } 1596 1597 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 1598 private void setAutoExposureLockIfSupported() { 1599 if (mAeLockSupported) { 1600 mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock()); 1601 } 1602 } 1603 1604 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 1605 private void setAutoWhiteBalanceLockIfSupported() { 1606 if (mAwbLockSupported) { 1607 mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock()); 1608 } 1609 } 1610 1611 private void setFocusAreasIfSupported() { 1612 if (mFocusAreaSupported) { 1613 mParameters.setFocusAreas(mFocusManager.getFocusAreas()); 1614 } 1615 } 1616 1617 private void setMeteringAreasIfSupported() { 1618 if (mMeteringAreaSupported) { 1619 mParameters.setMeteringAreas(mFocusManager.getMeteringAreas()); 1620 } 1621 } 1622 1623 private void updateCameraParametersPreference() { 1624 setAutoExposureLockIfSupported(); 1625 setAutoWhiteBalanceLockIfSupported(); 1626 setFocusAreasIfSupported(); 1627 setMeteringAreasIfSupported(); 1628 1629 // Initialize focus mode. 1630 mFocusManager.overrideFocusMode(null); 1631 mParameters.setFocusMode(mFocusManager.getFocusMode()); 1632 SessionStatsCollector.instance().autofocusActive( 1633 mFocusManager.getFocusMode() == CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE); 1634 1635 // Set picture size. 1636 updateParametersPictureSize(); 1637 1638 // Set JPEG quality. 1639 updateParametersPictureQuality(); 1640 1641 // For the following settings, we need to check if the settings are 1642 // still supported by latest driver, if not, ignore the settings. 1643 1644 // Set exposure compensation 1645 updateParametersExposureCompensation(); 1646 1647 // Set the scene mode: also sets flash and white balance. 1648 updateParametersSceneMode(); 1649 1650 if (mContinuousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) { 1651 updateAutoFocusMoveCallback(); 1652 } 1653 } 1654 1655 private void updateParametersPictureSize() { 1656 SettingsManager settingsManager = mActivity.getSettingsManager(); 1657 String pictureSize = settingsManager 1658 .get(isCameraFrontFacing() ? SettingsManager.SETTING_PICTURE_SIZE_FRONT 1659 : SettingsManager.SETTING_PICTURE_SIZE_BACK); 1660 1661 List<Size> supported = mParameters.getSupportedPictureSizes(); 1662 SettingsUtil.setCameraPictureSize(pictureSize, supported, mParameters, 1663 mCameraDevice.getCameraId()); 1664 Size size = mParameters.getPictureSize(); 1665 1666 // Set a preview size that is closest to the viewfinder height and has 1667 // the right aspect ratio. 1668 List<Size> sizes = mParameters.getSupportedPreviewSizes(); 1669 Size optimalSize = CameraUtil.getOptimalPreviewSize(mActivity, sizes, 1670 (double) size.width / size.height); 1671 Size original = mParameters.getPreviewSize(); 1672 if (!original.equals(optimalSize)) { 1673 mParameters.setPreviewSize(optimalSize.width, optimalSize.height); 1674 1675 // Zoom related settings will be changed for different preview 1676 // sizes, so set and read the parameters to get latest values 1677 if (mHandler.getLooper() == Looper.myLooper()) { 1678 // On UI thread only, not when camera starts up 1679 setupPreview(); 1680 } else { 1681 mCameraDevice.setParameters(mParameters); 1682 } 1683 mParameters = mCameraDevice.getParameters(); 1684 } 1685 1686 if (optimalSize.width != 0 && optimalSize.height != 0) { 1687 mUI.updatePreviewAspectRatio((float) optimalSize.width 1688 / (float) optimalSize.height); 1689 } 1690 Log.i(TAG, "Preview size is " + optimalSize.width + "x" + optimalSize.height); 1691 } 1692 1693 private void updateParametersPictureQuality() { 1694 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId, 1695 CameraProfile.QUALITY_HIGH); 1696 mParameters.setJpegQuality(jpegQuality); 1697 } 1698 1699 private void updateParametersExposureCompensation() { 1700 SettingsManager settingsManager = mActivity.getSettingsManager(); 1701 if (settingsManager.getBoolean(SettingsManager.SETTING_EXPOSURE_COMPENSATION_ENABLED)) { 1702 int value = Integer.parseInt(settingsManager.get(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE)); 1703 int max = mParameters.getMaxExposureCompensation(); 1704 int min = mParameters.getMinExposureCompensation(); 1705 if (value >= min && value <= max) { 1706 mParameters.setExposureCompensation(value); 1707 } else { 1708 Log.w(TAG, "invalid exposure range: " + value); 1709 } 1710 } else { 1711 // If exposure compensation is not enabled, reset the exposure compensation value. 1712 setExposureCompensation(0); 1713 } 1714 1715 } 1716 1717 private void updateParametersSceneMode() { 1718 SettingsManager settingsManager = mActivity.getSettingsManager(); 1719 1720 mSceneMode = settingsManager.get(SettingsManager.SETTING_SCENE_MODE); 1721 if (CameraUtil.isSupported(mSceneMode, mParameters.getSupportedSceneModes())) { 1722 if (!mParameters.getSceneMode().equals(mSceneMode)) { 1723 mParameters.setSceneMode(mSceneMode); 1724 1725 // Setting scene mode will change the settings of flash mode, 1726 // white balance, and focus mode. Here we read back the 1727 // parameters, so we can know those settings. 1728 mCameraDevice.setParameters(mParameters); 1729 mParameters = mCameraDevice.getParameters(); 1730 } 1731 } else { 1732 mSceneMode = mParameters.getSceneMode(); 1733 if (mSceneMode == null) { 1734 mSceneMode = Parameters.SCENE_MODE_AUTO; 1735 } 1736 } 1737 1738 if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) { 1739 // Set flash mode. 1740 updateParametersFlashMode(); 1741 1742 // Set focus mode. 1743 mFocusManager.overrideFocusMode(null); 1744 mParameters.setFocusMode(mFocusManager.getFocusMode()); 1745 } else { 1746 mFocusManager.overrideFocusMode(mParameters.getFocusMode()); 1747 } 1748 } 1749 1750 private void updateParametersFlashMode() { 1751 SettingsManager settingsManager = mActivity.getSettingsManager(); 1752 1753 String flashMode = settingsManager.get(SettingsManager.SETTING_FLASH_MODE); 1754 List<String> supportedFlash = mParameters.getSupportedFlashModes(); 1755 if (CameraUtil.isSupported(flashMode, supportedFlash)) { 1756 mParameters.setFlashMode(flashMode); 1757 } 1758 } 1759 1760 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 1761 private void updateAutoFocusMoveCallback() { 1762 if (mParameters.getFocusMode().equals(CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE)) { 1763 mCameraDevice.setAutoFocusMoveCallback(mHandler, 1764 (CameraAFMoveCallback) mAutoFocusMoveCallback); 1765 } else { 1766 mCameraDevice.setAutoFocusMoveCallback(null, null); 1767 } 1768 } 1769 1770 /** 1771 * Sets the exposure compensation to the given value and also updates settings. 1772 * 1773 * @param value exposure compensation value to be set 1774 */ 1775 public void setExposureCompensation(int value) { 1776 int max = mParameters.getMaxExposureCompensation(); 1777 int min = mParameters.getMinExposureCompensation(); 1778 if (value >= min && value <= max) { 1779 mParameters.setExposureCompensation(value); 1780 SettingsManager settingsManager = mActivity.getSettingsManager(); 1781 settingsManager.set(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE, Integer.toString(value)); 1782 } else { 1783 Log.w(TAG, "invalid exposure range: " + value); 1784 } 1785 } 1786 1787 // We separate the parameters into several subsets, so we can update only 1788 // the subsets actually need updating. The PREFERENCE set needs extra 1789 // locking because the preference can be changed from GLThread as well. 1790 private void setCameraParameters(int updateSet) { 1791 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) { 1792 updateCameraParametersInitialize(); 1793 } 1794 1795 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) { 1796 updateCameraParametersZoom(); 1797 } 1798 1799 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) { 1800 updateCameraParametersPreference(); 1801 } 1802 1803 mCameraDevice.setParameters(mParameters); 1804 } 1805 1806 // If the Camera is idle, update the parameters immediately, otherwise 1807 // accumulate them in mUpdateSet and update later. 1808 private void setCameraParametersWhenIdle(int additionalUpdateSet) { 1809 mUpdateSet |= additionalUpdateSet; 1810 if (mCameraDevice == null) { 1811 // We will update all the parameters when we open the device, so 1812 // we don't need to do anything now. 1813 mUpdateSet = 0; 1814 return; 1815 } else if (isCameraIdle()) { 1816 setCameraParameters(mUpdateSet); 1817 updateSceneMode(); 1818 mUpdateSet = 0; 1819 } else { 1820 if (!mHandler.hasMessages(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE)) { 1821 mHandler.sendEmptyMessageDelayed(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000); 1822 } 1823 } 1824 } 1825 1826 @Override 1827 public boolean isCameraIdle() { 1828 return (mCameraState == IDLE) || 1829 (mCameraState == PREVIEW_STOPPED) || 1830 ((mFocusManager != null) && mFocusManager.isFocusCompleted() 1831 && (mCameraState != SWITCHING_CAMERA)); 1832 } 1833 1834 @Override 1835 public boolean isImageCaptureIntent() { 1836 String action = mActivity.getIntent().getAction(); 1837 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action) 1838 || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action)); 1839 } 1840 1841 private void setupCaptureParams() { 1842 Bundle myExtras = mActivity.getIntent().getExtras(); 1843 if (myExtras != null) { 1844 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT); 1845 mCropValue = myExtras.getString("crop"); 1846 } 1847 } 1848 1849 private void initializeCapabilities() { 1850 mInitialParams = mCameraDevice.getParameters(); 1851 mFocusAreaSupported = CameraUtil.isFocusAreaSupported(mInitialParams); 1852 mMeteringAreaSupported = CameraUtil.isMeteringAreaSupported(mInitialParams); 1853 mAeLockSupported = CameraUtil.isAutoExposureLockSupported(mInitialParams); 1854 mAwbLockSupported = CameraUtil.isAutoWhiteBalanceLockSupported(mInitialParams); 1855 mContinuousFocusSupported = mInitialParams.getSupportedFocusModes().contains( 1856 CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE); 1857 } 1858 1859 // TODO: Remove this 1860 @Override 1861 public int onZoomChanged(int index) { 1862 // Not useful to change zoom value when the activity is paused. 1863 if (mPaused) { 1864 return index; 1865 } 1866 mZoomValue = index; 1867 if (mParameters == null || mCameraDevice == null) { 1868 return index; 1869 } 1870 // Set zoom parameters asynchronously 1871 mParameters.setZoom(mZoomValue); 1872 mCameraDevice.setParameters(mParameters); 1873 Parameters p = mCameraDevice.getParameters(); 1874 if (p != null) { 1875 return p.getZoom(); 1876 } 1877 return index; 1878 } 1879 1880 @Override 1881 public int getCameraState() { 1882 return mCameraState; 1883 } 1884 1885 @Override 1886 public void onMemoryStateChanged(int state) { 1887 mAppController.setShutterEnabled(state == MemoryManager.STATE_OK); 1888 } 1889 1890 @Override 1891 public void onLowMemory() { 1892 // Not much we can do in the photo module. 1893 } 1894 1895 @Override 1896 public void onAccuracyChanged(Sensor sensor, int accuracy) { 1897 } 1898 1899 @Override 1900 public void onSensorChanged(SensorEvent event) { 1901 int type = event.sensor.getType(); 1902 float[] data; 1903 if (type == Sensor.TYPE_ACCELEROMETER) { 1904 data = mGData; 1905 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) { 1906 data = mMData; 1907 } else { 1908 // we should not be here. 1909 return; 1910 } 1911 for (int i = 0; i < 3; i++) { 1912 data[i] = event.values[i]; 1913 } 1914 float[] orientation = new float[3]; 1915 SensorManager.getRotationMatrix(mR, null, mGData, mMData); 1916 SensorManager.getOrientation(mR, orientation); 1917 mHeading = (int) (orientation[0] * 180f / Math.PI) % 360; 1918 if (mHeading < 0) { 1919 mHeading += 360; 1920 } 1921 } 1922 1923 // For debugging only. 1924 public void setDebugUri(Uri uri) { 1925 mDebugUri = uri; 1926 } 1927 1928 // For debugging only. 1929 private void saveToDebugUri(byte[] data) { 1930 if (mDebugUri != null) { 1931 OutputStream outputStream = null; 1932 try { 1933 outputStream = mContentResolver.openOutputStream(mDebugUri); 1934 outputStream.write(data); 1935 outputStream.close(); 1936 } catch (IOException e) { 1937 Log.e(TAG, "Exception while writing debug jpeg file", e); 1938 } finally { 1939 CameraUtil.closeSilently(outputStream); 1940 } 1941 } 1942 } 1943 1944 /** 1945 * Depending on the device, this returns whether we should avoid using the 1946 * ICS-only workaround to call stopPreview before startPreview 1947 * <p> 1948 * The proper solution is to remove the stopPreview call completely, but as 1949 * we have only limited time for testing left, let's be careful and target 1950 * specific devices only. 1951 * <p> 1952 * Context: http://b/13966525 1953 */ 1954 private static boolean shouldNotCallStopPreviewAfterTakingPicture() { 1955 // The M8 is the only known device with a really long stopPreview 1956 // duration. 1957 return Build.MANUFACTURER.toLowerCase().contains("htc") && 1958 Build.DEVICE.toLowerCase().contains("m8"); 1959 } 1960} 1961