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