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