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