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