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