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