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