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