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