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