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