PhotoModule.java revision 987ee64612e2510004fdf08536746c87234d01c1
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.ContentResolver; 22import android.content.Context; 23import android.content.Intent; 24import android.graphics.Bitmap; 25import android.graphics.BitmapFactory; 26import android.graphics.SurfaceTexture; 27import android.hardware.Sensor; 28import android.hardware.SensorEvent; 29import android.hardware.SensorEventListener; 30import android.hardware.SensorManager; 31import android.location.Location; 32import android.media.CameraProfile; 33import android.net.Uri; 34import android.os.AsyncTask; 35import android.os.Build; 36import android.os.Bundle; 37import android.os.Handler; 38import android.os.Looper; 39import android.os.Message; 40import android.os.MessageQueue; 41import android.os.SystemClock; 42import android.provider.MediaStore; 43import android.view.KeyEvent; 44import android.view.View; 45 46import com.android.camera.PhotoModule.NamedImages.NamedEntity; 47import com.android.camera.app.AppController; 48import com.android.camera.app.CameraAppUI; 49import com.android.camera.app.CameraProvider; 50import com.android.camera.app.MediaSaver; 51import com.android.camera.app.MemoryManager; 52import com.android.camera.app.MemoryManager.MemoryListener; 53import com.android.camera.app.MotionManager; 54import com.android.camera.debug.Log; 55import com.android.camera.exif.ExifInterface; 56import com.android.camera.exif.ExifTag; 57import com.android.camera.exif.Rational; 58import com.android.camera.hardware.HardwareSpec; 59import com.android.camera.hardware.HardwareSpecImpl; 60import com.android.camera.module.ModuleController; 61import com.android.camera.remote.RemoteCameraModule; 62import com.android.camera.settings.CameraPictureSizesCacher; 63import com.android.camera.settings.Keys; 64import com.android.camera.settings.ResolutionUtil; 65import com.android.camera.settings.SettingsManager; 66import com.android.camera.settings.SettingsUtil; 67import com.android.camera.ui.CountDownView; 68import com.android.camera.ui.TouchCoordinate; 69import com.android.camera.util.ApiHelper; 70import com.android.camera.util.CameraUtil; 71import com.android.camera.util.GcamHelper; 72import com.android.camera.util.GservicesHelper; 73import com.android.camera.util.SessionStatsCollector; 74import com.android.camera.util.UsageStatistics; 75import com.android.camera.widget.AspectRatioSelector; 76import com.android.camera2.R; 77import com.android.ex.camera2.portability.CameraAgent; 78import com.android.ex.camera2.portability.CameraAgent.CameraAFCallback; 79import com.android.ex.camera2.portability.CameraAgent.CameraAFMoveCallback; 80import com.android.ex.camera2.portability.CameraAgent.CameraPictureCallback; 81import com.android.ex.camera2.portability.CameraAgent.CameraProxy; 82import com.android.ex.camera2.portability.CameraAgent.CameraShutterCallback; 83import com.android.ex.camera2.portability.CameraCapabilities; 84import com.android.ex.camera2.portability.CameraDeviceInfo.Characteristics; 85import com.android.ex.camera2.portability.CameraSettings; 86import com.android.ex.camera2.portability.Size; 87import com.google.common.logging.eventprotos; 88 89import java.io.ByteArrayOutputStream; 90import java.io.File; 91import java.io.FileNotFoundException; 92import java.io.FileOutputStream; 93import java.io.IOException; 94import java.io.OutputStream; 95import java.lang.ref.WeakReference; 96import java.util.ArrayList; 97import java.util.List; 98import java.util.Vector; 99 100public class PhotoModule 101 extends CameraModule 102 implements PhotoController, 103 ModuleController, 104 MemoryListener, 105 FocusOverlayManager.Listener, 106 SensorEventListener, 107 SettingsManager.OnSettingChangedListener, 108 RemoteCameraModule, 109 CountDownView.OnCountDownStatusListener { 110 111 public static final String PHOTO_MODULE_STRING_ID = "PhotoModule"; 112 113 private static final Log.Tag TAG = new Log.Tag(PHOTO_MODULE_STRING_ID); 114 115 // We number the request code from 1000 to avoid collision with Gallery. 116 private static final int REQUEST_CROP = 1000; 117 118 // Messages defined for the UI thread handler. 119 private static final int MSG_FIRST_TIME_INIT = 1; 120 private static final int MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE = 2; 121 122 // The subset of parameters we need to update in setCameraParameters(). 123 private static final int UPDATE_PARAM_INITIALIZE = 1; 124 private static final int UPDATE_PARAM_ZOOM = 2; 125 private static final int UPDATE_PARAM_PREFERENCE = 4; 126 private static final int UPDATE_PARAM_ALL = -1; 127 128 private static final String DEBUG_IMAGE_PREFIX = "DEBUG_"; 129 130 private CameraActivity mActivity; 131 private CameraProxy mCameraDevice; 132 private int mCameraId; 133 private CameraCapabilities mCameraCapabilities; 134 private CameraSettings mCameraSettings; 135 private boolean mPaused; 136 137 private PhotoUI mUI; 138 139 // The activity is going to switch to the specified camera id. This is 140 // needed because texture copy is done in GL thread. -1 means camera is not 141 // switching. 142 protected int mPendingSwitchCameraId = -1; 143 144 // When setCameraParametersWhenIdle() is called, we accumulate the subsets 145 // needed to be updated in mUpdateSet. 146 private int mUpdateSet; 147 148 private float mZoomValue; // The current zoom ratio. 149 private int mTimerDuration; 150 /** Set when a volume button is clicked to take photo */ 151 private boolean mVolumeButtonClickedFlag = false; 152 153 private boolean mFocusAreaSupported; 154 private boolean mMeteringAreaSupported; 155 private boolean mAeLockSupported; 156 private boolean mAwbLockSupported; 157 private boolean mContinuousFocusSupported; 158 159 private static final String sTempCropFilename = "crop-temp"; 160 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 is 182 * completed. TODO: consider publishing by moving into MediaStore. 183 */ 184 private static final String EXTRA_QUICK_CAPTURE = 185 "android.intent.extra.quickCapture"; 186 187 // The display rotation in degrees. This is only valid when mCameraState is 188 // not PREVIEW_STOPPED. 189 private int mDisplayRotation; 190 // The value for UI components like indicators. 191 private int mDisplayOrientation; 192 // The value for cameradevice.CameraSettings.setPhotoRotationDegrees. 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 AppController mAppController; 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 long mFocusStartTime; 218 private long mShutterCallbackTime; 219 private long mPostViewPictureCallbackTime; 220 private long mRawPictureCallbackTime; 221 private long mJpegPictureCallbackTime; 222 private long mOnResumeTime; 223 private byte[] mJpegImageData; 224 /** Touch coordinate for shutter button press. */ 225 private TouchCoordinate mShutterTouchCoordinate; 226 227 228 // These latency time are for the CameraLatency test. 229 public long mAutoFocusTime; 230 public long mShutterLag; 231 public long mShutterToPictureDisplayedTime; 232 public long mPictureDisplayedToJpegCallbackTime; 233 public long mJpegCallbackFinishTime; 234 public long mCaptureStartTime; 235 236 // This handles everything about focus. 237 private FocusOverlayManager mFocusManager; 238 239 private final int mGcamModeIndex; 240 private SoundPlayer mCountdownSoundPlayer; 241 242 private CameraCapabilities.SceneMode mSceneMode; 243 244 private final Handler mHandler = new MainHandler(this); 245 246 private boolean mQuickCapture; 247 private SensorManager mSensorManager; 248 private final float[] mGData = new float[3]; 249 private final float[] mMData = new float[3]; 250 private final float[] mR = new float[16]; 251 private int mHeading = -1; 252 253 /** Used to detect motion. We use this to release focus lock early. */ 254 private MotionManager mMotionManager; 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 private boolean mShouldResizeTo16x9 = false; 269 270 /** 271 * We keep the flash setting before entering scene modes (HDR) 272 * and restore it after HDR is off. 273 */ 274 private String mFlashModeBeforeSceneMode; 275 276 /** 277 * This callback gets called when user select whether or not to 278 * turn on geo-tagging. 279 */ 280 public interface LocationDialogCallback { 281 /** 282 * Gets called after user selected/unselected geo-tagging feature. 283 * 284 * @param selected whether or not geo-tagging feature is selected 285 */ 286 public void onLocationTaggingSelected(boolean selected); 287 } 288 289 /** 290 * This callback defines the text that is shown in the aspect ratio selection 291 * dialog, provides the current aspect ratio, and gets notified when user changes 292 * aspect ratio selection in the dialog. 293 */ 294 public interface AspectRatioDialogCallback { 295 /** 296 * Returns current aspect ratio that is being used to set as default. 297 */ 298 public AspectRatioSelector.AspectRatio getCurrentAspectRatio(); 299 300 /** 301 * Gets notified when user has made the aspect ratio selection. 302 * 303 * @param newAspectRatio aspect ratio that user has selected 304 * @param dialogHandlingFinishedRunnable runnable to run when the operations 305 * needed to handle changes from dialog 306 * are finished. 307 */ 308 public void onAspectRatioSelected(AspectRatioSelector.AspectRatio newAspectRatio, 309 Runnable dialogHandlingFinishedRunnable); 310 } 311 312 private void checkDisplayRotation() { 313 // Need to just be a no-op for the quick resume-pause scenario. 314 if (mPaused) { 315 return; 316 } 317 // Set the display orientation if display rotation has changed. 318 // Sometimes this happens when the device is held upside 319 // down and camera app is opened. Rotation animation will 320 // take some time and the rotation value we have got may be 321 // wrong. Framework does not have a callback for this now. 322 if (CameraUtil.getDisplayRotation(mActivity) != mDisplayRotation) { 323 setDisplayOrientation(); 324 } 325 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) { 326 mHandler.postDelayed(new Runnable() { 327 @Override 328 public void run() { 329 checkDisplayRotation(); 330 } 331 }, 100); 332 } 333 } 334 335 /** 336 * This Handler is used to post message back onto the main thread of the 337 * application 338 */ 339 private static class MainHandler extends Handler { 340 private final WeakReference<PhotoModule> mModule; 341 342 public MainHandler(PhotoModule module) { 343 super(Looper.getMainLooper()); 344 mModule = new WeakReference<PhotoModule>(module); 345 } 346 347 @Override 348 public void handleMessage(Message msg) { 349 PhotoModule module = mModule.get(); 350 if (module == null) { 351 return; 352 } 353 switch (msg.what) { 354 case MSG_FIRST_TIME_INIT: { 355 module.initializeFirstTime(); 356 break; 357 } 358 359 case MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE: { 360 module.setCameraParametersWhenIdle(0); 361 break; 362 } 363 } 364 } 365 } 366 367 private void switchToGcamCapture() { 368 if (mActivity != null && mGcamModeIndex != 0) { 369 SettingsManager settingsManager = mActivity.getSettingsManager(); 370 settingsManager.set(SettingsManager.SCOPE_GLOBAL, 371 Keys.KEY_CAMERA_HDR_PLUS, true); 372 373 // Disable the HDR+ button to prevent callbacks from being 374 // queued before the correct callback is attached to the button 375 // in the new module. The new module will set the enabled/disabled 376 // of this button when the module's preferred camera becomes available. 377 ButtonManager buttonManager = mActivity.getButtonManager(); 378 379 buttonManager.disableButtonClick(ButtonManager.BUTTON_HDR_PLUS); 380 381 mAppController.getCameraAppUI().freezeScreenUntilPreviewReady(); 382 383 // Do not post this to avoid this module switch getting interleaved with 384 // other button callbacks. 385 mActivity.onModeSelected(mGcamModeIndex); 386 387 buttonManager.enableButtonClick(ButtonManager.BUTTON_HDR_PLUS); 388 } 389 } 390 391 /** 392 * Constructs a new photo module. 393 */ 394 public PhotoModule(AppController app) { 395 super(app); 396 mGcamModeIndex = app.getAndroidContext().getResources() 397 .getInteger(R.integer.camera_mode_gcam); 398 } 399 400 @Override 401 public String getPeekAccessibilityString() { 402 return mAppController.getAndroidContext() 403 .getResources().getString(R.string.photo_accessibility_peek); 404 } 405 406 @Override 407 public String getModuleStringIdentifier() { 408 return PHOTO_MODULE_STRING_ID; 409 } 410 411 @Override 412 public void init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent) { 413 mActivity = activity; 414 // TODO: Need to look at the controller interface to see if we can get 415 // rid of passing in the activity directly. 416 mAppController = mActivity; 417 418 mUI = new PhotoUI(mActivity, this, mActivity.getModuleLayoutRoot()); 419 mActivity.setPreviewStatusListener(mUI); 420 421 SettingsManager settingsManager = mActivity.getSettingsManager(); 422 mCameraId = settingsManager.getInteger(mAppController.getModuleScope(), 423 Keys.KEY_CAMERA_ID); 424 425 // TODO: Move this to SettingsManager as a part of upgrade procedure. 426 if (!settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL, 427 Keys.KEY_USER_SELECTED_ASPECT_RATIO)) { 428 // Switch to back camera to set aspect ratio. 429 mCameraId = settingsManager.getIntegerDefault(Keys.KEY_CAMERA_ID); 430 } 431 432 mContentResolver = mActivity.getContentResolver(); 433 434 // Surface texture is from camera screen nail and startPreview needs it. 435 // This must be done before startPreview. 436 mIsImageCaptureIntent = isImageCaptureIntent(); 437 438 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false); 439 mSensorManager = (SensorManager) (mActivity.getSystemService(Context.SENSOR_SERVICE)); 440 mUI.setCountdownFinishedListener(this); 441 mCountdownSoundPlayer = new SoundPlayer(mAppController.getAndroidContext()); 442 443 // TODO: Make this a part of app controller API. 444 View cancelButton = mActivity.findViewById(R.id.shutter_cancel_button); 445 cancelButton.setOnClickListener(new View.OnClickListener() { 446 @Override 447 public void onClick(View view) { 448 cancelCountDown(); 449 } 450 }); 451 } 452 453 private void cancelCountDown() { 454 if (mUI.isCountingDown()) { 455 // Cancel on-going countdown. 456 mUI.cancelCountDown(); 457 } 458 mAppController.getCameraAppUI().transitionToCapture(); 459 mAppController.getCameraAppUI().showModeOptions(); 460 mAppController.setShutterEnabled(true); 461 } 462 463 @Override 464 public boolean isUsingBottomBar() { 465 return true; 466 } 467 468 private void initializeControlByIntent() { 469 if (mIsImageCaptureIntent) { 470 mActivity.getCameraAppUI().transitionToIntentCaptureLayout(); 471 setupCaptureParams(); 472 } 473 } 474 475 private void onPreviewStarted() { 476 mAppController.onPreviewStarted(); 477 mAppController.setShutterEnabled(true); 478 setCameraState(IDLE); 479 startFaceDetection(); 480 settingsFirstRun(); 481 } 482 483 /** 484 * Prompt the user to pick to record location and choose aspect ratio for the 485 * very first run of camera only. 486 */ 487 private void settingsFirstRun() { 488 final SettingsManager settingsManager = mActivity.getSettingsManager(); 489 490 if (mActivity.isSecureCamera() || isImageCaptureIntent()) { 491 return; 492 } 493 494 boolean locationPrompt = !settingsManager.isSet(SettingsManager.SCOPE_GLOBAL, 495 Keys.KEY_RECORD_LOCATION); 496 boolean aspectRatioPrompt = !settingsManager.getBoolean( 497 SettingsManager.SCOPE_GLOBAL, Keys.KEY_USER_SELECTED_ASPECT_RATIO); 498 if (!locationPrompt && !aspectRatioPrompt) { 499 return; 500 } 501 502 // Check if the back camera exists 503 int backCameraId = mAppController.getCameraProvider().getFirstBackCameraId(); 504 if (backCameraId == -1) { 505 // If there is no back camera, do not show the prompt. 506 return; 507 } 508 509 if (locationPrompt) { 510 // Show both location and aspect ratio selection dialog. 511 mUI.showLocationAndAspectRatioDialog(new LocationDialogCallback(){ 512 @Override 513 public void onLocationTaggingSelected(boolean selected) { 514 Keys.setLocation(mActivity.getSettingsManager(), selected, 515 mActivity.getLocationManager()); 516 } 517 }, createAspectRatioDialogCallback()); 518 } else { 519 // App upgrade. Only show aspect ratio selection. 520 boolean wasShown = mUI.showAspectRatioDialog(createAspectRatioDialogCallback()); 521 if (!wasShown) { 522 // If the dialog was not shown, set this flag to true so that we 523 // never have to check for it again. It means that we don't need 524 // to show the dialog on this device. 525 mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL, 526 Keys.KEY_USER_SELECTED_ASPECT_RATIO, true); 527 } 528 } 529 } 530 531 private AspectRatioDialogCallback createAspectRatioDialogCallback() { 532 Size currentSize = mCameraSettings.getCurrentPhotoSize(); 533 float aspectRatio = (float) currentSize.width() / (float) currentSize.height(); 534 if (aspectRatio < 1f) { 535 aspectRatio = 1 / aspectRatio; 536 } 537 final AspectRatioSelector.AspectRatio currentAspectRatio; 538 if (Math.abs(aspectRatio - 4f / 3f) <= 0.1f) { 539 currentAspectRatio = AspectRatioSelector.AspectRatio.ASPECT_RATIO_4x3; 540 } else if (Math.abs(aspectRatio - 16f / 9f) <= 0.1f) { 541 currentAspectRatio = AspectRatioSelector.AspectRatio.ASPECT_RATIO_16x9; 542 } else { 543 // TODO: Log error and not show dialog. 544 return null; 545 } 546 547 List<Size> sizes = mCameraCapabilities.getSupportedPhotoSizes(); 548 List<Size> pictureSizes = ResolutionUtil 549 .getDisplayableSizesFromSupported(sizes, true); 550 551 // This logic below finds the largest resolution for each aspect ratio. 552 // TODO: Move this somewhere that can be shared with SettingsActivity 553 int aspectRatio4x3Resolution = 0; 554 int aspectRatio16x9Resolution = 0; 555 Size largestSize4x3 = new Size(0, 0); 556 Size largestSize16x9 = new Size(0, 0); 557 for (Size size : pictureSizes) { 558 float pictureAspectRatio = (float) size.width() / (float) size.height(); 559 pictureAspectRatio = pictureAspectRatio < 1 ? 560 1f / pictureAspectRatio : pictureAspectRatio; 561 int resolution = size.width() * size.height(); 562 if (Math.abs(pictureAspectRatio - 4f / 3f) < 0.1f) { 563 if (resolution > aspectRatio4x3Resolution) { 564 aspectRatio4x3Resolution = resolution; 565 largestSize4x3 = size; 566 } 567 } else if (Math.abs(pictureAspectRatio - 16f / 9f) < 0.1f) { 568 if (resolution > aspectRatio16x9Resolution) { 569 aspectRatio16x9Resolution = resolution; 570 largestSize16x9 = size; 571 } 572 } 573 } 574 575 // Use the largest 4x3 and 16x9 sizes as candidates for picture size selection. 576 final Size size4x3ToSelect = largestSize4x3; 577 final Size size16x9ToSelect = largestSize16x9; 578 579 AspectRatioDialogCallback callback = new AspectRatioDialogCallback() { 580 581 @Override 582 public AspectRatioSelector.AspectRatio getCurrentAspectRatio() { 583 return currentAspectRatio; 584 } 585 586 @Override 587 public void onAspectRatioSelected(AspectRatioSelector.AspectRatio newAspectRatio, 588 Runnable dialogHandlingFinishedRunnable) { 589 if (newAspectRatio == AspectRatioSelector.AspectRatio.ASPECT_RATIO_4x3) { 590 String largestSize4x3Text = SettingsUtil.sizeToSetting(size4x3ToSelect); 591 mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL, 592 Keys.KEY_PICTURE_SIZE_BACK, 593 largestSize4x3Text); 594 } else if (newAspectRatio == AspectRatioSelector.AspectRatio.ASPECT_RATIO_16x9) { 595 String largestSize16x9Text = SettingsUtil.sizeToSetting(size16x9ToSelect); 596 mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL, 597 Keys.KEY_PICTURE_SIZE_BACK, 598 largestSize16x9Text); 599 } 600 mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL, 601 Keys.KEY_USER_SELECTED_ASPECT_RATIO, true); 602 String aspectRatio = mActivity.getSettingsManager().getString( 603 SettingsManager.SCOPE_GLOBAL, 604 Keys.KEY_USER_SELECTED_ASPECT_RATIO); 605 Log.e(TAG, "aspect ratio after setting it to true=" + aspectRatio); 606 if (newAspectRatio != currentAspectRatio) { 607 Log.i(TAG, "changing aspect ratio from dialog"); 608 stopPreview(); 609 startPreview(); 610 mUI.setRunnableForNextFrame(dialogHandlingFinishedRunnable); 611 } else { 612 mHandler.post(dialogHandlingFinishedRunnable); 613 } 614 } 615 }; 616 return callback; 617 } 618 619 @Override 620 public void onPreviewUIReady() { 621 Log.i(TAG, "onPreviewUIReady"); 622 startPreview(); 623 } 624 625 @Override 626 public void onPreviewUIDestroyed() { 627 if (mCameraDevice == null) { 628 return; 629 } 630 mCameraDevice.setPreviewTexture(null); 631 stopPreview(); 632 } 633 634 @Override 635 public void startPreCaptureAnimation() { 636 mAppController.startFlashAnimation(false); 637 } 638 639 private void onCameraOpened() { 640 openCameraCommon(); 641 initializeControlByIntent(); 642 } 643 644 private void switchCamera() { 645 if (mPaused) { 646 return; 647 } 648 cancelCountDown(); 649 650 mAppController.freezeScreenUntilPreviewReady(); 651 SettingsManager settingsManager = mActivity.getSettingsManager(); 652 653 Log.i(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId); 654 closeCamera(); 655 mCameraId = mPendingSwitchCameraId; 656 657 settingsManager.set(mAppController.getModuleScope(), Keys.KEY_CAMERA_ID, mCameraId); 658 requestCameraOpen(); 659 mUI.clearFaces(); 660 if (mFocusManager != null) { 661 mFocusManager.removeMessages(); 662 } 663 664 mMirror = isCameraFrontFacing(); 665 mFocusManager.setMirror(mMirror); 666 // Start switch camera animation. Post a message because 667 // onFrameAvailable from the old camera may already exist. 668 } 669 670 /** 671 * Uses the {@link CameraProvider} to open the currently-selected camera 672 * device, using {@link GservicesHelper} to choose between API-1 and API-2. 673 */ 674 private void requestCameraOpen() { 675 Log.v(TAG, "requestCameraOpen"); 676 mActivity.getCameraProvider().requestCamera(mCameraId, 677 GservicesHelper.useCamera2ApiThroughPortabilityLayer(mActivity)); 678 } 679 680 private final ButtonManager.ButtonCallback mCameraCallback = 681 new ButtonManager.ButtonCallback() { 682 @Override 683 public void onStateChanged(int state) { 684 // At the time this callback is fired, the camera id 685 // has be set to the desired camera. 686 687 if (mPaused || mAppController.getCameraProvider().waitingForCamera()) { 688 return; 689 } 690 // If switching to back camera, and HDR+ is still on, 691 // switch back to gcam, otherwise handle callback normally. 692 SettingsManager settingsManager = mActivity.getSettingsManager(); 693 if (Keys.isCameraBackFacing(settingsManager, 694 mAppController.getModuleScope())) { 695 if (Keys.requestsReturnToHdrPlus(settingsManager, 696 mAppController.getModuleScope())) { 697 switchToGcamCapture(); 698 return; 699 } 700 } 701 702 mPendingSwitchCameraId = state; 703 704 Log.d(TAG, "Start to switch camera. cameraId=" + state); 705 // We need to keep a preview frame for the animation before 706 // releasing the camera. This will trigger 707 // onPreviewTextureCopied. 708 // TODO: Need to animate the camera switch 709 switchCamera(); 710 } 711 }; 712 713 private final ButtonManager.ButtonCallback mHdrPlusCallback = 714 new ButtonManager.ButtonCallback() { 715 @Override 716 public void onStateChanged(int state) { 717 SettingsManager settingsManager = mActivity.getSettingsManager(); 718 if (GcamHelper.hasGcamAsSeparateModule()) { 719 // Set the camera setting to default backfacing. 720 settingsManager.setToDefault(mAppController.getModuleScope(), 721 Keys.KEY_CAMERA_ID); 722 switchToGcamCapture(); 723 } else { 724 if (Keys.isHdrOn(settingsManager)) { 725 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_SCENE_MODE, 726 mCameraCapabilities.getStringifier().stringify( 727 CameraCapabilities.SceneMode.HDR)); 728 } else { 729 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_SCENE_MODE, 730 mCameraCapabilities.getStringifier().stringify( 731 CameraCapabilities.SceneMode.AUTO)); 732 } 733 updateParametersSceneMode(); 734 if (mCameraDevice != null) { 735 mCameraDevice.applySettings(mCameraSettings); 736 } 737 updateSceneMode(); 738 } 739 } 740 }; 741 742 private final View.OnClickListener mCancelCallback = new View.OnClickListener() { 743 @Override 744 public void onClick(View v) { 745 onCaptureCancelled(); 746 } 747 }; 748 749 private final View.OnClickListener mDoneCallback = new View.OnClickListener() { 750 @Override 751 public void onClick(View v) { 752 onCaptureDone(); 753 } 754 }; 755 756 private final View.OnClickListener mRetakeCallback = new View.OnClickListener() { 757 @Override 758 public void onClick(View v) { 759 mActivity.getCameraAppUI().transitionToIntentCaptureLayout(); 760 onCaptureRetake(); 761 } 762 }; 763 764 @Override 765 public void hardResetSettings(SettingsManager settingsManager) { 766 // PhotoModule should hard reset HDR+ to off, 767 // and HDR to off if HDR+ is supported. 768 settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR_PLUS, false); 769 if (GcamHelper.hasGcamAsSeparateModule()) { 770 settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR, false); 771 } 772 } 773 774 @Override 775 public HardwareSpec getHardwareSpec() { 776 return (mCameraSettings != null ? 777 new HardwareSpecImpl(getCameraProvider(), mCameraCapabilities) : null); 778 } 779 780 @Override 781 public CameraAppUI.BottomBarUISpec getBottomBarSpec() { 782 CameraAppUI.BottomBarUISpec bottomBarSpec = new CameraAppUI.BottomBarUISpec(); 783 784 bottomBarSpec.enableCamera = true; 785 bottomBarSpec.cameraCallback = mCameraCallback; 786 bottomBarSpec.enableFlash = !mAppController.getSettingsManager() 787 .getBoolean(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR); 788 bottomBarSpec.enableHdr = true; 789 bottomBarSpec.hdrCallback = mHdrPlusCallback; 790 bottomBarSpec.enableGridLines = true; 791 if (mCameraCapabilities != null) { 792 bottomBarSpec.enableExposureCompensation = true; 793 bottomBarSpec.exposureCompensationSetCallback = 794 new CameraAppUI.BottomBarUISpec.ExposureCompensationSetCallback() { 795 @Override 796 public void setExposure(int value) { 797 setExposureCompensation(value); 798 } 799 }; 800 bottomBarSpec.minExposureCompensation = 801 mCameraCapabilities.getMinExposureCompensation(); 802 bottomBarSpec.maxExposureCompensation = 803 mCameraCapabilities.getMaxExposureCompensation(); 804 bottomBarSpec.exposureCompensationStep = 805 mCameraCapabilities.getExposureCompensationStep(); 806 } 807 808 bottomBarSpec.enableSelfTimer = true; 809 bottomBarSpec.showSelfTimer = true; 810 811 if (isImageCaptureIntent()) { 812 bottomBarSpec.showCancel = true; 813 bottomBarSpec.cancelCallback = mCancelCallback; 814 bottomBarSpec.showDone = true; 815 bottomBarSpec.doneCallback = mDoneCallback; 816 bottomBarSpec.showRetake = true; 817 bottomBarSpec.retakeCallback = mRetakeCallback; 818 } 819 820 return bottomBarSpec; 821 } 822 823 // either open a new camera or switch cameras 824 private void openCameraCommon() { 825 mUI.onCameraOpened(mCameraCapabilities, mCameraSettings); 826 if (mIsImageCaptureIntent) { 827 // Set hdr plus to default: off. 828 SettingsManager settingsManager = mActivity.getSettingsManager(); 829 settingsManager.setToDefault(SettingsManager.SCOPE_GLOBAL, 830 Keys.KEY_CAMERA_HDR_PLUS); 831 } 832 updateSceneMode(); 833 } 834 835 @Override 836 public void updatePreviewAspectRatio(float aspectRatio) { 837 mAppController.updatePreviewAspectRatio(aspectRatio); 838 } 839 840 private void resetExposureCompensation() { 841 SettingsManager settingsManager = mActivity.getSettingsManager(); 842 if (settingsManager == null) { 843 Log.e(TAG, "Settings manager is null!"); 844 return; 845 } 846 settingsManager.setToDefault(mAppController.getCameraScope(), 847 Keys.KEY_EXPOSURE); 848 } 849 850 // Snapshots can only be taken after this is called. It should be called 851 // once only. We could have done these things in onCreate() but we want to 852 // make preview screen appear as soon as possible. 853 private void initializeFirstTime() { 854 if (mFirstTimeInitialized || mPaused) { 855 return; 856 } 857 858 mUI.initializeFirstTime(); 859 860 // We set the listener only when both service and shutterbutton 861 // are initialized. 862 getServices().getMemoryManager().addListener(this); 863 864 mNamedImages = new NamedImages(); 865 866 mFirstTimeInitialized = true; 867 addIdleHandler(); 868 869 mActivity.updateStorageSpaceAndHint(null); 870 } 871 872 // If the activity is paused and resumed, this method will be called in 873 // onResume. 874 private void initializeSecondTime() { 875 getServices().getMemoryManager().addListener(this); 876 mNamedImages = new NamedImages(); 877 mUI.initializeSecondTime(mCameraCapabilities, mCameraSettings); 878 } 879 880 private void addIdleHandler() { 881 MessageQueue queue = Looper.myQueue(); 882 queue.addIdleHandler(new MessageQueue.IdleHandler() { 883 @Override 884 public boolean queueIdle() { 885 Storage.ensureOSXCompatible(); 886 return false; 887 } 888 }); 889 } 890 891 @Override 892 public void startFaceDetection() { 893 if (mFaceDetectionStarted || mCameraDevice == null) { 894 return; 895 } 896 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) { 897 mFaceDetectionStarted = true; 898 mUI.onStartFaceDetection(mDisplayOrientation, isCameraFrontFacing()); 899 mCameraDevice.setFaceDetectionCallback(mHandler, mUI); 900 mCameraDevice.startFaceDetection(); 901 SessionStatsCollector.instance().faceScanActive(true); 902 } 903 } 904 905 @Override 906 public void stopFaceDetection() { 907 if (!mFaceDetectionStarted || mCameraDevice == null) { 908 return; 909 } 910 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) { 911 mFaceDetectionStarted = false; 912 mCameraDevice.setFaceDetectionCallback(null, null); 913 mCameraDevice.stopFaceDetection(); 914 mUI.clearFaces(); 915 SessionStatsCollector.instance().faceScanActive(false); 916 } 917 } 918 919 private final class ShutterCallback 920 implements CameraShutterCallback { 921 922 private final boolean mNeedsAnimation; 923 924 public ShutterCallback(boolean needsAnimation) { 925 mNeedsAnimation = needsAnimation; 926 } 927 928 @Override 929 public void onShutter(CameraProxy camera) { 930 mShutterCallbackTime = System.currentTimeMillis(); 931 mShutterLag = mShutterCallbackTime - mCaptureStartTime; 932 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms"); 933 if (mNeedsAnimation) { 934 mActivity.runOnUiThread(new Runnable() { 935 @Override 936 public void run() { 937 animateAfterShutter(); 938 } 939 }); 940 } 941 } 942 } 943 944 private final class PostViewPictureCallback 945 implements CameraPictureCallback { 946 @Override 947 public void onPictureTaken(byte[] data, CameraProxy camera) { 948 mPostViewPictureCallbackTime = System.currentTimeMillis(); 949 Log.v(TAG, "mShutterToPostViewCallbackTime = " 950 + (mPostViewPictureCallbackTime - mShutterCallbackTime) 951 + "ms"); 952 } 953 } 954 955 private final class RawPictureCallback 956 implements CameraPictureCallback { 957 @Override 958 public void onPictureTaken(byte[] rawData, CameraProxy camera) { 959 mRawPictureCallbackTime = System.currentTimeMillis(); 960 Log.v(TAG, "mShutterToRawCallbackTime = " 961 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms"); 962 } 963 } 964 965 private static class ResizeBundle { 966 byte[] jpegData; 967 float targetAspectRatio; 968 ExifInterface exif; 969 } 970 971 /** 972 * @return Cropped image if the target aspect ratio is larger than the jpeg 973 * aspect ratio on the long axis. The original jpeg otherwise. 974 */ 975 private ResizeBundle cropJpegDataToAspectRatio(ResizeBundle dataBundle) { 976 977 final byte[] jpegData = dataBundle.jpegData; 978 final ExifInterface exif = dataBundle.exif; 979 float targetAspectRatio = dataBundle.targetAspectRatio; 980 981 Bitmap original = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length); 982 int originalWidth = original.getWidth(); 983 int originalHeight = original.getHeight(); 984 int newWidth; 985 int newHeight; 986 987 if (originalWidth > originalHeight) { 988 newHeight = (int) (originalWidth / targetAspectRatio); 989 newWidth = originalWidth; 990 } else { 991 newWidth = (int) (originalHeight / targetAspectRatio); 992 newHeight = originalHeight; 993 } 994 int xOffset = (originalWidth - newWidth)/2; 995 int yOffset = (originalHeight - newHeight)/2; 996 997 if (xOffset < 0 || yOffset < 0) { 998 return dataBundle; 999 } 1000 1001 Bitmap resized = Bitmap.createBitmap(original,xOffset,yOffset,newWidth, newHeight); 1002 exif.setTagValue(ExifInterface.TAG_PIXEL_X_DIMENSION, new Integer(newWidth)); 1003 exif.setTagValue(ExifInterface.TAG_PIXEL_Y_DIMENSION, new Integer(newHeight)); 1004 1005 ByteArrayOutputStream stream = new ByteArrayOutputStream(); 1006 1007 resized.compress(Bitmap.CompressFormat.JPEG, 90, stream); 1008 dataBundle.jpegData = stream.toByteArray(); 1009 return dataBundle; 1010 } 1011 1012 private final class JpegPictureCallback 1013 implements CameraPictureCallback { 1014 Location mLocation; 1015 1016 public JpegPictureCallback(Location loc) { 1017 mLocation = loc; 1018 } 1019 1020 @Override 1021 public void onPictureTaken(final byte[] originalJpegData, final CameraProxy camera) { 1022 Log.i(TAG, "onPictureTaken"); 1023 mAppController.setShutterEnabled(true); 1024 if (mPaused) { 1025 return; 1026 } 1027 if (mIsImageCaptureIntent) { 1028 stopPreview(); 1029 } 1030 if (mSceneMode == CameraCapabilities.SceneMode.HDR) { 1031 mUI.setSwipingEnabled(true); 1032 } 1033 1034 mJpegPictureCallbackTime = System.currentTimeMillis(); 1035 // If postview callback has arrived, the captured image is displayed 1036 // in postview callback. If not, the captured image is displayed in 1037 // raw picture callback. 1038 if (mPostViewPictureCallbackTime != 0) { 1039 mShutterToPictureDisplayedTime = 1040 mPostViewPictureCallbackTime - mShutterCallbackTime; 1041 mPictureDisplayedToJpegCallbackTime = 1042 mJpegPictureCallbackTime - mPostViewPictureCallbackTime; 1043 } else { 1044 mShutterToPictureDisplayedTime = 1045 mRawPictureCallbackTime - mShutterCallbackTime; 1046 mPictureDisplayedToJpegCallbackTime = 1047 mJpegPictureCallbackTime - mRawPictureCallbackTime; 1048 } 1049 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = " 1050 + mPictureDisplayedToJpegCallbackTime + "ms"); 1051 1052 if (!mIsImageCaptureIntent) { 1053 setupPreview(); 1054 } 1055 1056 long now = System.currentTimeMillis(); 1057 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime; 1058 Log.v(TAG, "mJpegCallbackFinishTime = " + mJpegCallbackFinishTime + "ms"); 1059 mJpegPictureCallbackTime = 0; 1060 1061 final ExifInterface exif = Exif.getExif(originalJpegData); 1062 final NamedEntity name = mNamedImages.getNextNameEntity(); 1063 if (mShouldResizeTo16x9) { 1064 final ResizeBundle dataBundle = new ResizeBundle(); 1065 dataBundle.jpegData = originalJpegData; 1066 dataBundle.targetAspectRatio = ResolutionUtil.NEXUS_5_LARGE_16_BY_9_ASPECT_RATIO; 1067 dataBundle.exif = exif; 1068 new AsyncTask<ResizeBundle, Void, ResizeBundle>() { 1069 1070 @Override 1071 protected ResizeBundle doInBackground(ResizeBundle... resizeBundles) { 1072 return cropJpegDataToAspectRatio(resizeBundles[0]); 1073 } 1074 1075 @Override 1076 protected void onPostExecute(ResizeBundle result) { 1077 saveFinalPhoto(result.jpegData, name, result.exif, camera); 1078 } 1079 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, dataBundle); 1080 1081 } else { 1082 saveFinalPhoto(originalJpegData, name, exif, camera); 1083 } 1084 } 1085 1086 void saveFinalPhoto(final byte[] jpegData, NamedEntity name, final ExifInterface exif, 1087 CameraProxy camera) { 1088 int orientation = Exif.getOrientation(exif); 1089 1090 float zoomValue = 1.0f; 1091 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) { 1092 zoomValue = mCameraSettings.getCurrentZoomRatio(); 1093 } 1094 boolean hdrOn = CameraCapabilities.SceneMode.HDR == mSceneMode; 1095 String flashSetting = 1096 mActivity.getSettingsManager().getString(mAppController.getCameraScope(), 1097 Keys.KEY_FLASH_MODE); 1098 boolean gridLinesOn = Keys.areGridLinesOn(mActivity.getSettingsManager()); 1099 UsageStatistics.instance().photoCaptureDoneEvent( 1100 eventprotos.NavigationChange.Mode.PHOTO_CAPTURE, 1101 name.title + ".jpg", exif, 1102 isCameraFrontFacing(), hdrOn, zoomValue, flashSetting, gridLinesOn, 1103 (float) mTimerDuration, mShutterTouchCoordinate, mVolumeButtonClickedFlag); 1104 mShutterTouchCoordinate = null; 1105 mVolumeButtonClickedFlag = false; 1106 1107 if (!mIsImageCaptureIntent) { 1108 // Calculate the width and the height of the jpeg. 1109 Integer exifWidth = exif.getTagIntValue(ExifInterface.TAG_PIXEL_X_DIMENSION); 1110 Integer exifHeight = exif.getTagIntValue(ExifInterface.TAG_PIXEL_Y_DIMENSION); 1111 int width, height; 1112 if (mShouldResizeTo16x9 && exifWidth != null && exifHeight != null) { 1113 width = exifWidth; 1114 height = exifHeight; 1115 } else { 1116 Size s; 1117 s = mCameraSettings.getCurrentPhotoSize(); 1118 if ((mJpegRotation + orientation) % 180 == 0) { 1119 width = s.width(); 1120 height = s.height(); 1121 } else { 1122 width = s.height(); 1123 height = s.width(); 1124 } 1125 } 1126 String title = (name == null) ? null : name.title; 1127 long date = (name == null) ? -1 : name.date; 1128 1129 // Handle debug mode outputs 1130 if (mDebugUri != null) { 1131 // If using a debug uri, save jpeg there. 1132 saveToDebugUri(jpegData); 1133 1134 // Adjust the title of the debug image shown in mediastore. 1135 if (title != null) { 1136 title = DEBUG_IMAGE_PREFIX + title; 1137 } 1138 } 1139 1140 if (title == null) { 1141 Log.e(TAG, "Unbalanced name/data pair"); 1142 } else { 1143 if (date == -1) { 1144 date = mCaptureStartTime; 1145 } 1146 if (mHeading >= 0) { 1147 // heading direction has been updated by the sensor. 1148 ExifTag directionRefTag = exif.buildTag( 1149 ExifInterface.TAG_GPS_IMG_DIRECTION_REF, 1150 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION); 1151 ExifTag directionTag = exif.buildTag( 1152 ExifInterface.TAG_GPS_IMG_DIRECTION, 1153 new Rational(mHeading, 1)); 1154 exif.setTag(directionRefTag); 1155 exif.setTag(directionTag); 1156 } 1157 getServices().getMediaSaver().addImage( 1158 jpegData, title, date, mLocation, width, height, 1159 orientation, exif, mOnMediaSavedListener, mContentResolver); 1160 } 1161 // Animate capture with real jpeg data instead of a preview 1162 // frame. 1163 mUI.animateCapture(jpegData, orientation, mMirror); 1164 } else { 1165 mJpegImageData = jpegData; 1166 if (!mQuickCapture) { 1167 Log.v(TAG, "showing UI"); 1168 mUI.showCapturedImageForReview(jpegData, orientation, mMirror); 1169 } else { 1170 onCaptureDone(); 1171 } 1172 } 1173 1174 // Send the taken photo to remote shutter listeners, if any are 1175 // registered. 1176 getServices().getRemoteShutterListener().onPictureTaken(jpegData); 1177 1178 // Check this in advance of each shot so we don't add to shutter 1179 // latency. It's true that someone else could write to the SD card 1180 // in the mean time and fill it, but that could have happened 1181 // between the shutter press and saving the JPEG too. 1182 mActivity.updateStorageSpaceAndHint(null); 1183 } 1184 } 1185 1186 private final class AutoFocusCallback implements CameraAFCallback { 1187 @Override 1188 public void onAutoFocus(boolean focused, CameraProxy camera) { 1189 SessionStatsCollector.instance().autofocusResult(focused); 1190 if (mPaused) { 1191 return; 1192 } 1193 1194 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime; 1195 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms focused = "+focused); 1196 setCameraState(IDLE); 1197 mFocusManager.onAutoFocus(focused, false); 1198 } 1199 } 1200 1201 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 1202 private final class AutoFocusMoveCallback 1203 implements CameraAFMoveCallback { 1204 @Override 1205 public void onAutoFocusMoving( 1206 boolean moving, CameraProxy camera) { 1207 mFocusManager.onAutoFocusMoving(moving); 1208 SessionStatsCollector.instance().autofocusMoving(moving); 1209 } 1210 } 1211 1212 /** 1213 * This class is just a thread-safe queue for name,date holder objects. 1214 */ 1215 public static class NamedImages { 1216 private final Vector<NamedEntity> mQueue; 1217 1218 public NamedImages() { 1219 mQueue = new Vector<NamedEntity>(); 1220 } 1221 1222 public void nameNewImage(long date) { 1223 NamedEntity r = new NamedEntity(); 1224 r.title = CameraUtil.createJpegName(date); 1225 r.date = date; 1226 mQueue.add(r); 1227 } 1228 1229 public NamedEntity getNextNameEntity() { 1230 synchronized (mQueue) { 1231 if (!mQueue.isEmpty()) { 1232 return mQueue.remove(0); 1233 } 1234 } 1235 return null; 1236 } 1237 1238 public static class NamedEntity { 1239 public String title; 1240 public long date; 1241 } 1242 } 1243 1244 private void setCameraState(int state) { 1245 mCameraState = state; 1246 switch (state) { 1247 case PREVIEW_STOPPED: 1248 case SNAPSHOT_IN_PROGRESS: 1249 case SWITCHING_CAMERA: 1250 // TODO: Tell app UI to disable swipe 1251 break; 1252 case PhotoController.IDLE: 1253 // TODO: Tell app UI to enable swipe 1254 break; 1255 } 1256 } 1257 1258 private void animateAfterShutter() { 1259 // Only animate when in full screen capture mode 1260 // i.e. If monkey/a user swipes to the gallery during picture taking, 1261 // don't show animation 1262 if (!mIsImageCaptureIntent) { 1263 mUI.animateFlash(); 1264 } 1265 } 1266 1267 @Override 1268 public boolean capture() { 1269 Log.i(TAG, "capture"); 1270 // If we are already in the middle of taking a snapshot or the image 1271 // save request is full then ignore. 1272 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS 1273 || mCameraState == SWITCHING_CAMERA) { 1274 return false; 1275 } 1276 setCameraState(SNAPSHOT_IN_PROGRESS); 1277 1278 mCaptureStartTime = System.currentTimeMillis(); 1279 1280 mPostViewPictureCallbackTime = 0; 1281 mJpegImageData = null; 1282 1283 final boolean animateBefore = (mSceneMode == CameraCapabilities.SceneMode.HDR); 1284 1285 if (animateBefore) { 1286 animateAfterShutter(); 1287 } 1288 1289 Location loc = mActivity.getLocationManager().getCurrentLocation(); 1290 CameraUtil.setGpsParameters(mCameraSettings, loc); 1291 mCameraDevice.applySettings(mCameraSettings); 1292 1293 // Set JPEG orientation. Even if screen UI is locked in portrait, camera orientation should 1294 // still match device orientation (e.g., users should always get landscape photos while 1295 // capturing by putting device in landscape.) 1296 Characteristics info = mActivity.getCameraProvider().getCharacteristics(mCameraId); 1297 int sensorOrientation = info.getSensorOrientation(); 1298 int deviceOrientation = 1299 mAppController.getOrientationManager().getDeviceOrientation().getDegrees(); 1300 boolean isFrontCamera = info.isFacingFront(); 1301 mJpegRotation = 1302 CameraUtil.getImageRotation(sensorOrientation, deviceOrientation, isFrontCamera); 1303 mCameraDevice.setJpegOrientation(mJpegRotation); 1304 1305 mCameraDevice.takePicture(mHandler, 1306 new ShutterCallback(!animateBefore), 1307 mRawPictureCallback, mPostViewPictureCallback, 1308 new JpegPictureCallback(loc)); 1309 1310 mNamedImages.nameNewImage(mCaptureStartTime); 1311 1312 mFaceDetectionStarted = false; 1313 return true; 1314 } 1315 1316 @Override 1317 public void setFocusParameters() { 1318 setCameraParameters(UPDATE_PARAM_PREFERENCE); 1319 } 1320 1321 private void updateSceneMode() { 1322 // If scene mode is set, we cannot set flash mode, white balance, and 1323 // focus mode, instead, we read it from driver. Some devices don't have 1324 // any scene modes, so we must check both NO_SCENE_MODE in addition to 1325 // AUTO to check where there is no actual scene mode set. 1326 if (!(CameraCapabilities.SceneMode.AUTO == mSceneMode || 1327 CameraCapabilities.SceneMode.NO_SCENE_MODE == mSceneMode)) { 1328 overrideCameraSettings(mCameraSettings.getCurrentFlashMode(), 1329 mCameraSettings.getCurrentFocusMode()); 1330 } 1331 } 1332 1333 private void overrideCameraSettings(CameraCapabilities.FlashMode flashMode, 1334 CameraCapabilities.FocusMode focusMode) { 1335 CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier(); 1336 SettingsManager settingsManager = mActivity.getSettingsManager(); 1337 if (!CameraCapabilities.FlashMode.NO_FLASH.equals(flashMode)) { 1338 String flashModeString = stringifier.stringify(flashMode); 1339 Log.v(TAG, "override flash setting to: " + flashModeString); 1340 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_FLASH_MODE, 1341 flashModeString); 1342 } else { 1343 Log.v(TAG, "skip setting flash mode on override due to NO_FLASH"); 1344 } 1345 String focusModeString = stringifier.stringify(focusMode); 1346 Log.v(TAG, "override focus setting to: " + focusModeString); 1347 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_FOCUS_MODE, 1348 focusModeString); 1349 } 1350 1351 @Override 1352 public void onCameraAvailable(CameraProxy cameraProxy) { 1353 Log.i(TAG, "onCameraAvailable"); 1354 if (mPaused) { 1355 return; 1356 } 1357 mCameraDevice = cameraProxy; 1358 1359 initializeCapabilities(); 1360 1361 // Reset zoom value index. 1362 mZoomValue = 1.0f; 1363 if (mFocusManager == null) { 1364 initializeFocusManager(); 1365 } 1366 mFocusManager.updateCapabilities(mCameraCapabilities); 1367 1368 // Do camera parameter dependent initialization. 1369 mCameraSettings = mCameraDevice.getSettings(); 1370 1371 setCameraParameters(UPDATE_PARAM_ALL); 1372 // Set a listener which updates camera parameters based 1373 // on changed settings. 1374 SettingsManager settingsManager = mActivity.getSettingsManager(); 1375 settingsManager.addListener(this); 1376 mCameraPreviewParamsReady = true; 1377 1378 startPreview(); 1379 1380 onCameraOpened(); 1381 } 1382 1383 @Override 1384 public void onCaptureCancelled() { 1385 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent()); 1386 mActivity.finish(); 1387 } 1388 1389 @Override 1390 public void onCaptureRetake() { 1391 Log.i(TAG, "onCaptureRetake"); 1392 if (mPaused) { 1393 return; 1394 } 1395 mUI.hidePostCaptureAlert(); 1396 mUI.hideIntentReviewImageView(); 1397 setupPreview(); 1398 } 1399 1400 @Override 1401 public void onCaptureDone() { 1402 Log.i(TAG, "onCaptureDone"); 1403 if (mPaused) { 1404 return; 1405 } 1406 1407 byte[] data = mJpegImageData; 1408 1409 if (mCropValue == null) { 1410 // First handle the no crop case -- just return the value. If the 1411 // caller specifies a "save uri" then write the data to its 1412 // stream. Otherwise, pass back a scaled down version of the bitmap 1413 // directly in the extras. 1414 if (mSaveUri != null) { 1415 OutputStream outputStream = null; 1416 try { 1417 outputStream = mContentResolver.openOutputStream(mSaveUri); 1418 outputStream.write(data); 1419 outputStream.close(); 1420 1421 Log.v(TAG, "saved result to URI: " + mSaveUri); 1422 mActivity.setResultEx(Activity.RESULT_OK); 1423 mActivity.finish(); 1424 } catch (IOException ex) { 1425 Log.w(TAG, "exception saving result to URI: " + mSaveUri, ex); 1426 // ignore exception 1427 } finally { 1428 CameraUtil.closeSilently(outputStream); 1429 } 1430 } else { 1431 ExifInterface exif = Exif.getExif(data); 1432 int orientation = Exif.getOrientation(exif); 1433 Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024); 1434 bitmap = CameraUtil.rotate(bitmap, orientation); 1435 Log.v(TAG, "inlined bitmap into capture intent result"); 1436 mActivity.setResultEx(Activity.RESULT_OK, 1437 new Intent("inline-data").putExtra("data", bitmap)); 1438 mActivity.finish(); 1439 } 1440 } else { 1441 // Save the image to a temp file and invoke the cropper 1442 Uri tempUri = null; 1443 FileOutputStream tempStream = null; 1444 try { 1445 File path = mActivity.getFileStreamPath(sTempCropFilename); 1446 path.delete(); 1447 tempStream = mActivity.openFileOutput(sTempCropFilename, 0); 1448 tempStream.write(data); 1449 tempStream.close(); 1450 tempUri = Uri.fromFile(path); 1451 Log.v(TAG, "wrote temp file for cropping to: " + sTempCropFilename); 1452 } catch (FileNotFoundException ex) { 1453 Log.w(TAG, "error writing temp cropping file to: " + sTempCropFilename, ex); 1454 mActivity.setResultEx(Activity.RESULT_CANCELED); 1455 mActivity.finish(); 1456 return; 1457 } catch (IOException ex) { 1458 Log.w(TAG, "error writing temp cropping file to: " + sTempCropFilename, ex); 1459 mActivity.setResultEx(Activity.RESULT_CANCELED); 1460 mActivity.finish(); 1461 return; 1462 } finally { 1463 CameraUtil.closeSilently(tempStream); 1464 } 1465 1466 Bundle newExtras = new Bundle(); 1467 if (mCropValue.equals("circle")) { 1468 newExtras.putString("circleCrop", "true"); 1469 } 1470 if (mSaveUri != null) { 1471 Log.v(TAG, "setting output of cropped file to: " + mSaveUri); 1472 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri); 1473 } else { 1474 newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true); 1475 } 1476 if (mActivity.isSecureCamera()) { 1477 newExtras.putBoolean(CameraUtil.KEY_SHOW_WHEN_LOCKED, true); 1478 } 1479 1480 // TODO: Share this constant. 1481 final String CROP_ACTION = "com.android.camera.action.CROP"; 1482 Intent cropIntent = new Intent(CROP_ACTION); 1483 1484 cropIntent.setData(tempUri); 1485 cropIntent.putExtras(newExtras); 1486 Log.v(TAG, "starting CROP intent for capture"); 1487 mActivity.startActivityForResult(cropIntent, REQUEST_CROP); 1488 } 1489 } 1490 1491 @Override 1492 public void onShutterCoordinate(TouchCoordinate coord) { 1493 mShutterTouchCoordinate = coord; 1494 } 1495 1496 @Override 1497 public void onShutterButtonFocus(boolean pressed) { 1498 // Do nothing. We don't support half-press to focus anymore. 1499 } 1500 1501 @Override 1502 public void onShutterButtonClick() { 1503 if (mPaused || (mCameraState == SWITCHING_CAMERA) 1504 || (mCameraState == PREVIEW_STOPPED) 1505 || !mAppController.isShutterEnabled()) { 1506 mVolumeButtonClickedFlag = false; 1507 return; 1508 } 1509 1510 // Do not take the picture if there is not enough storage. 1511 if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) { 1512 Log.i(TAG, "Not enough space or storage not ready. remaining=" 1513 + mActivity.getStorageSpaceBytes()); 1514 mVolumeButtonClickedFlag = false; 1515 return; 1516 } 1517 Log.d(TAG, "onShutterButtonClick: mCameraState=" + mCameraState + 1518 " mVolumeButtonClickedFlag=" + mVolumeButtonClickedFlag); 1519 1520 mAppController.setShutterEnabled(false); 1521 1522 int countDownDuration = mActivity.getSettingsManager() 1523 .getInteger(SettingsManager.SCOPE_GLOBAL, Keys.KEY_COUNTDOWN_DURATION); 1524 mTimerDuration = countDownDuration; 1525 if (countDownDuration > 0) { 1526 // Start count down. 1527 mAppController.getCameraAppUI().transitionToCancel(); 1528 mAppController.getCameraAppUI().hideModeOptions(); 1529 mUI.startCountdown(countDownDuration); 1530 return; 1531 } else { 1532 focusAndCapture(); 1533 } 1534 } 1535 1536 private void focusAndCapture() { 1537 if (mSceneMode == CameraCapabilities.SceneMode.HDR) { 1538 mUI.setSwipingEnabled(false); 1539 } 1540 // If the user wants to do a snapshot while the previous one is still 1541 // in progress, remember the fact and do it after we finish the previous 1542 // one and re-start the preview. Snapshot in progress also includes the 1543 // state that autofocus is focusing and a picture will be taken when 1544 // focus callback arrives. 1545 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)) { 1546 if (!mIsImageCaptureIntent) { 1547 mSnapshotOnIdle = true; 1548 } 1549 return; 1550 } 1551 1552 mSnapshotOnIdle = false; 1553 mFocusManager.focusAndCapture(mCameraSettings.getCurrentFocusMode()); 1554 } 1555 1556 @Override 1557 public void onRemainingSecondsChanged(int remainingSeconds) { 1558 if (remainingSeconds == 1) { 1559 mCountdownSoundPlayer.play(R.raw.timer_final_second, 0.6f); 1560 } else if (remainingSeconds == 2 || remainingSeconds == 3) { 1561 mCountdownSoundPlayer.play(R.raw.timer_increment, 0.6f); 1562 } 1563 } 1564 1565 @Override 1566 public void onCountDownFinished() { 1567 mAppController.getCameraAppUI().transitionToCapture(); 1568 mAppController.getCameraAppUI().showModeOptions(); 1569 if (mPaused) { 1570 return; 1571 } 1572 focusAndCapture(); 1573 } 1574 1575 @Override 1576 public void resume() { 1577 mPaused = false; 1578 1579 mCountdownSoundPlayer.loadSound(R.raw.timer_final_second); 1580 mCountdownSoundPlayer.loadSound(R.raw.timer_increment); 1581 if (mFocusManager != null) { 1582 // If camera is not open when resume is called, focus manager will 1583 // not be initialized yet, in which case it will start listening to 1584 // preview area size change later in the initialization. 1585 mAppController.addPreviewAreaSizeChangedListener(mFocusManager); 1586 } 1587 mAppController.addPreviewAreaSizeChangedListener(mUI); 1588 1589 CameraProvider camProvider = mActivity.getCameraProvider(); 1590 if (camProvider == null) { 1591 // No camera provider, the Activity is destroyed already. 1592 return; 1593 } 1594 requestCameraOpen(); 1595 1596 mJpegPictureCallbackTime = 0; 1597 mZoomValue = 1.0f; 1598 1599 mOnResumeTime = SystemClock.uptimeMillis(); 1600 checkDisplayRotation(); 1601 1602 // If first time initialization is not finished, put it in the 1603 // message queue. 1604 if (!mFirstTimeInitialized) { 1605 mHandler.sendEmptyMessage(MSG_FIRST_TIME_INIT); 1606 } else { 1607 initializeSecondTime(); 1608 } 1609 1610 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); 1611 if (gsensor != null) { 1612 mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL); 1613 } 1614 1615 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); 1616 if (msensor != null) { 1617 mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL); 1618 } 1619 1620 getServices().getRemoteShutterListener().onModuleReady(this); 1621 SessionStatsCollector.instance().sessionActive(true); 1622 } 1623 1624 /** 1625 * @return Whether the currently active camera is front-facing. 1626 */ 1627 private boolean isCameraFrontFacing() { 1628 return mAppController.getCameraProvider().getCharacteristics(mCameraId) 1629 .isFacingFront(); 1630 } 1631 1632 /** 1633 * The focus manager is the first UI related element to get initialized, and 1634 * it requires the RenderOverlay, so initialize it here 1635 */ 1636 private void initializeFocusManager() { 1637 // Create FocusManager object. startPreview needs it. 1638 // if mFocusManager not null, reuse it 1639 // otherwise create a new instance 1640 if (mFocusManager != null) { 1641 mFocusManager.removeMessages(); 1642 } else { 1643 mMirror = isCameraFrontFacing(); 1644 String[] defaultFocusModesStrings = mActivity.getResources().getStringArray( 1645 R.array.pref_camera_focusmode_default_array); 1646 ArrayList<CameraCapabilities.FocusMode> defaultFocusModes = 1647 new ArrayList<CameraCapabilities.FocusMode>(); 1648 CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier(); 1649 for (String modeString : defaultFocusModesStrings) { 1650 CameraCapabilities.FocusMode mode = stringifier.focusModeFromString(modeString); 1651 if (mode != null) { 1652 defaultFocusModes.add(mode); 1653 } 1654 } 1655 mFocusManager = 1656 new FocusOverlayManager(mAppController, defaultFocusModes, 1657 mCameraCapabilities, this, mMirror, mActivity.getMainLooper(), 1658 mUI.getFocusRing()); 1659 mMotionManager = getServices().getMotionManager(); 1660 if (mMotionManager != null) { 1661 mMotionManager.addListener(mFocusManager); 1662 } 1663 } 1664 mAppController.addPreviewAreaSizeChangedListener(mFocusManager); 1665 } 1666 1667 /** 1668 * @return Whether we are resuming from within the lockscreen. 1669 */ 1670 private boolean isResumeFromLockscreen() { 1671 String action = mActivity.getIntent().getAction(); 1672 return (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action) 1673 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)); 1674 } 1675 1676 @Override 1677 public void pause() { 1678 Log.v(TAG, "pause"); 1679 mPaused = true; 1680 getServices().getRemoteShutterListener().onModuleExit(); 1681 SessionStatsCollector.instance().sessionActive(false); 1682 1683 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); 1684 if (gsensor != null) { 1685 mSensorManager.unregisterListener(this, gsensor); 1686 } 1687 1688 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); 1689 if (msensor != null) { 1690 mSensorManager.unregisterListener(this, msensor); 1691 } 1692 1693 // Reset the focus first. Camera CTS does not guarantee that 1694 // cancelAutoFocus is allowed after preview stops. 1695 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) { 1696 mCameraDevice.cancelAutoFocus(); 1697 } 1698 1699 // If the camera has not been opened asynchronously yet, 1700 // and startPreview hasn't been called, then this is a no-op. 1701 // (e.g. onResume -> onPause -> onResume). 1702 stopPreview(); 1703 cancelCountDown(); 1704 mCountdownSoundPlayer.unloadSound(R.raw.timer_final_second); 1705 mCountdownSoundPlayer.unloadSound(R.raw.timer_increment); 1706 1707 mNamedImages = null; 1708 // If we are in an image capture intent and has taken 1709 // a picture, we just clear it in onPause. 1710 mJpegImageData = null; 1711 1712 // Remove the messages and runnables in the queue. 1713 mHandler.removeCallbacksAndMessages(null); 1714 1715 if (mMotionManager != null) { 1716 mMotionManager.removeListener(mFocusManager); 1717 mMotionManager = null; 1718 } 1719 1720 closeCamera(); 1721 mActivity.enableKeepScreenOn(false); 1722 mUI.onPause(); 1723 1724 mPendingSwitchCameraId = -1; 1725 if (mFocusManager != null) { 1726 mFocusManager.removeMessages(); 1727 } 1728 getServices().getMemoryManager().removeListener(this); 1729 mAppController.removePreviewAreaSizeChangedListener(mFocusManager); 1730 mAppController.removePreviewAreaSizeChangedListener(mUI); 1731 1732 SettingsManager settingsManager = mActivity.getSettingsManager(); 1733 settingsManager.removeListener(this); 1734 } 1735 1736 @Override 1737 public void destroy() { 1738 mCountdownSoundPlayer.release(); 1739 } 1740 1741 @Override 1742 public void onLayoutOrientationChanged(boolean isLandscape) { 1743 setDisplayOrientation(); 1744 } 1745 1746 @Override 1747 public void updateCameraOrientation() { 1748 if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) { 1749 setDisplayOrientation(); 1750 } 1751 } 1752 1753 private boolean canTakePicture() { 1754 return isCameraIdle() 1755 && (mActivity.getStorageSpaceBytes() > Storage.LOW_STORAGE_THRESHOLD_BYTES); 1756 } 1757 1758 @Override 1759 public void autoFocus() { 1760 if (mCameraDevice == null) { 1761 return; 1762 } 1763 Log.v(TAG,"Starting auto focus"); 1764 mFocusStartTime = System.currentTimeMillis(); 1765 mCameraDevice.autoFocus(mHandler, mAutoFocusCallback); 1766 SessionStatsCollector.instance().autofocusManualTrigger(); 1767 setCameraState(FOCUSING); 1768 } 1769 1770 @Override 1771 public void cancelAutoFocus() { 1772 if (mCameraDevice == null) { 1773 return; 1774 } 1775 mCameraDevice.cancelAutoFocus(); 1776 setCameraState(IDLE); 1777 setCameraParameters(UPDATE_PARAM_PREFERENCE); 1778 } 1779 1780 @Override 1781 public void onSingleTapUp(View view, int x, int y) { 1782 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized 1783 || mCameraState == SNAPSHOT_IN_PROGRESS 1784 || mCameraState == SWITCHING_CAMERA 1785 || mCameraState == PREVIEW_STOPPED) { 1786 return; 1787 } 1788 1789 // Check if metering area or focus area is supported. 1790 if (!mFocusAreaSupported && !mMeteringAreaSupported) { 1791 return; 1792 } 1793 mFocusManager.onSingleTapUp(x, y); 1794 } 1795 1796 @Override 1797 public boolean onBackPressed() { 1798 return mUI.onBackPressed(); 1799 } 1800 1801 @Override 1802 public boolean onKeyDown(int keyCode, KeyEvent event) { 1803 switch (keyCode) { 1804 case KeyEvent.KEYCODE_VOLUME_UP: 1805 case KeyEvent.KEYCODE_VOLUME_DOWN: 1806 case KeyEvent.KEYCODE_FOCUS: 1807 if (/* TODO: mActivity.isInCameraApp() && */mFirstTimeInitialized && 1808 !mActivity.getCameraAppUI().isInIntentReview()) { 1809 if (event.getRepeatCount() == 0) { 1810 onShutterButtonFocus(true); 1811 } 1812 return true; 1813 } 1814 return false; 1815 case KeyEvent.KEYCODE_CAMERA: 1816 if (mFirstTimeInitialized && event.getRepeatCount() == 0) { 1817 onShutterButtonClick(); 1818 } 1819 return true; 1820 case KeyEvent.KEYCODE_DPAD_CENTER: 1821 // If we get a dpad center event without any focused view, move 1822 // the focus to the shutter button and press it. 1823 if (mFirstTimeInitialized && event.getRepeatCount() == 0) { 1824 // Start auto-focus immediately to reduce shutter lag. After 1825 // the shutter button gets the focus, onShutterButtonFocus() 1826 // will be called again but it is fine. 1827 onShutterButtonFocus(true); 1828 } 1829 return true; 1830 } 1831 return false; 1832 } 1833 1834 @Override 1835 public boolean onKeyUp(int keyCode, KeyEvent event) { 1836 switch (keyCode) { 1837 case KeyEvent.KEYCODE_VOLUME_UP: 1838 case KeyEvent.KEYCODE_VOLUME_DOWN: 1839 if (/* mActivity.isInCameraApp() && */mFirstTimeInitialized && 1840 !mActivity.getCameraAppUI().isInIntentReview()) { 1841 if (mUI.isCountingDown()) { 1842 cancelCountDown(); 1843 } else { 1844 mVolumeButtonClickedFlag = true; 1845 onShutterButtonClick(); 1846 } 1847 return true; 1848 } 1849 return false; 1850 case KeyEvent.KEYCODE_FOCUS: 1851 if (mFirstTimeInitialized) { 1852 onShutterButtonFocus(false); 1853 } 1854 return true; 1855 } 1856 return false; 1857 } 1858 1859 private void closeCamera() { 1860 if (mCameraDevice != null) { 1861 stopFaceDetection(); 1862 mCameraDevice.setZoomChangeListener(null); 1863 mCameraDevice.setFaceDetectionCallback(null, null); 1864 1865 mFaceDetectionStarted = false; 1866 mActivity.getCameraProvider().releaseCamera(mCameraDevice.getCameraId()); 1867 mCameraDevice = null; 1868 setCameraState(PREVIEW_STOPPED); 1869 mFocusManager.onCameraReleased(); 1870 } 1871 } 1872 1873 private void setDisplayOrientation() { 1874 mDisplayRotation = CameraUtil.getDisplayRotation(mActivity); 1875 Characteristics info = 1876 mActivity.getCameraProvider().getCharacteristics(mCameraId); 1877 mDisplayOrientation = info.getPreviewOrientation(mDisplayRotation); 1878 mUI.setDisplayOrientation(mDisplayOrientation); 1879 if (mFocusManager != null) { 1880 mFocusManager.setDisplayOrientation(mDisplayOrientation); 1881 } 1882 // Change the camera display orientation 1883 if (mCameraDevice != null) { 1884 mCameraDevice.setDisplayOrientation(mDisplayRotation); 1885 } 1886 Log.v(TAG, "setDisplayOrientation (screen:preview) " + 1887 mDisplayRotation + ":" + mDisplayOrientation); 1888 } 1889 1890 /** Only called by UI thread. */ 1891 private void setupPreview() { 1892 Log.i(TAG, "setupPreview"); 1893 mFocusManager.resetTouchFocus(); 1894 startPreview(); 1895 } 1896 1897 /** 1898 * Returns whether we can/should start the preview or not. 1899 */ 1900 private boolean checkPreviewPreconditions() { 1901 if (mPaused) { 1902 return false; 1903 } 1904 1905 if (mCameraDevice == null) { 1906 Log.w(TAG, "startPreview: camera device not ready yet."); 1907 return false; 1908 } 1909 1910 SurfaceTexture st = mActivity.getCameraAppUI().getSurfaceTexture(); 1911 if (st == null) { 1912 Log.w(TAG, "startPreview: surfaceTexture is not ready."); 1913 return false; 1914 } 1915 1916 if (!mCameraPreviewParamsReady) { 1917 Log.w(TAG, "startPreview: parameters for preview is not ready."); 1918 return false; 1919 } 1920 return true; 1921 } 1922 1923 /** 1924 * The start/stop preview should only run on the UI thread. 1925 */ 1926 private void startPreview() { 1927 if (mCameraDevice == null) { 1928 Log.i(TAG, "attempted to start preview before camera device"); 1929 // do nothing 1930 return; 1931 } 1932 1933 if (!checkPreviewPreconditions()) { 1934 return; 1935 } 1936 1937 setDisplayOrientation(); 1938 1939 if (!mSnapshotOnIdle) { 1940 // If the focus mode is continuous autofocus, call cancelAutoFocus 1941 // to resume it because it may have been paused by autoFocus call. 1942 if (mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()) == 1943 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE) { 1944 mCameraDevice.cancelAutoFocus(); 1945 } 1946 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB. 1947 } 1948 1949 // Nexus 4 must have picture size set to > 640x480 before other 1950 // parameters are set in setCameraParameters, b/18227551. This call to 1951 // updateParametersPictureSize should occur before setCameraParameters 1952 // to address the issue. 1953 updateParametersPictureSize(); 1954 1955 setCameraParameters(UPDATE_PARAM_ALL); 1956 1957 mCameraDevice.setPreviewTexture(mActivity.getCameraAppUI().getSurfaceTexture()); 1958 1959 Log.i(TAG, "startPreview"); 1960 // If we're using API2 in portability layers, don't use startPreviewWithCallback() 1961 // b/17576554 1962 CameraAgent.CameraStartPreviewCallback startPreviewCallback = 1963 new CameraAgent.CameraStartPreviewCallback() { 1964 @Override 1965 public void onPreviewStarted() { 1966 mFocusManager.onPreviewStarted(); 1967 PhotoModule.this.onPreviewStarted(); 1968 SessionStatsCollector.instance().previewActive(true); 1969 if (mSnapshotOnIdle) { 1970 mHandler.post(mDoSnapRunnable); 1971 } 1972 } 1973 }; 1974 if (GservicesHelper.useCamera2ApiThroughPortabilityLayer(mActivity)) { 1975 mCameraDevice.startPreview(); 1976 startPreviewCallback.onPreviewStarted(); 1977 } else { 1978 mCameraDevice.startPreviewWithCallback(new Handler(Looper.getMainLooper()), 1979 startPreviewCallback); 1980 } 1981 } 1982 1983 @Override 1984 public void stopPreview() { 1985 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) { 1986 Log.i(TAG, "stopPreview"); 1987 mCameraDevice.stopPreview(); 1988 mFaceDetectionStarted = false; 1989 } 1990 setCameraState(PREVIEW_STOPPED); 1991 if (mFocusManager != null) { 1992 mFocusManager.onPreviewStopped(); 1993 } 1994 SessionStatsCollector.instance().previewActive(false); 1995 } 1996 1997 @Override 1998 public void onSettingChanged(SettingsManager settingsManager, String key) { 1999 if (key.equals(Keys.KEY_FLASH_MODE)) { 2000 updateParametersFlashMode(); 2001 } 2002 if (key.equals(Keys.KEY_CAMERA_HDR)) { 2003 if (settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL, 2004 Keys.KEY_CAMERA_HDR)) { 2005 // HDR is on. 2006 mAppController.getButtonManager().disableButton(ButtonManager.BUTTON_FLASH); 2007 mFlashModeBeforeSceneMode = settingsManager.getString( 2008 mAppController.getCameraScope(), Keys.KEY_FLASH_MODE); 2009 } else { 2010 if (mFlashModeBeforeSceneMode != null) { 2011 settingsManager.set(mAppController.getCameraScope(), 2012 Keys.KEY_FLASH_MODE, 2013 mFlashModeBeforeSceneMode); 2014 updateParametersFlashMode(); 2015 mFlashModeBeforeSceneMode = null; 2016 } 2017 mAppController.getButtonManager().enableButton(ButtonManager.BUTTON_FLASH); 2018 } 2019 } 2020 2021 if (mCameraDevice != null) { 2022 mCameraDevice.applySettings(mCameraSettings); 2023 } 2024 } 2025 2026 private void updateCameraParametersInitialize() { 2027 // Reset preview frame rate to the maximum because it may be lowered by 2028 // video camera application. 2029 int[] fpsRange = CameraUtil.getPhotoPreviewFpsRange(mCameraCapabilities); 2030 if (fpsRange != null && fpsRange.length > 0) { 2031 mCameraSettings.setPreviewFpsRange(fpsRange[0], fpsRange[1]); 2032 } 2033 2034 mCameraSettings.setRecordingHintEnabled(false); 2035 2036 if (mCameraCapabilities.supports(CameraCapabilities.Feature.VIDEO_STABILIZATION)) { 2037 mCameraSettings.setVideoStabilization(false); 2038 } 2039 } 2040 2041 private void updateCameraParametersZoom() { 2042 // Set zoom. 2043 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) { 2044 mCameraSettings.setZoomRatio(mZoomValue); 2045 } 2046 } 2047 2048 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 2049 private void setAutoExposureLockIfSupported() { 2050 if (mAeLockSupported) { 2051 mCameraSettings.setAutoExposureLock(mFocusManager.getAeAwbLock()); 2052 } 2053 } 2054 2055 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 2056 private void setAutoWhiteBalanceLockIfSupported() { 2057 if (mAwbLockSupported) { 2058 mCameraSettings.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock()); 2059 } 2060 } 2061 2062 private void setFocusAreasIfSupported() { 2063 if (mFocusAreaSupported) { 2064 mCameraSettings.setFocusAreas(mFocusManager.getFocusAreas()); 2065 } 2066 } 2067 2068 private void setMeteringAreasIfSupported() { 2069 if (mMeteringAreaSupported) { 2070 mCameraSettings.setMeteringAreas(mFocusManager.getMeteringAreas()); 2071 } 2072 } 2073 2074 private void updateCameraParametersPreference() { 2075 // some monkey tests can get here when shutting the app down 2076 // make sure mCameraDevice is still valid, b/17580046 2077 if (mCameraDevice == null) { 2078 return; 2079 } 2080 2081 setAutoExposureLockIfSupported(); 2082 setAutoWhiteBalanceLockIfSupported(); 2083 setFocusAreasIfSupported(); 2084 setMeteringAreasIfSupported(); 2085 2086 // Initialize focus mode. 2087 mFocusManager.overrideFocusMode(null); 2088 mCameraSettings 2089 .setFocusMode(mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode())); 2090 SessionStatsCollector.instance().autofocusActive( 2091 mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()) == 2092 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE 2093 ); 2094 2095 // Set JPEG quality. 2096 updateParametersPictureQuality(); 2097 2098 // For the following settings, we need to check if the settings are 2099 // still supported by latest driver, if not, ignore the settings. 2100 2101 // Set exposure compensation 2102 updateParametersExposureCompensation(); 2103 2104 // Set the scene mode: also sets flash and white balance. 2105 updateParametersSceneMode(); 2106 2107 if (mContinuousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) { 2108 updateAutoFocusMoveCallback(); 2109 } 2110 } 2111 2112 /** 2113 * This method sets picture size parameters. Size parameters should only be 2114 * set when the preview is stopped, and so this method is only invoked in 2115 * {@link #startPreview()} just before starting the preview. 2116 */ 2117 private void updateParametersPictureSize() { 2118 if (mCameraDevice == null) { 2119 Log.w(TAG, "attempting to set picture size without caemra device"); 2120 return; 2121 } 2122 2123 SettingsManager settingsManager = mActivity.getSettingsManager(); 2124 String pictureSizeKey = isCameraFrontFacing() ? Keys.KEY_PICTURE_SIZE_FRONT 2125 : Keys.KEY_PICTURE_SIZE_BACK; 2126 String pictureSize = settingsManager.getString(SettingsManager.SCOPE_GLOBAL, 2127 pictureSizeKey); 2128 2129 List<Size> supported = mCameraCapabilities.getSupportedPhotoSizes(); 2130 CameraPictureSizesCacher.updateSizesForCamera(mAppController.getAndroidContext(), 2131 mCameraDevice.getCameraId(), supported); 2132 SettingsUtil.setCameraPictureSize(pictureSize, supported, mCameraSettings, 2133 mCameraDevice.getCameraId()); 2134 2135 Size size = SettingsUtil.getPhotoSize(pictureSize, supported, 2136 mCameraDevice.getCameraId()); 2137 if (ApiHelper.IS_NEXUS_5) { 2138 if (ResolutionUtil.NEXUS_5_LARGE_16_BY_9.equals(pictureSize)) { 2139 mShouldResizeTo16x9 = true; 2140 } else { 2141 mShouldResizeTo16x9 = false; 2142 } 2143 } 2144 2145 // Set a preview size that is closest to the viewfinder height and has 2146 // the right aspect ratio. 2147 List<Size> sizes = mCameraCapabilities.getSupportedPreviewSizes(); 2148 Size optimalSize = CameraUtil.getOptimalPreviewSize(mActivity, sizes, 2149 (double) size.width() / size.height()); 2150 Size original = mCameraSettings.getCurrentPreviewSize(); 2151 if (!optimalSize.equals(original)) { 2152 Log.v(TAG, "setting preview size. optimal: " + optimalSize + "original: " + original); 2153 mCameraSettings.setPreviewSize(optimalSize); 2154 2155 mCameraDevice.applySettings(mCameraSettings); 2156 mCameraSettings = mCameraDevice.getSettings(); 2157 } 2158 2159 if (optimalSize.width() != 0 && optimalSize.height() != 0) { 2160 Log.v(TAG, "updating aspect ratio"); 2161 mUI.updatePreviewAspectRatio((float) optimalSize.width() 2162 / (float) optimalSize.height()); 2163 } 2164 Log.d(TAG, "Preview size is " + optimalSize); 2165 } 2166 2167 private void updateParametersPictureQuality() { 2168 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId, 2169 CameraProfile.QUALITY_HIGH); 2170 mCameraSettings.setPhotoJpegCompressionQuality(jpegQuality); 2171 } 2172 2173 private void updateParametersExposureCompensation() { 2174 SettingsManager settingsManager = mActivity.getSettingsManager(); 2175 if (settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL, 2176 Keys.KEY_EXPOSURE_COMPENSATION_ENABLED)) { 2177 int value = settingsManager.getInteger(mAppController.getCameraScope(), 2178 Keys.KEY_EXPOSURE); 2179 int max = mCameraCapabilities.getMaxExposureCompensation(); 2180 int min = mCameraCapabilities.getMinExposureCompensation(); 2181 if (value >= min && value <= max) { 2182 mCameraSettings.setExposureCompensationIndex(value); 2183 } else { 2184 Log.w(TAG, "invalid exposure range: " + value); 2185 } 2186 } else { 2187 // If exposure compensation is not enabled, reset the exposure compensation value. 2188 setExposureCompensation(0); 2189 } 2190 2191 } 2192 2193 private void updateParametersSceneMode() { 2194 CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier(); 2195 SettingsManager settingsManager = mActivity.getSettingsManager(); 2196 2197 mSceneMode = stringifier. 2198 sceneModeFromString(settingsManager.getString(mAppController.getCameraScope(), 2199 Keys.KEY_SCENE_MODE)); 2200 if (mCameraCapabilities.supports(mSceneMode)) { 2201 if (mCameraSettings.getCurrentSceneMode() != mSceneMode) { 2202 mCameraSettings.setSceneMode(mSceneMode); 2203 2204 // Setting scene mode will change the settings of flash mode, 2205 // white balance, and focus mode. Here we read back the 2206 // parameters, so we can know those settings. 2207 mCameraDevice.applySettings(mCameraSettings); 2208 mCameraSettings = mCameraDevice.getSettings(); 2209 } 2210 } else { 2211 mSceneMode = mCameraSettings.getCurrentSceneMode(); 2212 if (mSceneMode == null) { 2213 mSceneMode = CameraCapabilities.SceneMode.AUTO; 2214 } 2215 } 2216 2217 if (CameraCapabilities.SceneMode.AUTO == mSceneMode) { 2218 // Set flash mode. 2219 updateParametersFlashMode(); 2220 2221 // Set focus mode. 2222 mFocusManager.overrideFocusMode(null); 2223 mCameraSettings.setFocusMode( 2224 mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode())); 2225 } else { 2226 mFocusManager.overrideFocusMode(mCameraSettings.getCurrentFocusMode()); 2227 } 2228 } 2229 2230 private void updateParametersFlashMode() { 2231 SettingsManager settingsManager = mActivity.getSettingsManager(); 2232 2233 CameraCapabilities.FlashMode flashMode = mCameraCapabilities.getStringifier() 2234 .flashModeFromString(settingsManager.getString(mAppController.getCameraScope(), 2235 Keys.KEY_FLASH_MODE)); 2236 if (mCameraCapabilities.supports(flashMode)) { 2237 mCameraSettings.setFlashMode(flashMode); 2238 } 2239 } 2240 2241 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 2242 private void updateAutoFocusMoveCallback() { 2243 if (mCameraDevice == null) { 2244 return; 2245 } 2246 if (mCameraSettings.getCurrentFocusMode() == 2247 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE) { 2248 mCameraDevice.setAutoFocusMoveCallback(mHandler, 2249 (CameraAFMoveCallback) mAutoFocusMoveCallback); 2250 } else { 2251 mCameraDevice.setAutoFocusMoveCallback(null, null); 2252 } 2253 } 2254 2255 /** 2256 * Sets the exposure compensation to the given value and also updates settings. 2257 * 2258 * @param value exposure compensation value to be set 2259 */ 2260 public void setExposureCompensation(int value) { 2261 int max = mCameraCapabilities.getMaxExposureCompensation(); 2262 int min = mCameraCapabilities.getMinExposureCompensation(); 2263 if (value >= min && value <= max) { 2264 mCameraSettings.setExposureCompensationIndex(value); 2265 SettingsManager settingsManager = mActivity.getSettingsManager(); 2266 settingsManager.set(mAppController.getCameraScope(), 2267 Keys.KEY_EXPOSURE, value); 2268 } else { 2269 Log.w(TAG, "invalid exposure range: " + value); 2270 } 2271 } 2272 2273 // We separate the parameters into several subsets, so we can update only 2274 // the subsets actually need updating. The PREFERENCE set needs extra 2275 // locking because the preference can be changed from GLThread as well. 2276 private void setCameraParameters(int updateSet) { 2277 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) { 2278 updateCameraParametersInitialize(); 2279 } 2280 2281 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) { 2282 updateCameraParametersZoom(); 2283 } 2284 2285 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) { 2286 updateCameraParametersPreference(); 2287 } 2288 2289 if (mCameraDevice != null) { 2290 mCameraDevice.applySettings(mCameraSettings); 2291 } 2292 } 2293 2294 // If the Camera is idle, update the parameters immediately, otherwise 2295 // accumulate them in mUpdateSet and update later. 2296 private void setCameraParametersWhenIdle(int additionalUpdateSet) { 2297 mUpdateSet |= additionalUpdateSet; 2298 if (mCameraDevice == null) { 2299 // We will update all the parameters when we open the device, so 2300 // we don't need to do anything now. 2301 mUpdateSet = 0; 2302 return; 2303 } else if (isCameraIdle()) { 2304 setCameraParameters(mUpdateSet); 2305 updateSceneMode(); 2306 mUpdateSet = 0; 2307 } else { 2308 if (!mHandler.hasMessages(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE)) { 2309 mHandler.sendEmptyMessageDelayed(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000); 2310 } 2311 } 2312 } 2313 2314 @Override 2315 public boolean isCameraIdle() { 2316 return (mCameraState == IDLE) || 2317 (mCameraState == PREVIEW_STOPPED) || 2318 ((mFocusManager != null) && mFocusManager.isFocusCompleted() 2319 && (mCameraState != SWITCHING_CAMERA)); 2320 } 2321 2322 @Override 2323 public boolean isImageCaptureIntent() { 2324 String action = mActivity.getIntent().getAction(); 2325 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action) 2326 || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action)); 2327 } 2328 2329 private void setupCaptureParams() { 2330 Bundle myExtras = mActivity.getIntent().getExtras(); 2331 if (myExtras != null) { 2332 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT); 2333 mCropValue = myExtras.getString("crop"); 2334 } 2335 } 2336 2337 private void initializeCapabilities() { 2338 mCameraCapabilities = mCameraDevice.getCapabilities(); 2339 mFocusAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA); 2340 mMeteringAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.METERING_AREA); 2341 mAeLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK); 2342 mAwbLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK); 2343 mContinuousFocusSupported = 2344 mCameraCapabilities.supports(CameraCapabilities.FocusMode.CONTINUOUS_PICTURE); 2345 } 2346 2347 @Override 2348 public void onZoomChanged(float ratio) { 2349 // Not useful to change zoom value when the activity is paused. 2350 if (mPaused) { 2351 return; 2352 } 2353 mZoomValue = ratio; 2354 if (mCameraSettings == null || mCameraDevice == null) { 2355 return; 2356 } 2357 // Set zoom parameters asynchronously 2358 mCameraSettings.setZoomRatio(mZoomValue); 2359 mCameraDevice.applySettings(mCameraSettings); 2360 } 2361 2362 @Override 2363 public int getCameraState() { 2364 return mCameraState; 2365 } 2366 2367 @Override 2368 public void onMemoryStateChanged(int state) { 2369 mAppController.setShutterEnabled(state == MemoryManager.STATE_OK); 2370 } 2371 2372 @Override 2373 public void onLowMemory() { 2374 // Not much we can do in the photo module. 2375 } 2376 2377 @Override 2378 public void onAccuracyChanged(Sensor sensor, int accuracy) { 2379 } 2380 2381 @Override 2382 public void onSensorChanged(SensorEvent event) { 2383 int type = event.sensor.getType(); 2384 float[] data; 2385 if (type == Sensor.TYPE_ACCELEROMETER) { 2386 data = mGData; 2387 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) { 2388 data = mMData; 2389 } else { 2390 // we should not be here. 2391 return; 2392 } 2393 for (int i = 0; i < 3; i++) { 2394 data[i] = event.values[i]; 2395 } 2396 float[] orientation = new float[3]; 2397 SensorManager.getRotationMatrix(mR, null, mGData, mMData); 2398 SensorManager.getOrientation(mR, orientation); 2399 mHeading = (int) (orientation[0] * 180f / Math.PI) % 360; 2400 if (mHeading < 0) { 2401 mHeading += 360; 2402 } 2403 } 2404 2405 // For debugging only. 2406 public void setDebugUri(Uri uri) { 2407 mDebugUri = uri; 2408 } 2409 2410 // For debugging only. 2411 private void saveToDebugUri(byte[] data) { 2412 if (mDebugUri != null) { 2413 OutputStream outputStream = null; 2414 try { 2415 outputStream = mContentResolver.openOutputStream(mDebugUri); 2416 outputStream.write(data); 2417 outputStream.close(); 2418 } catch (IOException e) { 2419 Log.e(TAG, "Exception while writing debug jpeg file", e); 2420 } finally { 2421 CameraUtil.closeSilently(outputStream); 2422 } 2423 } 2424 } 2425 2426 @Override 2427 public void onRemoteShutterPress() { 2428 mHandler.post(new Runnable() { 2429 @Override 2430 public void run() { 2431 focusAndCapture(); 2432 } 2433 }); 2434 } 2435} 2436