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