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