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