PhotoModule.java revision c6ef756c0eb736c3e43cff13d593f5ad71d2cb7b
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(); 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 int zoomIndex = mParameters.getZoom(); 758 float zoomValue = 0.01f * mParameters.getZoomRatios().get(zoomIndex); 759 760 boolean hdrOn = CameraUtil.SCENE_MODE_HDR.equals(mSceneMode); 761 UsageStatistics.instance().photoCaptureDoneEvent( 762 eventprotos.NavigationChange.Mode.PHOTO_CAPTURE, 763 mNamedImages.mQueue.lastElement().title + ".jpg", exif, 764 isCameraFrontFacing(), hdrOn, zoomValue); 765 766 if (!mIsImageCaptureIntent) { 767 // Calculate the width and the height of the jpeg. 768 Size s = mParameters.getPictureSize(); 769 int width, height; 770 if ((mJpegRotation + orientation) % 180 == 0) { 771 width = s.width; 772 height = s.height; 773 } else { 774 width = s.height; 775 height = s.width; 776 } 777 NamedEntity name = mNamedImages.getNextNameEntity(); 778 String title = (name == null) ? null : name.title; 779 long date = (name == null) ? -1 : name.date; 780 781 // Handle debug mode outputs 782 if (mDebugUri != null) { 783 // If using a debug uri, save jpeg there. 784 saveToDebugUri(jpegData); 785 786 // Adjust the title of the debug image shown in mediastore. 787 if (title != null) { 788 title = DEBUG_IMAGE_PREFIX + title; 789 } 790 } 791 792 if (title == null) { 793 Log.e(TAG, "Unbalanced name/data pair"); 794 } else { 795 if (date == -1) { 796 date = mCaptureStartTime; 797 } 798 if (mHeading >= 0) { 799 // heading direction has been updated by the sensor. 800 ExifTag directionRefTag = exif.buildTag( 801 ExifInterface.TAG_GPS_IMG_DIRECTION_REF, 802 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION); 803 ExifTag directionTag = exif.buildTag( 804 ExifInterface.TAG_GPS_IMG_DIRECTION, 805 new Rational(mHeading, 1)); 806 exif.setTag(directionRefTag); 807 exif.setTag(directionTag); 808 } 809 getServices().getMediaSaver().addImage( 810 jpegData, title, date, mLocation, width, height, 811 orientation, exif, mOnMediaSavedListener, mContentResolver); 812 } 813 // Animate capture with real jpeg data instead of a preview 814 // frame. 815 mUI.animateCapture(jpegData, orientation, mMirror); 816 } else { 817 mJpegImageData = jpegData; 818 if (!mQuickCapture) { 819 mUI.showCapturedImageForReview(jpegData, orientation, mMirror); 820 } else { 821 onCaptureDone(); 822 } 823 } 824 825 // Check this in advance of each shot so we don't add to shutter 826 // latency. It's true that someone else could write to the SD card 827 // in the mean time and fill it, but that could have happened 828 // between the shutter press and saving the JPEG too. 829 mActivity.updateStorageSpaceAndHint(); 830 831 long now = System.currentTimeMillis(); 832 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime; 833 Log.v(TAG, "mJpegCallbackFinishTime = " + mJpegCallbackFinishTime + "ms"); 834 mJpegPictureCallbackTime = 0; 835 } 836 } 837 838 private final class AutoFocusCallback implements CameraAFCallback { 839 @Override 840 public void onAutoFocus(boolean focused, CameraProxy camera) { 841 SessionStatsCollector.instance().autofocusResult(focused); 842 if (mPaused) { 843 return; 844 } 845 846 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime; 847 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms focused = "+focused); 848 setCameraState(IDLE); 849 mFocusManager.onAutoFocus(focused, false); 850 } 851 } 852 853 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 854 private final class AutoFocusMoveCallback 855 implements CameraAFMoveCallback { 856 @Override 857 public void onAutoFocusMoving( 858 boolean moving, CameraProxy camera) { 859 mFocusManager.onAutoFocusMoving(moving); 860 SessionStatsCollector.instance().autofocusMoving(moving); 861 } 862 } 863 864 /** 865 * This class is just a thread-safe queue for name,date holder objects. 866 */ 867 public static class NamedImages { 868 private final Vector<NamedEntity> mQueue; 869 870 public NamedImages() { 871 mQueue = new Vector<NamedEntity>(); 872 } 873 874 public void nameNewImage(long date) { 875 NamedEntity r = new NamedEntity(); 876 r.title = CameraUtil.createJpegName(date); 877 r.date = date; 878 mQueue.add(r); 879 } 880 881 public NamedEntity getNextNameEntity() { 882 synchronized (mQueue) { 883 if (!mQueue.isEmpty()) { 884 return mQueue.remove(0); 885 } 886 } 887 return null; 888 } 889 890 public static class NamedEntity { 891 public String title; 892 public long date; 893 } 894 } 895 896 private void setCameraState(int state) { 897 mCameraState = state; 898 switch (state) { 899 case PREVIEW_STOPPED: 900 case SNAPSHOT_IN_PROGRESS: 901 case SWITCHING_CAMERA: 902 // TODO: Tell app UI to disable swipe 903 break; 904 case PhotoController.IDLE: 905 // TODO: Tell app UI to enable swipe 906 break; 907 } 908 } 909 910 private void animateAfterShutter() { 911 // Only animate when in full screen capture mode 912 // i.e. If monkey/a user swipes to the gallery during picture taking, 913 // don't show animation 914 if (!mIsImageCaptureIntent) { 915 mUI.animateFlash(); 916 } 917 } 918 919 @Override 920 public boolean capture() { 921 // If we are already in the middle of taking a snapshot or the image 922 // save request is full then ignore. 923 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS 924 || mCameraState == SWITCHING_CAMERA || !mAppController.isShutterEnabled()) { 925 return false; 926 } 927 mCaptureStartTime = System.currentTimeMillis(); 928 mPostViewPictureCallbackTime = 0; 929 mJpegImageData = null; 930 931 final boolean animateBefore = (mSceneMode == CameraUtil.SCENE_MODE_HDR); 932 933 if (animateBefore) { 934 animateAfterShutter(); 935 } 936 937 // Set rotation and gps data. 938 int orientation; 939 940 // We need to be consistent with the framework orientation (i.e. the 941 // orientation of the UI.) when the auto-rotate screen setting is on. 942 if (mActivity.isAutoRotateScreen()) { 943 orientation = (360 - mDisplayRotation) % 360; 944 } else { 945 orientation = mOrientation; 946 } 947 CameraInfo info = mActivity.getCameraProvider().getCameraInfo()[mCameraId]; 948 mJpegRotation = CameraUtil.getJpegRotation(info, orientation); 949 mParameters.setRotation(mJpegRotation); 950 Location loc = mActivity.getLocationManager().getCurrentLocation(); 951 CameraUtil.setGpsParameters(mParameters, loc); 952 mCameraDevice.setParameters(mParameters); 953 954 // We don't want user to press the button again while taking a 955 // multi-second HDR photo. 956 mAppController.setShutterEnabled(false); 957 mCameraDevice.takePicture(mHandler, 958 new ShutterCallback(!animateBefore), 959 mRawPictureCallback, mPostViewPictureCallback, 960 new JpegPictureCallback(loc)); 961 962 mNamedImages.nameNewImage(mCaptureStartTime); 963 964 mFaceDetectionStarted = false; 965 setCameraState(SNAPSHOT_IN_PROGRESS); 966 return true; 967 } 968 969 @Override 970 public void setFocusParameters() { 971 setCameraParameters(UPDATE_PARAM_PREFERENCE); 972 } 973 974 private void updateSceneMode() { 975 // If scene mode is set, we cannot set flash mode, white balance, and 976 // focus mode, instead, we read it from driver 977 if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) { 978 overrideCameraSettings(mParameters.getFlashMode(), mParameters.getFocusMode()); 979 } 980 } 981 982 private void overrideCameraSettings(final String flashMode, final String focusMode) { 983 SettingsManager settingsManager = mActivity.getSettingsManager(); 984 settingsManager.set(SettingsManager.SETTING_FLASH_MODE, flashMode); 985 settingsManager.set(SettingsManager.SETTING_FOCUS_MODE, focusMode); 986 } 987 988 @Override 989 public void onOrientationChanged(int orientation) { 990 // We keep the last known orientation. So if the user first orient 991 // the camera then point the camera to floor or sky, we still have 992 // the correct orientation. 993 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) { 994 return; 995 } 996 mOrientation = CameraUtil.roundOrientation(orientation, mOrientation); 997 } 998 999 @Override 1000 public void onCameraAvailable(CameraProxy cameraProxy) { 1001 if (mPaused) { 1002 return; 1003 } 1004 mCameraDevice = cameraProxy; 1005 1006 initializeCapabilities(); 1007 1008 // Reset zoom value index. 1009 mZoomValue = 0; 1010 if (mFocusManager == null) { 1011 initializeFocusManager(); 1012 } 1013 mFocusManager.setParameters(mInitialParams); 1014 1015 // Do camera parameter dependent initialization. 1016 mParameters = mCameraDevice.getParameters(); 1017 setCameraParameters(UPDATE_PARAM_ALL); 1018 // Set a listener which updates camera parameters based 1019 // on changed settings. 1020 SettingsManager settingsManager = mActivity.getSettingsManager(); 1021 settingsManager.addListener(this); 1022 mCameraPreviewParamsReady = true; 1023 1024 startPreview(); 1025 1026 onCameraOpened(); 1027 } 1028 1029 @Override 1030 public void onCaptureCancelled() { 1031 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent()); 1032 mActivity.finish(); 1033 } 1034 1035 @Override 1036 public void onCaptureRetake() { 1037 if (mPaused) { 1038 return; 1039 } 1040 mUI.hidePostCaptureAlert(); 1041 setupPreview(); 1042 } 1043 1044 @Override 1045 public void onCaptureDone() { 1046 if (mPaused) { 1047 return; 1048 } 1049 1050 byte[] data = mJpegImageData; 1051 1052 if (mCropValue == null) { 1053 // First handle the no crop case -- just return the value. If the 1054 // caller specifies a "save uri" then write the data to its 1055 // stream. Otherwise, pass back a scaled down version of the bitmap 1056 // directly in the extras. 1057 if (mSaveUri != null) { 1058 OutputStream outputStream = null; 1059 try { 1060 outputStream = mContentResolver.openOutputStream(mSaveUri); 1061 outputStream.write(data); 1062 outputStream.close(); 1063 1064 mActivity.setResultEx(Activity.RESULT_OK); 1065 mActivity.finish(); 1066 } catch (IOException ex) { 1067 // ignore exception 1068 } finally { 1069 CameraUtil.closeSilently(outputStream); 1070 } 1071 } else { 1072 ExifInterface exif = Exif.getExif(data); 1073 int orientation = Exif.getOrientation(exif); 1074 Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024); 1075 bitmap = CameraUtil.rotate(bitmap, orientation); 1076 mActivity.setResultEx(Activity.RESULT_OK, 1077 new Intent("inline-data").putExtra("data", bitmap)); 1078 mActivity.finish(); 1079 } 1080 } else { 1081 // Save the image to a temp file and invoke the cropper 1082 Uri tempUri = null; 1083 FileOutputStream tempStream = null; 1084 try { 1085 File path = mActivity.getFileStreamPath(sTempCropFilename); 1086 path.delete(); 1087 tempStream = mActivity.openFileOutput(sTempCropFilename, 0); 1088 tempStream.write(data); 1089 tempStream.close(); 1090 tempUri = Uri.fromFile(path); 1091 } catch (FileNotFoundException ex) { 1092 mActivity.setResultEx(Activity.RESULT_CANCELED); 1093 mActivity.finish(); 1094 return; 1095 } catch (IOException ex) { 1096 mActivity.setResultEx(Activity.RESULT_CANCELED); 1097 mActivity.finish(); 1098 return; 1099 } finally { 1100 CameraUtil.closeSilently(tempStream); 1101 } 1102 1103 Bundle newExtras = new Bundle(); 1104 if (mCropValue.equals("circle")) { 1105 newExtras.putString("circleCrop", "true"); 1106 } 1107 if (mSaveUri != null) { 1108 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri); 1109 } else { 1110 newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true); 1111 } 1112 if (mActivity.isSecureCamera()) { 1113 newExtras.putBoolean(CameraUtil.KEY_SHOW_WHEN_LOCKED, true); 1114 } 1115 1116 // TODO: Share this constant. 1117 final String CROP_ACTION = "com.android.camera.action.CROP"; 1118 Intent cropIntent = new Intent(CROP_ACTION); 1119 1120 cropIntent.setData(tempUri); 1121 cropIntent.putExtras(newExtras); 1122 1123 mActivity.startActivityForResult(cropIntent, REQUEST_CROP); 1124 } 1125 } 1126 1127 @Override 1128 public void onShutterButtonFocus(boolean pressed) { 1129 // Do nothing. We don't support half-press to focus anymore. 1130 } 1131 1132 @Override 1133 public void onShutterButtonClick() { 1134 if (mPaused || (mCameraState == SWITCHING_CAMERA) 1135 || (mCameraState == PREVIEW_STOPPED)) { 1136 return; 1137 } 1138 1139 // Do not take the picture if there is not enough storage. 1140 if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) { 1141 Log.i(TAG, "Not enough space or storage not ready. remaining=" 1142 + mActivity.getStorageSpaceBytes()); 1143 return; 1144 } 1145 Log.d(TAG, "onShutterButtonClick: mCameraState=" + mCameraState); 1146 1147 if (mSceneMode == CameraUtil.SCENE_MODE_HDR) { 1148 mUI.setSwipingEnabled(false); 1149 } 1150 // If the user wants to do a snapshot while the previous one is still 1151 // in progress, remember the fact and do it after we finish the previous 1152 // one and re-start the preview. Snapshot in progress also includes the 1153 // state that autofocus is focusing and a picture will be taken when 1154 // focus callback arrives. 1155 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)) { 1156 if (!mIsImageCaptureIntent) { 1157 mSnapshotOnIdle = true; 1158 } 1159 return; 1160 } 1161 1162 mSnapshotOnIdle = false; 1163 mFocusManager.focusAndCapture(); 1164 } 1165 1166 private void onResumeTasks() { 1167 Log.v(TAG, "Executing onResumeTasks."); 1168 mActivity.getCameraProvider().requestCamera(mCameraId); 1169 1170 mJpegPictureCallbackTime = 0; 1171 mZoomValue = 0; 1172 1173 mOnResumeTime = SystemClock.uptimeMillis(); 1174 checkDisplayRotation(); 1175 1176 // If first time initialization is not finished, put it in the 1177 // message queue. 1178 if (!mFirstTimeInitialized) { 1179 mHandler.sendEmptyMessage(MSG_FIRST_TIME_INIT); 1180 } else { 1181 initializeSecondTime(); 1182 } 1183 1184 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); 1185 if (gsensor != null) { 1186 mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL); 1187 } 1188 1189 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); 1190 if (msensor != null) { 1191 mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL); 1192 } 1193 } 1194 1195 /** 1196 * @return Whether the currently active camera is front-facing. 1197 */ 1198 private boolean isCameraFrontFacing() { 1199 CameraInfo info = mAppController.getCameraProvider().getCameraInfo()[mCameraId]; 1200 return info.facing == CameraInfo.CAMERA_FACING_FRONT; 1201 } 1202 1203 /** 1204 * The focus manager is the first UI related element to get initialized, and 1205 * it requires the RenderOverlay, so initialize it here 1206 */ 1207 private void initializeFocusManager() { 1208 // Create FocusManager object. startPreview needs it. 1209 // if mFocusManager not null, reuse it 1210 // otherwise create a new instance 1211 if (mFocusManager != null) { 1212 mFocusManager.removeMessages(); 1213 } else { 1214 mMirror = isCameraFrontFacing(); 1215 String[] defaultFocusModes = mActivity.getResources().getStringArray( 1216 R.array.pref_camera_focusmode_default_array); 1217 mFocusManager = new FocusOverlayManager(mActivity.getSettingsManager(), 1218 defaultFocusModes, 1219 mInitialParams, this, mMirror, 1220 mActivity.getMainLooper(), mUI.getFocusUI()); 1221 } 1222 mAppController.addPreviewAreaSizeChangedListener(mFocusManager); 1223 } 1224 1225 @Override 1226 public void resume() { 1227 mPaused = false; 1228 if (mFocusManager != null) { 1229 // If camera is not open when resume is called, focus manager will 1230 // not 1231 // be initialized yet, in which case it will start listening to 1232 // preview area size change later in the initialization. 1233 mAppController.addPreviewAreaSizeChangedListener(mFocusManager); 1234 } 1235 1236 if (mUI.getPreviewAreaSizeChangedListener() != null) { 1237 mAppController.addPreviewAreaSizeChangedListener( 1238 mUI.getPreviewAreaSizeChangedListener()); 1239 } 1240 1241 // Add delay on resume from lock screen only, in order to to speed up 1242 // the onResume --> onPause --> onResume cycle from lock screen. 1243 // Don't do always because letting go of thread can cause delay. 1244 String action = mActivity.getIntent().getAction(); 1245 if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action) 1246 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)) { 1247 Log.v(TAG, "On resume, from lock screen."); 1248 // Note: onPauseAfterSuper() will delete this runnable, so we will 1249 // at most have 1 copy queued up. 1250 mHandler.postDelayed(new Runnable() { 1251 @Override 1252 public void run() { 1253 onResumeTasks(); 1254 } 1255 }, ON_RESUME_TASKS_DELAY_MSEC); 1256 } else { 1257 Log.v(TAG, "On resume."); 1258 onResumeTasks(); 1259 } 1260 SessionStatsCollector.instance().sessionActive(true); 1261 } 1262 1263 @Override 1264 public void pause() { 1265 mPaused = true; 1266 SessionStatsCollector.instance().sessionActive(false); 1267 1268 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); 1269 if (gsensor != null) { 1270 mSensorManager.unregisterListener(this, gsensor); 1271 } 1272 1273 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); 1274 if (msensor != null) { 1275 mSensorManager.unregisterListener(this, msensor); 1276 } 1277 1278 // Reset the focus first. Camera CTS does not guarantee that 1279 // cancelAutoFocus is allowed after preview stops. 1280 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) { 1281 mCameraDevice.cancelAutoFocus(); 1282 } 1283 1284 // If the camera has not been opened asynchronously yet, 1285 // and startPreview hasn't been called, then this is a no-op. 1286 // (e.g. onResume -> onPause -> onResume). 1287 stopPreview(); 1288 1289 mNamedImages = null; 1290 1291 // If we are in an image capture intent and has taken 1292 // a picture, we just clear it in onPause. 1293 mJpegImageData = null; 1294 1295 // Remove the messages and runnables in the queue. 1296 mHandler.removeCallbacksAndMessages(null); 1297 1298 closeCamera(); 1299 mActivity.enableKeepScreenOn(false); 1300 mUI.onPause(); 1301 1302 mPendingSwitchCameraId = -1; 1303 if (mFocusManager != null) { 1304 mFocusManager.removeMessages(); 1305 } 1306 getServices().getMemoryManager().removeListener(this); 1307 mAppController.removePreviewAreaSizeChangedListener(mFocusManager); 1308 if (mUI.getPreviewAreaSizeChangedListener() != null) { 1309 mAppController.removePreviewAreaSizeChangedListener( 1310 mUI.getPreviewAreaSizeChangedListener()); 1311 } 1312 1313 SettingsManager settingsManager = mActivity.getSettingsManager(); 1314 settingsManager.removeListener(this); 1315 } 1316 1317 @Override 1318 public void destroy() { 1319 // TODO: implement this. 1320 } 1321 1322 @Override 1323 public void onLayoutOrientationChanged(boolean isLandscape) { 1324 setDisplayOrientation(); 1325 } 1326 1327 @Override 1328 public void updateCameraOrientation() { 1329 if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) { 1330 setDisplayOrientation(); 1331 } 1332 } 1333 1334 private boolean canTakePicture() { 1335 return isCameraIdle() 1336 && (mActivity.getStorageSpaceBytes() > Storage.LOW_STORAGE_THRESHOLD_BYTES); 1337 } 1338 1339 @Override 1340 public void autoFocus() { 1341 Log.v(TAG,"Starting auto focus"); 1342 mFocusStartTime = System.currentTimeMillis(); 1343 mCameraDevice.autoFocus(mHandler, mAutoFocusCallback); 1344 SessionStatsCollector.instance().autofocusManualTrigger(); 1345 setCameraState(FOCUSING); 1346 } 1347 1348 @Override 1349 public void cancelAutoFocus() { 1350 mCameraDevice.cancelAutoFocus(); 1351 setCameraState(IDLE); 1352 setCameraParameters(UPDATE_PARAM_PREFERENCE); 1353 } 1354 1355 @Override 1356 public void onSingleTapUp(View view, int x, int y) { 1357 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized 1358 || mCameraState == SNAPSHOT_IN_PROGRESS 1359 || mCameraState == SWITCHING_CAMERA 1360 || mCameraState == PREVIEW_STOPPED) { 1361 return; 1362 } 1363 1364 // Check if metering area or focus area is supported. 1365 if (!mFocusAreaSupported && !mMeteringAreaSupported) { 1366 return; 1367 } 1368 mFocusManager.onSingleTapUp(x, y); 1369 } 1370 1371 @Override 1372 public boolean onBackPressed() { 1373 return mUI.onBackPressed(); 1374 } 1375 1376 @Override 1377 public boolean onKeyDown(int keyCode, KeyEvent event) { 1378 switch (keyCode) { 1379 case KeyEvent.KEYCODE_VOLUME_UP: 1380 case KeyEvent.KEYCODE_VOLUME_DOWN: 1381 case KeyEvent.KEYCODE_FOCUS: 1382 if (/* TODO: mActivity.isInCameraApp() && */mFirstTimeInitialized) { 1383 if (event.getRepeatCount() == 0) { 1384 onShutterButtonFocus(true); 1385 } 1386 return true; 1387 } 1388 return false; 1389 case KeyEvent.KEYCODE_CAMERA: 1390 if (mFirstTimeInitialized && event.getRepeatCount() == 0) { 1391 onShutterButtonClick(); 1392 } 1393 return true; 1394 case KeyEvent.KEYCODE_DPAD_CENTER: 1395 // If we get a dpad center event without any focused view, move 1396 // the focus to the shutter button and press it. 1397 if (mFirstTimeInitialized && event.getRepeatCount() == 0) { 1398 // Start auto-focus immediately to reduce shutter lag. After 1399 // the shutter button gets the focus, onShutterButtonFocus() 1400 // will be called again but it is fine. 1401 onShutterButtonFocus(true); 1402 } 1403 return true; 1404 } 1405 return false; 1406 } 1407 1408 @Override 1409 public boolean onKeyUp(int keyCode, KeyEvent event) { 1410 switch (keyCode) { 1411 case KeyEvent.KEYCODE_VOLUME_UP: 1412 case KeyEvent.KEYCODE_VOLUME_DOWN: 1413 if (/* mActivity.isInCameraApp() && */mFirstTimeInitialized) { 1414 onShutterButtonClick(); 1415 return true; 1416 } 1417 return false; 1418 case KeyEvent.KEYCODE_FOCUS: 1419 if (mFirstTimeInitialized) { 1420 onShutterButtonFocus(false); 1421 } 1422 return true; 1423 } 1424 return false; 1425 } 1426 1427 private void closeCamera() { 1428 if (mCameraDevice != null) { 1429 stopFaceDetection(); 1430 mCameraDevice.setZoomChangeListener(null); 1431 mCameraDevice.setFaceDetectionCallback(null, null); 1432 mCameraDevice.setErrorCallback(null, null); 1433 1434 mFaceDetectionStarted = false; 1435 mActivity.getCameraProvider().releaseCamera(mCameraDevice.getCameraId()); 1436 mCameraDevice = null; 1437 setCameraState(PREVIEW_STOPPED); 1438 mFocusManager.onCameraReleased(); 1439 } 1440 } 1441 1442 private void setDisplayOrientation() { 1443 mDisplayRotation = CameraUtil.getDisplayRotation(mActivity); 1444 mDisplayOrientation = CameraUtil.getDisplayOrientation(mDisplayRotation, mCameraId); 1445 mCameraDisplayOrientation = mDisplayOrientation; 1446 mUI.setDisplayOrientation(mDisplayOrientation); 1447 if (mFocusManager != null) { 1448 mFocusManager.setDisplayOrientation(mDisplayOrientation); 1449 } 1450 // Change the camera display orientation 1451 if (mCameraDevice != null) { 1452 mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation); 1453 } 1454 } 1455 1456 /** Only called by UI thread. */ 1457 private void setupPreview() { 1458 mFocusManager.resetTouchFocus(); 1459 startPreview(); 1460 } 1461 1462 /** 1463 * Returns whether we can/should start the preview or not. 1464 */ 1465 private boolean checkPreviewPreconditions() { 1466 if (mPaused) { 1467 return false; 1468 } 1469 1470 if (mCameraDevice == null) { 1471 Log.w(TAG, "startPreview: camera device not ready yet."); 1472 return false; 1473 } 1474 1475 SurfaceTexture st = mActivity.getCameraAppUI().getSurfaceTexture(); 1476 if (st == null) { 1477 Log.w(TAG, "startPreview: surfaceTexture is not ready."); 1478 return false; 1479 } 1480 1481 if (!mCameraPreviewParamsReady) { 1482 Log.w(TAG, "startPreview: parameters for preview is not ready."); 1483 return false; 1484 } 1485 return true; 1486 } 1487 1488 /** 1489 * The start/stop preview should only run on the UI thread. 1490 */ 1491 private void startPreview() { 1492 if (!checkPreviewPreconditions()) { 1493 return; 1494 } 1495 1496 mCameraDevice.setErrorCallback(mHandler, mErrorCallback); 1497 // ICS camera frameworks has a bug. Face detection state is not cleared 1498 // after taking a picture. Stop the preview to work around it. The bug 1499 // was fixed in JB. 1500 // TODO: Remove all of this for 'E'. 1501 if (!NO_STOP_PREVIEW_ICS_WORKAROUND) { 1502 if (mCameraState != PREVIEW_STOPPED) { 1503 stopPreview(); 1504 } 1505 } 1506 1507 setDisplayOrientation(); 1508 1509 if (!mSnapshotOnIdle) { 1510 // If the focus mode is continuous autofocus, call cancelAutoFocus 1511 // to resume it because it may have been paused by autoFocus call. 1512 String focusMode = mFocusManager.getFocusMode(); 1513 if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(focusMode)) { 1514 mCameraDevice.cancelAutoFocus(); 1515 } 1516 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB. 1517 } 1518 setCameraParameters(UPDATE_PARAM_ALL); 1519 // Let UI set its expected aspect ratio 1520 mCameraDevice.setPreviewTexture(mActivity.getCameraAppUI().getSurfaceTexture()); 1521 1522 Log.i(TAG, "startPreview"); 1523 mCameraDevice.startPreview(); 1524 1525 mFocusManager.onPreviewStarted(); 1526 onPreviewStarted(); 1527 SessionStatsCollector.instance().previewActive(true); 1528 if (mSnapshotOnIdle) { 1529 mHandler.post(mDoSnapRunnable); 1530 } 1531 } 1532 1533 @Override 1534 public void stopPreview() { 1535 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) { 1536 Log.i(TAG, "stopPreview"); 1537 mCameraDevice.stopPreview(); 1538 mFaceDetectionStarted = false; 1539 } 1540 setCameraState(PREVIEW_STOPPED); 1541 if (mFocusManager != null) { 1542 mFocusManager.onPreviewStopped(); 1543 } 1544 SessionStatsCollector.instance().previewActive(false); 1545 } 1546 1547 @Override 1548 public void onSettingChanged(SettingsManager settingsManager, int id) { 1549 switch (id) { 1550 case SettingsManager.SETTING_FLASH_MODE: { 1551 updateParametersFlashMode(); 1552 break; 1553 } 1554 default: { 1555 // Do nothing. 1556 } 1557 } 1558 1559 if (mCameraDevice != null) { 1560 mCameraDevice.setParameters(mParameters); 1561 } 1562 } 1563 1564 private void updateCameraParametersInitialize() { 1565 // Reset preview frame rate to the maximum because it may be lowered by 1566 // video camera application. 1567 int[] fpsRange = CameraUtil.getPhotoPreviewFpsRange(mParameters); 1568 if (fpsRange != null && fpsRange.length > 0) { 1569 mParameters.setPreviewFpsRange( 1570 fpsRange[Parameters.PREVIEW_FPS_MIN_INDEX], 1571 fpsRange[Parameters.PREVIEW_FPS_MAX_INDEX]); 1572 } 1573 1574 mParameters.set(CameraUtil.RECORDING_HINT, CameraUtil.FALSE); 1575 1576 // Disable video stabilization. Convenience methods not available in API 1577 // level <= 14 1578 String vstabSupported = mParameters.get("video-stabilization-supported"); 1579 if ("true".equals(vstabSupported)) { 1580 mParameters.set("video-stabilization", "false"); 1581 } 1582 } 1583 1584 private void updateCameraParametersZoom() { 1585 // Set zoom. 1586 if (mParameters.isZoomSupported()) { 1587 mParameters.setZoom(mZoomValue); 1588 } 1589 } 1590 1591 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 1592 private void setAutoExposureLockIfSupported() { 1593 if (mAeLockSupported) { 1594 mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock()); 1595 } 1596 } 1597 1598 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 1599 private void setAutoWhiteBalanceLockIfSupported() { 1600 if (mAwbLockSupported) { 1601 mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock()); 1602 } 1603 } 1604 1605 private void setFocusAreasIfSupported() { 1606 if (mFocusAreaSupported) { 1607 mParameters.setFocusAreas(mFocusManager.getFocusAreas()); 1608 } 1609 } 1610 1611 private void setMeteringAreasIfSupported() { 1612 if (mMeteringAreaSupported) { 1613 mParameters.setMeteringAreas(mFocusManager.getMeteringAreas()); 1614 } 1615 } 1616 1617 private void updateCameraParametersPreference() { 1618 setAutoExposureLockIfSupported(); 1619 setAutoWhiteBalanceLockIfSupported(); 1620 setFocusAreasIfSupported(); 1621 setMeteringAreasIfSupported(); 1622 1623 // Initialize focus mode. 1624 mFocusManager.overrideFocusMode(null); 1625 mParameters.setFocusMode(mFocusManager.getFocusMode()); 1626 SessionStatsCollector.instance().autofocusActive( 1627 mFocusManager.getFocusMode() == CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE); 1628 1629 // Set picture size. 1630 updateParametersPictureSize(); 1631 1632 // Set JPEG quality. 1633 updateParametersPictureQuality(); 1634 1635 // For the following settings, we need to check if the settings are 1636 // still supported by latest driver, if not, ignore the settings. 1637 1638 // Set exposure compensation 1639 updateParametersExposureCompensation(); 1640 1641 // Set the scene mode: also sets flash and white balance. 1642 updateParametersSceneMode(); 1643 1644 if (mContinuousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) { 1645 updateAutoFocusMoveCallback(); 1646 } 1647 } 1648 1649 private void updateParametersPictureSize() { 1650 SettingsManager settingsManager = mActivity.getSettingsManager(); 1651 String pictureSize = settingsManager 1652 .get(isCameraFrontFacing() ? SettingsManager.SETTING_PICTURE_SIZE_FRONT 1653 : SettingsManager.SETTING_PICTURE_SIZE_BACK); 1654 1655 List<Size> supported = mParameters.getSupportedPictureSizes(); 1656 SettingsUtil.setCameraPictureSize(pictureSize, supported, mParameters, 1657 mCameraDevice.getCameraId()); 1658 Size size = mParameters.getPictureSize(); 1659 1660 // Set a preview size that is closest to the viewfinder height and has 1661 // the right aspect ratio. 1662 List<Size> sizes = mParameters.getSupportedPreviewSizes(); 1663 Size optimalSize = CameraUtil.getOptimalPreviewSize(mActivity, sizes, 1664 (double) size.width / size.height); 1665 Size original = mParameters.getPreviewSize(); 1666 if (!original.equals(optimalSize)) { 1667 mParameters.setPreviewSize(optimalSize.width, optimalSize.height); 1668 1669 // Zoom related settings will be changed for different preview 1670 // sizes, so set and read the parameters to get latest values 1671 if (mHandler.getLooper() == Looper.myLooper()) { 1672 // On UI thread only, not when camera starts up 1673 setupPreview(); 1674 } else { 1675 mCameraDevice.setParameters(mParameters); 1676 } 1677 mParameters = mCameraDevice.getParameters(); 1678 } 1679 1680 if (optimalSize.width != 0 && optimalSize.height != 0) { 1681 mUI.updatePreviewAspectRatio((float) optimalSize.width 1682 / (float) optimalSize.height); 1683 } 1684 Log.i(TAG, "Preview size is " + optimalSize.width + "x" + optimalSize.height); 1685 } 1686 1687 private void updateParametersPictureQuality() { 1688 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId, 1689 CameraProfile.QUALITY_HIGH); 1690 mParameters.setJpegQuality(jpegQuality); 1691 } 1692 1693 private void updateParametersExposureCompensation() { 1694 SettingsManager settingsManager = mActivity.getSettingsManager(); 1695 if (settingsManager.getBoolean(SettingsManager.SETTING_EXPOSURE_COMPENSATION_ENABLED)) { 1696 int value = Integer.parseInt(settingsManager.get(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE)); 1697 int max = mParameters.getMaxExposureCompensation(); 1698 int min = mParameters.getMinExposureCompensation(); 1699 if (value >= min && value <= max) { 1700 mParameters.setExposureCompensation(value); 1701 } else { 1702 Log.w(TAG, "invalid exposure range: " + value); 1703 } 1704 } else { 1705 // If exposure compensation is not enabled, reset the exposure compensation value. 1706 setExposureCompensation(0); 1707 } 1708 1709 } 1710 1711 private void updateParametersSceneMode() { 1712 SettingsManager settingsManager = mActivity.getSettingsManager(); 1713 1714 mSceneMode = settingsManager.get(SettingsManager.SETTING_SCENE_MODE); 1715 if (CameraUtil.isSupported(mSceneMode, mParameters.getSupportedSceneModes())) { 1716 if (!mParameters.getSceneMode().equals(mSceneMode)) { 1717 mParameters.setSceneMode(mSceneMode); 1718 1719 // Setting scene mode will change the settings of flash mode, 1720 // white balance, and focus mode. Here we read back the 1721 // parameters, so we can know those settings. 1722 mCameraDevice.setParameters(mParameters); 1723 mParameters = mCameraDevice.getParameters(); 1724 } 1725 } else { 1726 mSceneMode = mParameters.getSceneMode(); 1727 if (mSceneMode == null) { 1728 mSceneMode = Parameters.SCENE_MODE_AUTO; 1729 } 1730 } 1731 1732 if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) { 1733 // Set flash mode. 1734 updateParametersFlashMode(); 1735 1736 // Set focus mode. 1737 mFocusManager.overrideFocusMode(null); 1738 mParameters.setFocusMode(mFocusManager.getFocusMode()); 1739 } else { 1740 mFocusManager.overrideFocusMode(mParameters.getFocusMode()); 1741 } 1742 } 1743 1744 private void updateParametersFlashMode() { 1745 SettingsManager settingsManager = mActivity.getSettingsManager(); 1746 1747 String flashMode = settingsManager.get(SettingsManager.SETTING_FLASH_MODE); 1748 List<String> supportedFlash = mParameters.getSupportedFlashModes(); 1749 if (CameraUtil.isSupported(flashMode, supportedFlash)) { 1750 mParameters.setFlashMode(flashMode); 1751 } 1752 } 1753 1754 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 1755 private void updateAutoFocusMoveCallback() { 1756 if (mParameters.getFocusMode().equals(CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE)) { 1757 mCameraDevice.setAutoFocusMoveCallback(mHandler, 1758 (CameraAFMoveCallback) mAutoFocusMoveCallback); 1759 } else { 1760 mCameraDevice.setAutoFocusMoveCallback(null, null); 1761 } 1762 } 1763 1764 /** 1765 * Sets the exposure compensation to the given value and also updates settings. 1766 * 1767 * @param value exposure compensation value to be set 1768 */ 1769 public void setExposureCompensation(int value) { 1770 int max = mParameters.getMaxExposureCompensation(); 1771 int min = mParameters.getMinExposureCompensation(); 1772 if (value >= min && value <= max) { 1773 mParameters.setExposureCompensation(value); 1774 SettingsManager settingsManager = mActivity.getSettingsManager(); 1775 settingsManager.set(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE, Integer.toString(value)); 1776 } else { 1777 Log.w(TAG, "invalid exposure range: " + value); 1778 } 1779 } 1780 1781 // We separate the parameters into several subsets, so we can update only 1782 // the subsets actually need updating. The PREFERENCE set needs extra 1783 // locking because the preference can be changed from GLThread as well. 1784 private void setCameraParameters(int updateSet) { 1785 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) { 1786 updateCameraParametersInitialize(); 1787 } 1788 1789 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) { 1790 updateCameraParametersZoom(); 1791 } 1792 1793 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) { 1794 updateCameraParametersPreference(); 1795 } 1796 1797 mCameraDevice.setParameters(mParameters); 1798 } 1799 1800 // If the Camera is idle, update the parameters immediately, otherwise 1801 // accumulate them in mUpdateSet and update later. 1802 private void setCameraParametersWhenIdle(int additionalUpdateSet) { 1803 mUpdateSet |= additionalUpdateSet; 1804 if (mCameraDevice == null) { 1805 // We will update all the parameters when we open the device, so 1806 // we don't need to do anything now. 1807 mUpdateSet = 0; 1808 return; 1809 } else if (isCameraIdle()) { 1810 setCameraParameters(mUpdateSet); 1811 updateSceneMode(); 1812 mUpdateSet = 0; 1813 } else { 1814 if (!mHandler.hasMessages(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE)) { 1815 mHandler.sendEmptyMessageDelayed(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000); 1816 } 1817 } 1818 } 1819 1820 @Override 1821 public boolean isCameraIdle() { 1822 return (mCameraState == IDLE) || 1823 (mCameraState == PREVIEW_STOPPED) || 1824 ((mFocusManager != null) && mFocusManager.isFocusCompleted() 1825 && (mCameraState != SWITCHING_CAMERA)); 1826 } 1827 1828 @Override 1829 public boolean isImageCaptureIntent() { 1830 String action = mActivity.getIntent().getAction(); 1831 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action) 1832 || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action)); 1833 } 1834 1835 private void setupCaptureParams() { 1836 Bundle myExtras = mActivity.getIntent().getExtras(); 1837 if (myExtras != null) { 1838 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT); 1839 mCropValue = myExtras.getString("crop"); 1840 } 1841 } 1842 1843 private void initializeCapabilities() { 1844 mInitialParams = mCameraDevice.getParameters(); 1845 mFocusAreaSupported = CameraUtil.isFocusAreaSupported(mInitialParams); 1846 mMeteringAreaSupported = CameraUtil.isMeteringAreaSupported(mInitialParams); 1847 mAeLockSupported = CameraUtil.isAutoExposureLockSupported(mInitialParams); 1848 mAwbLockSupported = CameraUtil.isAutoWhiteBalanceLockSupported(mInitialParams); 1849 mContinuousFocusSupported = mInitialParams.getSupportedFocusModes().contains( 1850 CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE); 1851 } 1852 1853 // TODO: Remove this 1854 @Override 1855 public int onZoomChanged(int index) { 1856 // Not useful to change zoom value when the activity is paused. 1857 if (mPaused) { 1858 return index; 1859 } 1860 mZoomValue = index; 1861 if (mParameters == null || mCameraDevice == null) { 1862 return index; 1863 } 1864 // Set zoom parameters asynchronously 1865 mParameters.setZoom(mZoomValue); 1866 mCameraDevice.setParameters(mParameters); 1867 Parameters p = mCameraDevice.getParameters(); 1868 if (p != null) { 1869 return p.getZoom(); 1870 } 1871 return index; 1872 } 1873 1874 @Override 1875 public int getCameraState() { 1876 return mCameraState; 1877 } 1878 1879 @Override 1880 public void onMemoryStateChanged(int state) { 1881 mAppController.setShutterEnabled(state == MemoryManager.STATE_OK); 1882 } 1883 1884 @Override 1885 public void onLowMemory() { 1886 // Not much we can do in the photo module. 1887 } 1888 1889 @Override 1890 public void onAccuracyChanged(Sensor sensor, int accuracy) { 1891 } 1892 1893 @Override 1894 public void onSensorChanged(SensorEvent event) { 1895 int type = event.sensor.getType(); 1896 float[] data; 1897 if (type == Sensor.TYPE_ACCELEROMETER) { 1898 data = mGData; 1899 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) { 1900 data = mMData; 1901 } else { 1902 // we should not be here. 1903 return; 1904 } 1905 for (int i = 0; i < 3; i++) { 1906 data[i] = event.values[i]; 1907 } 1908 float[] orientation = new float[3]; 1909 SensorManager.getRotationMatrix(mR, null, mGData, mMData); 1910 SensorManager.getOrientation(mR, orientation); 1911 mHeading = (int) (orientation[0] * 180f / Math.PI) % 360; 1912 if (mHeading < 0) { 1913 mHeading += 360; 1914 } 1915 } 1916 1917 // For debugging only. 1918 public void setDebugUri(Uri uri) { 1919 mDebugUri = uri; 1920 } 1921 1922 // For debugging only. 1923 private void saveToDebugUri(byte[] data) { 1924 if (mDebugUri != null) { 1925 OutputStream outputStream = null; 1926 try { 1927 outputStream = mContentResolver.openOutputStream(mDebugUri); 1928 outputStream.write(data); 1929 outputStream.close(); 1930 } catch (IOException e) { 1931 Log.e(TAG, "Exception while writing debug jpeg file", e); 1932 } finally { 1933 CameraUtil.closeSilently(outputStream); 1934 } 1935 } 1936 } 1937 1938 /** 1939 * Depending on the device, this returns whether we should avoid using the 1940 * ICS-only workaround to call stopPreview before startPreview 1941 * <p> 1942 * The proper solution is to remove the stopPreview call completely, but as 1943 * we have only limited time for testing left, let's be careful and target 1944 * specific devices only. 1945 * <p> 1946 * Context: http://b/13966525 1947 */ 1948 private static boolean shouldNotCallStopPreviewAfterTakingPicture() { 1949 // The M8 is the only known device with a really long stopPreview 1950 // duration. 1951 return Build.MANUFACTURER.toLowerCase().contains("htc") && 1952 Build.DEVICE.toLowerCase().contains("m8"); 1953 } 1954} 1955