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