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