PhotoModule.java revision 54987e8d3af88214ce4f3aed0b5c4e9ded5b1392
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.app.AlertDialog; 22import android.content.ContentProviderClient; 23import android.content.ContentResolver; 24import android.content.DialogInterface; 25import android.content.Intent; 26import android.content.SharedPreferences.Editor; 27import android.content.res.Configuration; 28import android.graphics.Bitmap; 29import android.graphics.SurfaceTexture; 30import android.hardware.Camera.CameraInfo; 31import android.hardware.Camera.Face; 32import android.hardware.Camera.FaceDetectionListener; 33import android.hardware.Camera.Parameters; 34import android.hardware.Camera.PictureCallback; 35import android.hardware.Camera.Size; 36import android.location.Location; 37import android.media.CameraProfile; 38import android.net.Uri; 39import android.os.Bundle; 40import android.os.ConditionVariable; 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.util.Log; 48import android.view.Gravity; 49import android.view.KeyEvent; 50import android.view.LayoutInflater; 51import android.view.MotionEvent; 52import android.view.OrientationEventListener; 53import android.view.SurfaceHolder; 54import android.view.View; 55import android.view.View.OnClickListener; 56import android.view.ViewGroup; 57import android.view.WindowManager; 58import android.widget.FrameLayout; 59import android.widget.FrameLayout.LayoutParams; 60import android.widget.ImageView; 61import android.widget.Toast; 62 63import com.android.camera.CameraManager.CameraProxy; 64import com.android.camera.ui.AbstractSettingPopup; 65import com.android.camera.ui.CountDownView; 66import com.android.camera.ui.FaceView; 67import com.android.camera.ui.PieRenderer; 68import com.android.camera.ui.PopupManager; 69import com.android.camera.ui.PreviewSurfaceView; 70import com.android.camera.ui.RenderOverlay; 71import com.android.camera.ui.Rotatable; 72import com.android.camera.ui.RotateTextToast; 73import com.android.camera.ui.TwoStateImageView; 74import com.android.camera.ui.ZoomRenderer; 75import com.android.gallery3d.R; 76import com.android.gallery3d.common.ApiHelper; 77import com.android.gallery3d.filtershow.CropExtras; 78import com.android.gallery3d.filtershow.FilterShowActivity; 79 80import java.io.File; 81import java.io.FileNotFoundException; 82import java.io.FileOutputStream; 83import java.io.IOException; 84import java.io.OutputStream; 85import java.util.ArrayList; 86import java.util.Collections; 87import java.util.Formatter; 88import java.util.List; 89 90public class PhotoModule 91 implements CameraModule, 92 FocusOverlayManager.Listener, 93 CameraPreference.OnPreferenceChangedListener, 94 LocationManager.Listener, 95 PreviewFrameLayout.OnSizeChangedListener, 96 ShutterButton.OnShutterButtonListener, 97 SurfaceHolder.Callback, 98 PieRenderer.PieListener, 99 CountDownView.OnCountDownFinishedListener, 100 MediaSaveService.Listener { 101 102 private static final String TAG = "CAM_PhotoModule"; 103 104 // We number the request code from 1000 to avoid collision with Gallery. 105 private static final int REQUEST_CROP = 1000; 106 107 private static final int SETUP_PREVIEW = 1; 108 private static final int FIRST_TIME_INIT = 2; 109 private static final int CLEAR_SCREEN_DELAY = 3; 110 private static final int SET_CAMERA_PARAMETERS_WHEN_IDLE = 4; 111 private static final int CHECK_DISPLAY_ROTATION = 5; 112 private static final int SHOW_TAP_TO_FOCUS_TOAST = 6; 113 private static final int SWITCH_CAMERA = 7; 114 private static final int SWITCH_CAMERA_START_ANIMATION = 8; 115 private static final int CAMERA_OPEN_DONE = 9; 116 private static final int START_PREVIEW_DONE = 10; 117 private static final int OPEN_CAMERA_FAIL = 11; 118 private static final int CAMERA_DISABLED = 12; 119 120 // The subset of parameters we need to update in setCameraParameters(). 121 private static final int UPDATE_PARAM_INITIALIZE = 1; 122 private static final int UPDATE_PARAM_ZOOM = 2; 123 private static final int UPDATE_PARAM_PREFERENCE = 4; 124 private static final int UPDATE_PARAM_ALL = -1; 125 126 // This is the timeout to keep the camera in onPause for the first time 127 // after screen on if the activity is started from secure lock screen. 128 private static final int KEEP_CAMERA_TIMEOUT = 1000; // ms 129 130 // copied from Camera hierarchy 131 private CameraActivity mActivity; 132 private View mRootView; 133 private CameraProxy mCameraDevice; 134 private int mCameraId; 135 private Parameters mParameters; 136 private boolean mPaused; 137 private AbstractSettingPopup mPopup; 138 139 // these are only used by Camera 140 141 // The activity is going to switch to the specified camera id. This is 142 // needed because texture copy is done in GL thread. -1 means camera is not 143 // switching. 144 protected int mPendingSwitchCameraId = -1; 145 private boolean mOpenCameraFail; 146 private boolean mCameraDisabled; 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 mZoomMax; 156 private List<Integer> mZoomRatios; 157 158 private Parameters mInitialParams; 159 private boolean mFocusAreaSupported; 160 private boolean mMeteringAreaSupported; 161 private boolean mAeLockSupported; 162 private boolean mAwbLockSupported; 163 private boolean mContinousFocusSupported; 164 165 // The degrees of the device rotated clockwise from its natural orientation. 166 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN; 167 private ComboPreferences mPreferences; 168 169 private static final String sTempCropFilename = "crop-temp"; 170 171 private ContentProviderClient mMediaProviderClient; 172 private ShutterButton mShutterButton; 173 private boolean mFaceDetectionStarted = false; 174 175 private PreviewFrameLayout mPreviewFrameLayout; 176 private Object mSurfaceTexture; 177 private CountDownView mCountDownView; 178 179 // for API level 10 180 private PreviewSurfaceView mPreviewSurfaceView; 181 private volatile SurfaceHolder mCameraSurfaceHolder; 182 183 private FaceView mFaceView; 184 private RenderOverlay mRenderOverlay; 185 private Rotatable mReviewCancelButton; 186 private Rotatable mReviewDoneButton; 187 private View mReviewRetakeButton; 188 189 // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true. 190 private String mCropValue; 191 private Uri mSaveUri; 192 193 private View mMenu; 194 private View mBlocker; 195 196 // Small indicators which show the camera settings in the viewfinder. 197 private ImageView mExposureIndicator; 198 private ImageView mFlashIndicator; 199 private ImageView mSceneIndicator; 200 private ImageView mHdrIndicator; 201 // A view group that contains all the small indicators. 202 private View mOnScreenIndicators; 203 204 // We use a queue to generated names of the images to be used later 205 // when the image is ready to be saved. 206 private NamedImages mNamedImages; 207 208 private Runnable mDoSnapRunnable = new Runnable() { 209 @Override 210 public void run() { 211 onShutterButtonClick(); 212 } 213 }; 214 215 private final StringBuilder mBuilder = new StringBuilder(); 216 private final Formatter mFormatter = new Formatter(mBuilder); 217 private final Object[] mFormatterArgs = new Object[1]; 218 219 /** 220 * An unpublished intent flag requesting to return as soon as capturing 221 * is completed. 222 * 223 * TODO: consider publishing by moving into MediaStore. 224 */ 225 private static final String EXTRA_QUICK_CAPTURE = 226 "android.intent.extra.quickCapture"; 227 228 // The display rotation in degrees. This is only valid when mCameraState is 229 // not PREVIEW_STOPPED. 230 private int mDisplayRotation; 231 // The value for android.hardware.Camera.setDisplayOrientation. 232 private int mCameraDisplayOrientation; 233 // The value for UI components like indicators. 234 private int mDisplayOrientation; 235 // The value for android.hardware.Camera.Parameters.setRotation. 236 private int mJpegRotation; 237 private boolean mFirstTimeInitialized; 238 private boolean mIsImageCaptureIntent; 239 240 private static final int PREVIEW_STOPPED = 0; 241 private static final int IDLE = 1; // preview is active 242 // Focus is in progress. The exact focus state is in Focus.java. 243 private static final int FOCUSING = 2; 244 private static final int SNAPSHOT_IN_PROGRESS = 3; 245 // Switching between cameras. 246 private static final int SWITCHING_CAMERA = 4; 247 private int mCameraState = PREVIEW_STOPPED; 248 private boolean mSnapshotOnIdle = false; 249 250 private ContentResolver mContentResolver; 251 252 private LocationManager mLocationManager; 253 254 private final ShutterCallback mShutterCallback = new ShutterCallback(); 255 private final PostViewPictureCallback mPostViewPictureCallback = 256 new PostViewPictureCallback(); 257 private final RawPictureCallback mRawPictureCallback = 258 new RawPictureCallback(); 259 private final AutoFocusCallback mAutoFocusCallback = 260 new AutoFocusCallback(); 261 private final Object mAutoFocusMoveCallback = 262 ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK 263 ? new AutoFocusMoveCallback() 264 : null; 265 266 private final CameraErrorCallback mErrorCallback = new CameraErrorCallback(); 267 268 private long mFocusStartTime; 269 private long mShutterCallbackTime; 270 private long mPostViewPictureCallbackTime; 271 private long mRawPictureCallbackTime; 272 private long mJpegPictureCallbackTime; 273 private long mOnResumeTime; 274 private byte[] mJpegImageData; 275 276 // These latency time are for the CameraLatency test. 277 public long mAutoFocusTime; 278 public long mShutterLag; 279 public long mShutterToPictureDisplayedTime; 280 public long mPictureDisplayedToJpegCallbackTime; 281 public long mJpegCallbackFinishTime; 282 public long mCaptureStartTime; 283 284 // This handles everything about focus. 285 private FocusOverlayManager mFocusManager; 286 287 private PieRenderer mPieRenderer; 288 private PhotoController mPhotoControl; 289 290 private ZoomRenderer mZoomRenderer; 291 292 private String mSceneMode; 293 private Toast mNotSelectableToast; 294 295 private final Handler mHandler = new MainHandler(); 296 private PreferenceGroup mPreferenceGroup; 297 298 private boolean mQuickCapture; 299 300 CameraStartUpThread mCameraStartUpThread; 301 ConditionVariable mStartPreviewPrerequisiteReady = new ConditionVariable(); 302 303 private PreviewGestures mGestures; 304 305 private MediaSaveService.OnMediaSavedListener mOnMediaSavedListener = 306 new MediaSaveService.OnMediaSavedListener() { 307 @Override 308 public void onMediaSaved(Uri uri) { 309 if (uri != null) { 310 mActivity.addSecureAlbumItemIfNeeded(false, uri); 311 Util.broadcastNewPicture(mActivity, uri); 312 } 313 } 314 }; 315 316 // The purpose is not to block the main thread in onCreate and onResume. 317 private class CameraStartUpThread extends Thread { 318 private volatile boolean mCancelled; 319 320 public void cancel() { 321 mCancelled = true; 322 interrupt(); 323 } 324 325 public boolean isCanceled() { 326 return mCancelled; 327 } 328 329 @Override 330 public void run() { 331 try { 332 // We need to check whether the activity is paused before long 333 // operations to ensure that onPause() can be done ASAP. 334 if (mCancelled) return; 335 mCameraDevice = Util.openCamera(mActivity, mCameraId); 336 mParameters = mCameraDevice.getParameters(); 337 // Wait until all the initialization needed by startPreview are 338 // done. 339 mStartPreviewPrerequisiteReady.block(); 340 341 initializeCapabilities(); 342 if (mFocusManager == null) initializeFocusManager(); 343 if (mCancelled) return; 344 setCameraParameters(UPDATE_PARAM_ALL); 345 mHandler.sendEmptyMessage(CAMERA_OPEN_DONE); 346 if (mCancelled) return; 347 startPreview(); 348 mHandler.sendEmptyMessage(START_PREVIEW_DONE); 349 mOnResumeTime = SystemClock.uptimeMillis(); 350 mHandler.sendEmptyMessage(CHECK_DISPLAY_ROTATION); 351 } catch (CameraHardwareException e) { 352 mHandler.sendEmptyMessage(OPEN_CAMERA_FAIL); 353 } catch (CameraDisabledException e) { 354 mHandler.sendEmptyMessage(CAMERA_DISABLED); 355 } 356 } 357 } 358 359 /** 360 * This Handler is used to post message back onto the main thread of the 361 * application 362 */ 363 private class MainHandler extends Handler { 364 @Override 365 public void handleMessage(Message msg) { 366 switch (msg.what) { 367 case SETUP_PREVIEW: { 368 setupPreview(); 369 break; 370 } 371 372 case CLEAR_SCREEN_DELAY: { 373 mActivity.getWindow().clearFlags( 374 WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 375 break; 376 } 377 378 case FIRST_TIME_INIT: { 379 initializeFirstTime(); 380 break; 381 } 382 383 case SET_CAMERA_PARAMETERS_WHEN_IDLE: { 384 setCameraParametersWhenIdle(0); 385 break; 386 } 387 388 case CHECK_DISPLAY_ROTATION: { 389 // Set the display orientation if display rotation has changed. 390 // Sometimes this happens when the device is held upside 391 // down and camera app is opened. Rotation animation will 392 // take some time and the rotation value we have got may be 393 // wrong. Framework does not have a callback for this now. 394 if (Util.getDisplayRotation(mActivity) != mDisplayRotation) { 395 setDisplayOrientation(); 396 } 397 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) { 398 mHandler.sendEmptyMessageDelayed(CHECK_DISPLAY_ROTATION, 100); 399 } 400 break; 401 } 402 403 case SHOW_TAP_TO_FOCUS_TOAST: { 404 showTapToFocusToast(); 405 break; 406 } 407 408 case SWITCH_CAMERA: { 409 switchCamera(); 410 break; 411 } 412 413 case SWITCH_CAMERA_START_ANIMATION: { 414 ((CameraScreenNail) mActivity.mCameraScreenNail).animateSwitchCamera(); 415 break; 416 } 417 418 case CAMERA_OPEN_DONE: { 419 initializeAfterCameraOpen(); 420 break; 421 } 422 423 case START_PREVIEW_DONE: { 424 mCameraStartUpThread = null; 425 setCameraState(IDLE); 426 if (!ApiHelper.HAS_SURFACE_TEXTURE) { 427 // This may happen if surfaceCreated has arrived. 428 mCameraDevice.setPreviewDisplayAsync(mCameraSurfaceHolder); 429 } 430 startFaceDetection(); 431 locationFirstRun(); 432 break; 433 } 434 435 case OPEN_CAMERA_FAIL: { 436 mCameraStartUpThread = null; 437 mOpenCameraFail = true; 438 Util.showErrorAndFinish(mActivity, 439 R.string.cannot_connect_camera); 440 break; 441 } 442 443 case CAMERA_DISABLED: { 444 mCameraStartUpThread = null; 445 mCameraDisabled = true; 446 Util.showErrorAndFinish(mActivity, 447 R.string.camera_disabled); 448 break; 449 } 450 } 451 } 452 } 453 454 @Override 455 public void init(CameraActivity activity, View parent, boolean reuseNail) { 456 mActivity = activity; 457 mRootView = parent; 458 mPreferences = new ComboPreferences(mActivity); 459 CameraSettings.upgradeGlobalPreferences(mPreferences.getGlobal()); 460 mCameraId = getPreferredCameraId(mPreferences); 461 462 mContentResolver = mActivity.getContentResolver(); 463 464 // To reduce startup time, open the camera and start the preview in 465 // another thread. 466 mCameraStartUpThread = new CameraStartUpThread(); 467 mCameraStartUpThread.start(); 468 469 mActivity.getLayoutInflater().inflate(R.layout.photo_module, (ViewGroup) mRootView); 470 471 // Surface texture is from camera screen nail and startPreview needs it. 472 // This must be done before startPreview. 473 mIsImageCaptureIntent = isImageCaptureIntent(); 474 if (reuseNail) { 475 mActivity.reuseCameraScreenNail(!mIsImageCaptureIntent); 476 } else { 477 mActivity.createCameraScreenNail(!mIsImageCaptureIntent); 478 } 479 480 mPreferences.setLocalId(mActivity, mCameraId); 481 CameraSettings.upgradeLocalPreferences(mPreferences.getLocal()); 482 // we need to reset exposure for the preview 483 resetExposureCompensation(); 484 // Starting the preview needs preferences, camera screen nail, and 485 // focus area indicator. 486 mStartPreviewPrerequisiteReady.open(); 487 488 initializeControlByIntent(); 489 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false); 490 initializeMiscControls(); 491 mLocationManager = new LocationManager(mActivity, this); 492 initOnScreenIndicator(); 493 mCountDownView = (CountDownView) (mRootView.findViewById(R.id.count_down_to_capture)); 494 mCountDownView.setCountDownFinishedListener(this); 495 } 496 497 // Prompt the user to pick to record location for the very first run of 498 // camera only 499 private void locationFirstRun() { 500 if (RecordLocationPreference.isSet(mPreferences)) { 501 return; 502 } 503 if (mActivity.isSecureCamera()) return; 504 // Check if the back camera exists 505 int backCameraId = CameraHolder.instance().getBackCameraId(); 506 if (backCameraId == -1) { 507 // If there is no back camera, do not show the prompt. 508 return; 509 } 510 511 new AlertDialog.Builder(mActivity) 512 .setTitle(R.string.remember_location_title) 513 .setMessage(R.string.remember_location_prompt) 514 .setPositiveButton(R.string.remember_location_yes, new DialogInterface.OnClickListener() { 515 @Override 516 public void onClick(DialogInterface dialog, int arg1) { 517 setLocationPreference(RecordLocationPreference.VALUE_ON); 518 } 519 }) 520 .setNegativeButton(R.string.remember_location_no, new DialogInterface.OnClickListener() { 521 @Override 522 public void onClick(DialogInterface dialog, int arg1) { 523 dialog.cancel(); 524 } 525 }) 526 .setOnCancelListener(new DialogInterface.OnCancelListener() { 527 @Override 528 public void onCancel(DialogInterface dialog) { 529 setLocationPreference(RecordLocationPreference.VALUE_OFF); 530 } 531 }) 532 .show(); 533 } 534 535 private void setLocationPreference(String value) { 536 mPreferences.edit() 537 .putString(CameraSettings.KEY_RECORD_LOCATION, value) 538 .apply(); 539 // TODO: Fix this to use the actual onSharedPreferencesChanged listener 540 // instead of invoking manually 541 onSharedPreferenceChanged(); 542 } 543 544 private void initializeRenderOverlay() { 545 if (mPieRenderer != null) { 546 mRenderOverlay.addRenderer(mPieRenderer); 547 mFocusManager.setFocusRenderer(mPieRenderer); 548 } 549 if (mZoomRenderer != null) { 550 mRenderOverlay.addRenderer(mZoomRenderer); 551 } 552 if (mGestures != null) { 553 mGestures.clearTouchReceivers(); 554 mGestures.setRenderOverlay(mRenderOverlay); 555 mGestures.addTouchReceiver(mMenu); 556 mGestures.addTouchReceiver(mBlocker); 557 558 if (isImageCaptureIntent()) { 559 if (mReviewCancelButton != null) { 560 mGestures.addTouchReceiver((View) mReviewCancelButton); 561 } 562 if (mReviewDoneButton != null) { 563 mGestures.addTouchReceiver((View) mReviewDoneButton); 564 } 565 } 566 } 567 mRenderOverlay.requestLayout(); 568 } 569 570 private void initializeAfterCameraOpen() { 571 if (mPieRenderer == null) { 572 mPieRenderer = new PieRenderer(mActivity); 573 mPhotoControl = new PhotoController(mActivity, this, mPieRenderer); 574 mPhotoControl.setListener(this); 575 mPieRenderer.setPieListener(this); 576 } 577 if (mZoomRenderer == null) { 578 mZoomRenderer = new ZoomRenderer(mActivity); 579 } 580 if (mGestures == null) { 581 // this will handle gesture disambiguation and dispatching 582 mGestures = new PreviewGestures(mActivity, this, mZoomRenderer, mPieRenderer); 583 } 584 initializeRenderOverlay(); 585 initializePhotoControl(); 586 587 // These depend on camera parameters. 588 setPreviewFrameLayoutAspectRatio(); 589 mFocusManager.setPreviewSize(mPreviewFrameLayout.getWidth(), 590 mPreviewFrameLayout.getHeight()); 591 loadCameraPreferences(); 592 initializeZoom(); 593 updateOnScreenIndicators(); 594 showTapToFocusToastIfNeeded(); 595 onFullScreenChanged(mActivity.isInCameraApp()); 596 } 597 598 private void initializePhotoControl() { 599 loadCameraPreferences(); 600 if (mPhotoControl != null) { 601 mPhotoControl.initialize(mPreferenceGroup); 602 } 603 updateSceneModeUI(); 604 } 605 606 607 private void resetExposureCompensation() { 608 String value = mPreferences.getString(CameraSettings.KEY_EXPOSURE, 609 CameraSettings.EXPOSURE_DEFAULT_VALUE); 610 if (!CameraSettings.EXPOSURE_DEFAULT_VALUE.equals(value)) { 611 Editor editor = mPreferences.edit(); 612 editor.putString(CameraSettings.KEY_EXPOSURE, "0"); 613 editor.apply(); 614 } 615 } 616 617 private void keepMediaProviderInstance() { 618 // We want to keep a reference to MediaProvider in camera's lifecycle. 619 // TODO: Utilize mMediaProviderClient instance to replace 620 // ContentResolver calls. 621 if (mMediaProviderClient == null) { 622 mMediaProviderClient = mContentResolver 623 .acquireContentProviderClient(MediaStore.AUTHORITY); 624 } 625 } 626 627 // Snapshots can only be taken after this is called. It should be called 628 // once only. We could have done these things in onCreate() but we want to 629 // make preview screen appear as soon as possible. 630 private void initializeFirstTime() { 631 if (mFirstTimeInitialized) return; 632 633 // Initialize location service. 634 boolean recordLocation = RecordLocationPreference.get( 635 mPreferences, mContentResolver); 636 mLocationManager.recordLocation(recordLocation); 637 638 keepMediaProviderInstance(); 639 640 // Initialize shutter button. 641 mShutterButton = mActivity.getShutterButton(); 642 mShutterButton.setImageResource(R.drawable.btn_new_shutter); 643 mShutterButton.setOnShutterButtonListener(this); 644 mShutterButton.setVisibility(View.VISIBLE); 645 MediaSaveService s = mActivity.getMediaSaveService(); 646 // We set the listener only when both service and shutterbutton 647 // are initialized. 648 if (s != null) { 649 s.setListener(this); 650 } 651 652 mNamedImages = new NamedImages(); 653 654 mFirstTimeInitialized = true; 655 addIdleHandler(); 656 657 mActivity.updateStorageSpaceAndHint(); 658 } 659 660 private void showTapToFocusToastIfNeeded() { 661 // Show the tap to focus toast if this is the first start. 662 if (mFocusAreaSupported && 663 mPreferences.getBoolean(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, true)) { 664 // Delay the toast for one second to wait for orientation. 665 mHandler.sendEmptyMessageDelayed(SHOW_TAP_TO_FOCUS_TOAST, 1000); 666 } 667 } 668 669 private void addIdleHandler() { 670 MessageQueue queue = Looper.myQueue(); 671 queue.addIdleHandler(new MessageQueue.IdleHandler() { 672 @Override 673 public boolean queueIdle() { 674 Storage.ensureOSXCompatible(); 675 return false; 676 } 677 }); 678 } 679 680 // If the activity is paused and resumed, this method will be called in 681 // onResume. 682 private void initializeSecondTime() { 683 684 // Start location update if needed. 685 boolean recordLocation = RecordLocationPreference.get( 686 mPreferences, mContentResolver); 687 mLocationManager.recordLocation(recordLocation); 688 689 MediaSaveService s = mActivity.getMediaSaveService(); 690 if (s != null) { 691 s.setListener(this); 692 } 693 mNamedImages = new NamedImages(); 694 initializeZoom(); 695 keepMediaProviderInstance(); 696 hidePostCaptureAlert(); 697 698 if (mPhotoControl != null) { 699 mPhotoControl.reloadPreferences(); 700 } 701 } 702 703 private class ZoomChangeListener implements ZoomRenderer.OnZoomChangedListener { 704 @Override 705 public void onZoomValueChanged(int index) { 706 // Not useful to change zoom value when the activity is paused. 707 if (mPaused) return; 708 mZoomValue = index; 709 if (mParameters == null || mCameraDevice == null) return; 710 // Set zoom parameters asynchronously 711 mParameters.setZoom(mZoomValue); 712 mCameraDevice.setParametersAsync(mParameters); 713 if (mZoomRenderer != null) { 714 Parameters p = mCameraDevice.getParameters(); 715 mZoomRenderer.setZoomValue(mZoomRatios.get(p.getZoom())); 716 } 717 } 718 719 @Override 720 public void onZoomStart() { 721 if (mPieRenderer != null) { 722 mPieRenderer.setBlockFocus(true); 723 } 724 } 725 726 @Override 727 public void onZoomEnd() { 728 if (mPieRenderer != null) { 729 mPieRenderer.setBlockFocus(false); 730 } 731 } 732 } 733 734 private void initializeZoom() { 735 if ((mParameters == null) || !mParameters.isZoomSupported() 736 || (mZoomRenderer == null)) return; 737 mZoomMax = mParameters.getMaxZoom(); 738 mZoomRatios = mParameters.getZoomRatios(); 739 // Currently we use immediate zoom for fast zooming to get better UX and 740 // there is no plan to take advantage of the smooth zoom. 741 if (mZoomRenderer != null) { 742 mZoomRenderer.setZoomMax(mZoomMax); 743 mZoomRenderer.setZoom(mParameters.getZoom()); 744 mZoomRenderer.setZoomValue(mZoomRatios.get(mParameters.getZoom())); 745 mZoomRenderer.setOnZoomChangeListener(new ZoomChangeListener()); 746 } 747 } 748 749 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH) 750 @Override 751 public void startFaceDetection() { 752 if (!ApiHelper.HAS_FACE_DETECTION) return; 753 if (mFaceDetectionStarted) return; 754 if (mParameters.getMaxNumDetectedFaces() > 0) { 755 mFaceDetectionStarted = true; 756 mFaceView.clear(); 757 mFaceView.setVisibility(View.VISIBLE); 758 mFaceView.setDisplayOrientation(mDisplayOrientation); 759 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId]; 760 mFaceView.setMirror(info.facing == CameraInfo.CAMERA_FACING_FRONT); 761 mFaceView.resume(); 762 mFocusManager.setFaceView(mFaceView); 763 mCameraDevice.setFaceDetectionListener(new FaceDetectionListener() { 764 @Override 765 public void onFaceDetection(Face[] faces, android.hardware.Camera camera) { 766 mFaceView.setFaces(faces); 767 } 768 }); 769 mCameraDevice.startFaceDetection(); 770 } 771 } 772 773 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH) 774 @Override 775 public void stopFaceDetection() { 776 if (!ApiHelper.HAS_FACE_DETECTION) return; 777 if (!mFaceDetectionStarted) return; 778 if (mParameters.getMaxNumDetectedFaces() > 0) { 779 mFaceDetectionStarted = false; 780 mCameraDevice.setFaceDetectionListener(null); 781 mCameraDevice.stopFaceDetection(); 782 if (mFaceView != null) mFaceView.clear(); 783 } 784 } 785 786 @Override 787 public boolean dispatchTouchEvent(MotionEvent m) { 788 if (mCameraState == SWITCHING_CAMERA) return true; 789 if (mPopup != null) { 790 return mActivity.superDispatchTouchEvent(m); 791 } else if (mGestures != null && mRenderOverlay != null) { 792 return mGestures.dispatchTouch(m); 793 } 794 return false; 795 } 796 797 private void initOnScreenIndicator() { 798 mOnScreenIndicators = mRootView.findViewById(R.id.on_screen_indicators); 799 mExposureIndicator = (ImageView) mOnScreenIndicators.findViewById(R.id.menu_exposure_indicator); 800 mFlashIndicator = (ImageView) mOnScreenIndicators.findViewById(R.id.menu_flash_indicator); 801 mSceneIndicator = (ImageView) mOnScreenIndicators.findViewById(R.id.menu_scenemode_indicator); 802 mHdrIndicator = (ImageView) mOnScreenIndicators.findViewById(R.id.menu_hdr_indicator); 803 } 804 805 @Override 806 public void showGpsOnScreenIndicator(boolean hasSignal) { } 807 808 @Override 809 public void hideGpsOnScreenIndicator() { } 810 811 private void updateExposureOnScreenIndicator(int value) { 812 if (mExposureIndicator == null) { 813 return; 814 } 815 int id = 0; 816 float step = mParameters.getExposureCompensationStep(); 817 value = (int) Math.round(value * step); 818 switch(value) { 819 case -3: 820 id = R.drawable.ic_indicator_ev_n3; 821 break; 822 case -2: 823 id = R.drawable.ic_indicator_ev_n2; 824 break; 825 case -1: 826 id = R.drawable.ic_indicator_ev_n1; 827 break; 828 case 0: 829 id = R.drawable.ic_indicator_ev_0; 830 break; 831 case 1: 832 id = R.drawable.ic_indicator_ev_p1; 833 break; 834 case 2: 835 id = R.drawable.ic_indicator_ev_p2; 836 break; 837 case 3: 838 id = R.drawable.ic_indicator_ev_p3; 839 break; 840 } 841 mExposureIndicator.setImageResource(id); 842 843 } 844 845 private void updateFlashOnScreenIndicator(String value) { 846 if (mFlashIndicator == null) { 847 return; 848 } 849 if (value == null || Parameters.FLASH_MODE_OFF.equals(value)) { 850 mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_off); 851 } else { 852 if (Parameters.FLASH_MODE_AUTO.equals(value)) { 853 mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_auto); 854 } else if (Parameters.FLASH_MODE_ON.equals(value)) { 855 mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_on); 856 } else { 857 mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_off); 858 } 859 } 860 } 861 862 private void updateSceneOnScreenIndicator(String value) { 863 if (mSceneIndicator == null) { 864 return; 865 } 866 if ((value == null) || Parameters.SCENE_MODE_AUTO.equals(value) 867 || Parameters.SCENE_MODE_HDR.equals(value)) { 868 mSceneIndicator.setImageResource(R.drawable.ic_indicator_sce_off); 869 } else { 870 mSceneIndicator.setImageResource(R.drawable.ic_indicator_sce_on); 871 } 872 } 873 874 private void updateHdrOnScreenIndicator(String value) { 875 if (mHdrIndicator == null) { 876 return; 877 } 878 if ((value != null) && Parameters.SCENE_MODE_HDR.equals(value)) { 879 mHdrIndicator.setImageResource(R.drawable.ic_indicator_hdr_on); 880 } else { 881 mHdrIndicator.setImageResource(R.drawable.ic_indicator_hdr_off); 882 } 883 } 884 885 private void updateOnScreenIndicators() { 886 if (mParameters == null) return; 887 updateSceneOnScreenIndicator(mParameters.getSceneMode()); 888 updateExposureOnScreenIndicator(CameraSettings.readExposure(mPreferences)); 889 updateFlashOnScreenIndicator(mParameters.getFlashMode()); 890 updateHdrOnScreenIndicator(mParameters.getSceneMode()); 891 } 892 893 private final class ShutterCallback 894 implements android.hardware.Camera.ShutterCallback { 895 @Override 896 public void onShutter() { 897 mShutterCallbackTime = System.currentTimeMillis(); 898 mShutterLag = mShutterCallbackTime - mCaptureStartTime; 899 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms"); 900 } 901 } 902 903 private final class PostViewPictureCallback implements PictureCallback { 904 @Override 905 public void onPictureTaken( 906 byte [] data, android.hardware.Camera camera) { 907 mPostViewPictureCallbackTime = System.currentTimeMillis(); 908 Log.v(TAG, "mShutterToPostViewCallbackTime = " 909 + (mPostViewPictureCallbackTime - mShutterCallbackTime) 910 + "ms"); 911 } 912 } 913 914 private final class RawPictureCallback implements PictureCallback { 915 @Override 916 public void onPictureTaken( 917 byte [] rawData, android.hardware.Camera camera) { 918 mRawPictureCallbackTime = System.currentTimeMillis(); 919 Log.v(TAG, "mShutterToRawCallbackTime = " 920 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms"); 921 } 922 } 923 924 private final class JpegPictureCallback implements PictureCallback { 925 Location mLocation; 926 927 public JpegPictureCallback(Location loc) { 928 mLocation = loc; 929 } 930 931 @Override 932 public void onPictureTaken( 933 final byte [] jpegData, final android.hardware.Camera camera) { 934 if (mPaused) { 935 return; 936 } 937 if (mSceneMode == Util.SCENE_MODE_HDR) { 938 mActivity.showSwitcher(); 939 mActivity.setSwipingEnabled(true); 940 } 941 942 mJpegPictureCallbackTime = System.currentTimeMillis(); 943 // If postview callback has arrived, the captured image is displayed 944 // in postview callback. If not, the captured image is displayed in 945 // raw picture callback. 946 if (mPostViewPictureCallbackTime != 0) { 947 mShutterToPictureDisplayedTime = 948 mPostViewPictureCallbackTime - mShutterCallbackTime; 949 mPictureDisplayedToJpegCallbackTime = 950 mJpegPictureCallbackTime - mPostViewPictureCallbackTime; 951 } else { 952 mShutterToPictureDisplayedTime = 953 mRawPictureCallbackTime - mShutterCallbackTime; 954 mPictureDisplayedToJpegCallbackTime = 955 mJpegPictureCallbackTime - mRawPictureCallbackTime; 956 } 957 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = " 958 + mPictureDisplayedToJpegCallbackTime + "ms"); 959 960 // Only animate when in full screen capture mode 961 // i.e. If monkey/a user swipes to the gallery during picture taking, 962 // don't show animation 963 if (ApiHelper.HAS_SURFACE_TEXTURE && !mIsImageCaptureIntent 964 && mActivity.mShowCameraAppView) { 965 // Finish capture animation 966 ((CameraScreenNail) mActivity.mCameraScreenNail).animateSlide(); 967 } 968 mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden. 969 if (!mIsImageCaptureIntent) { 970 if (ApiHelper.CAN_START_PREVIEW_IN_JPEG_CALLBACK) { 971 setupPreview(); 972 } else { 973 // Camera HAL of some devices have a bug. Starting preview 974 // immediately after taking a picture will fail. Wait some 975 // time before starting the preview. 976 mHandler.sendEmptyMessageDelayed(SETUP_PREVIEW, 300); 977 } 978 } 979 980 if (!mIsImageCaptureIntent) { 981 // Calculate the width and the height of the jpeg. 982 Size s = mParameters.getPictureSize(); 983 int orientation = Exif.getOrientation(jpegData); 984 int width, height; 985 if ((mJpegRotation + orientation) % 180 == 0) { 986 width = s.width; 987 height = s.height; 988 } else { 989 width = s.height; 990 height = s.width; 991 } 992 String title = mNamedImages.getTitle(); 993 long date = mNamedImages.getDate(); 994 if (title == null) { 995 Log.e(TAG, "Unbalanced name/data pair"); 996 } else { 997 if (date == -1) date = mCaptureStartTime; 998 mActivity.getMediaSaveService().addImage( 999 jpegData, title, date, mLocation, width, height, 1000 orientation, mOnMediaSavedListener, mContentResolver); 1001 } 1002 } else { 1003 mJpegImageData = jpegData; 1004 if (!mQuickCapture) { 1005 showPostCaptureAlert(); 1006 } else { 1007 doAttach(); 1008 } 1009 } 1010 1011 // Check this in advance of each shot so we don't add to shutter 1012 // latency. It's true that someone else could write to the SD card in 1013 // the mean time and fill it, but that could have happened between the 1014 // shutter press and saving the JPEG too. 1015 mActivity.updateStorageSpaceAndHint(); 1016 1017 long now = System.currentTimeMillis(); 1018 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime; 1019 Log.v(TAG, "mJpegCallbackFinishTime = " 1020 + mJpegCallbackFinishTime + "ms"); 1021 mJpegPictureCallbackTime = 0; 1022 } 1023 } 1024 1025 private final class AutoFocusCallback 1026 implements android.hardware.Camera.AutoFocusCallback { 1027 @Override 1028 public void onAutoFocus( 1029 boolean focused, android.hardware.Camera camera) { 1030 if (mPaused) return; 1031 1032 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime; 1033 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms"); 1034 setCameraState(IDLE); 1035 mFocusManager.onAutoFocus(focused, mShutterButton.isPressed()); 1036 } 1037 } 1038 1039 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN) 1040 private final class AutoFocusMoveCallback 1041 implements android.hardware.Camera.AutoFocusMoveCallback { 1042 @Override 1043 public void onAutoFocusMoving( 1044 boolean moving, android.hardware.Camera camera) { 1045 mFocusManager.onAutoFocusMoving(moving); 1046 } 1047 } 1048 1049 private static class NamedImages { 1050 private ArrayList<NamedEntity> mQueue; 1051 private boolean mStop; 1052 private NamedEntity mNamedEntity; 1053 1054 public NamedImages() { 1055 mQueue = new ArrayList<NamedEntity>(); 1056 } 1057 1058 public void nameNewImage(ContentResolver resolver, long date) { 1059 NamedEntity r = new NamedEntity(); 1060 r.title = Util.createJpegName(date); 1061 r.date = date; 1062 mQueue.add(r); 1063 } 1064 1065 public String getTitle() { 1066 if (mQueue.isEmpty()) { 1067 mNamedEntity = null; 1068 return null; 1069 } 1070 mNamedEntity = mQueue.get(0); 1071 mQueue.remove(0); 1072 1073 return mNamedEntity.title; 1074 } 1075 1076 // Must be called after getTitle(). 1077 public long getDate() { 1078 if (mNamedEntity == null) return -1; 1079 return mNamedEntity.date; 1080 } 1081 1082 private static class NamedEntity { 1083 String title; 1084 long date; 1085 } 1086 } 1087 1088 private void setCameraState(int state) { 1089 mCameraState = state; 1090 switch (state) { 1091 case PREVIEW_STOPPED: 1092 case SNAPSHOT_IN_PROGRESS: 1093 case FOCUSING: 1094 case SWITCHING_CAMERA: 1095 if (mGestures != null) mGestures.setEnabled(false); 1096 break; 1097 case IDLE: 1098 if (mGestures != null && mActivity.mShowCameraAppView) { 1099 // Enable gestures only when the camera app view is visible 1100 mGestures.setEnabled(true); 1101 } 1102 break; 1103 } 1104 } 1105 1106 private void animateFlash() { 1107 // Only animate when in full screen capture mode 1108 // i.e. If monkey/a user swipes to the gallery during picture taking, 1109 // don't show animation 1110 if (ApiHelper.HAS_SURFACE_TEXTURE && !mIsImageCaptureIntent 1111 && mActivity.mShowCameraAppView) { 1112 // Start capture animation. 1113 ((CameraScreenNail) mActivity.mCameraScreenNail).animateFlash(mDisplayRotation); 1114 } 1115 } 1116 1117 @Override 1118 public boolean capture() { 1119 // If we are already in the middle of taking a snapshot or the image save request 1120 // is full then ignore. 1121 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS 1122 || mCameraState == SWITCHING_CAMERA 1123 || mActivity.getMediaSaveService().isQueueFull()) { 1124 return false; 1125 } 1126 mCaptureStartTime = System.currentTimeMillis(); 1127 mPostViewPictureCallbackTime = 0; 1128 mJpegImageData = null; 1129 1130 final boolean animateBefore = (mSceneMode == Util.SCENE_MODE_HDR); 1131 1132 if (animateBefore) { 1133 animateFlash(); 1134 } 1135 1136 // Set rotation and gps data. 1137 mJpegRotation = Util.getJpegRotation(mCameraId, mOrientation); 1138 mParameters.setRotation(mJpegRotation); 1139 Location loc = mLocationManager.getCurrentLocation(); 1140 Util.setGpsParameters(mParameters, loc); 1141 mCameraDevice.setParameters(mParameters); 1142 1143 mCameraDevice.takePicture2(mShutterCallback, mRawPictureCallback, 1144 mPostViewPictureCallback, new JpegPictureCallback(loc), 1145 mCameraState, mFocusManager.getFocusState()); 1146 1147 if (!animateBefore) { 1148 animateFlash(); 1149 } 1150 1151 mNamedImages.nameNewImage(mContentResolver, mCaptureStartTime); 1152 1153 mFaceDetectionStarted = false; 1154 setCameraState(SNAPSHOT_IN_PROGRESS); 1155 return true; 1156 } 1157 1158 @Override 1159 public void setFocusParameters() { 1160 setCameraParameters(UPDATE_PARAM_PREFERENCE); 1161 } 1162 1163 private int getPreferredCameraId(ComboPreferences preferences) { 1164 int intentCameraId = Util.getCameraFacingIntentExtras(mActivity); 1165 if (intentCameraId != -1) { 1166 // Testing purpose. Launch a specific camera through the intent 1167 // extras. 1168 return intentCameraId; 1169 } else { 1170 return CameraSettings.readPreferredCameraId(preferences); 1171 } 1172 } 1173 1174 private void setShowMenu(boolean show) { 1175 if (mOnScreenIndicators != null) { 1176 mOnScreenIndicators.setVisibility(show ? View.VISIBLE : View.GONE); 1177 } 1178 if (mMenu != null) { 1179 mMenu.setVisibility(show ? View.VISIBLE : View.GONE); 1180 } 1181 } 1182 1183 @Override 1184 public void onFullScreenChanged(boolean full) { 1185 if (mFaceView != null) { 1186 mFaceView.setBlockDraw(!full); 1187 } 1188 if (mPopup != null) { 1189 dismissPopup(false, full); 1190 } 1191 if (mGestures != null) { 1192 mGestures.setEnabled(full); 1193 } 1194 if (mRenderOverlay != null) { 1195 // this can not happen in capture mode 1196 mRenderOverlay.setVisibility(full ? View.VISIBLE : View.GONE); 1197 } 1198 if (mPieRenderer != null) { 1199 mPieRenderer.setBlockFocus(!full); 1200 } 1201 setShowMenu(full); 1202 if (mBlocker != null) { 1203 mBlocker.setVisibility(full ? View.VISIBLE : View.GONE); 1204 } 1205 if (!full && mCountDownView != null) mCountDownView.cancelCountDown(); 1206 if (ApiHelper.HAS_SURFACE_TEXTURE) { 1207 if (mActivity.mCameraScreenNail != null) { 1208 ((CameraScreenNail) mActivity.mCameraScreenNail).setFullScreen(full); 1209 } 1210 return; 1211 } 1212 if (full) { 1213 mPreviewSurfaceView.expand(); 1214 } else { 1215 mPreviewSurfaceView.shrink(); 1216 } 1217 } 1218 1219 @Override 1220 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 1221 Log.v(TAG, "surfaceChanged:" + holder + " width=" + width + ". height=" 1222 + height); 1223 } 1224 1225 @Override 1226 public void surfaceCreated(SurfaceHolder holder) { 1227 Log.v(TAG, "surfaceCreated: " + holder); 1228 mCameraSurfaceHolder = holder; 1229 // Do not access the camera if camera start up thread is not finished. 1230 if (mCameraDevice == null || mCameraStartUpThread != null) return; 1231 1232 mCameraDevice.setPreviewDisplayAsync(holder); 1233 // This happens when onConfigurationChanged arrives, surface has been 1234 // destroyed, and there is no onFullScreenChanged. 1235 if (mCameraState == PREVIEW_STOPPED) { 1236 setupPreview(); 1237 } 1238 } 1239 1240 @Override 1241 public void surfaceDestroyed(SurfaceHolder holder) { 1242 Log.v(TAG, "surfaceDestroyed: " + holder); 1243 mCameraSurfaceHolder = null; 1244 stopPreview(); 1245 } 1246 1247 private void updateSceneModeUI() { 1248 // If scene mode is set, we cannot set flash mode, white balance, and 1249 // focus mode, instead, we read it from driver 1250 if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) { 1251 overrideCameraSettings(mParameters.getFlashMode(), 1252 mParameters.getWhiteBalance(), mParameters.getFocusMode()); 1253 } else { 1254 overrideCameraSettings(null, null, null); 1255 } 1256 } 1257 1258 private void overrideCameraSettings(final String flashMode, 1259 final String whiteBalance, final String focusMode) { 1260 if (mPhotoControl != null) { 1261// mPieControl.enableFilter(true); 1262 mPhotoControl.overrideSettings( 1263 CameraSettings.KEY_FLASH_MODE, flashMode, 1264 CameraSettings.KEY_WHITE_BALANCE, whiteBalance, 1265 CameraSettings.KEY_FOCUS_MODE, focusMode); 1266// mPieControl.enableFilter(false); 1267 } 1268 } 1269 1270 private void loadCameraPreferences() { 1271 CameraSettings settings = new CameraSettings(mActivity, mInitialParams, 1272 mCameraId, CameraHolder.instance().getCameraInfo()); 1273 mPreferenceGroup = settings.getPreferenceGroup(R.xml.camera_preferences); 1274 } 1275 1276 @Override 1277 public boolean collapseCameraControls() { 1278 // Remove all the popups/dialog boxes 1279 boolean ret = false; 1280 if (mPopup != null) { 1281 dismissPopup(false); 1282 ret = true; 1283 } 1284 return ret; 1285 } 1286 1287 public boolean removeTopLevelPopup() { 1288 // Remove the top level popup or dialog box and return true if there's any 1289 if (mPopup != null) { 1290 dismissPopup(true); 1291 return true; 1292 } 1293 return false; 1294 } 1295 1296 @Override 1297 public void onOrientationChanged(int orientation) { 1298 // We keep the last known orientation. So if the user first orient 1299 // the camera then point the camera to floor or sky, we still have 1300 // the correct orientation. 1301 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) return; 1302 mOrientation = Util.roundOrientation(orientation, mOrientation); 1303 1304 // Show the toast after getting the first orientation changed. 1305 if (mHandler.hasMessages(SHOW_TAP_TO_FOCUS_TOAST)) { 1306 mHandler.removeMessages(SHOW_TAP_TO_FOCUS_TOAST); 1307 showTapToFocusToast(); 1308 } 1309 } 1310 1311 @Override 1312 public void onStop() { 1313 if (mMediaProviderClient != null) { 1314 mMediaProviderClient.release(); 1315 mMediaProviderClient = null; 1316 } 1317 } 1318 1319 // onClick handler for R.id.btn_done 1320 @OnClickAttr 1321 public void onReviewDoneClicked(View v) { 1322 doAttach(); 1323 } 1324 1325 // onClick handler for R.id.btn_cancel 1326 @OnClickAttr 1327 public void onReviewCancelClicked(View v) { 1328 doCancel(); 1329 } 1330 1331 // onClick handler for R.id.btn_retake 1332 @OnClickAttr 1333 public void onReviewRetakeClicked(View v) { 1334 if (mPaused) 1335 return; 1336 1337 hidePostCaptureAlert(); 1338 setupPreview(); 1339 } 1340 1341 private void doAttach() { 1342 if (mPaused) { 1343 return; 1344 } 1345 1346 byte[] data = mJpegImageData; 1347 1348 if (mCropValue == null) { 1349 // First handle the no crop case -- just return the value. If the 1350 // caller specifies a "save uri" then write the data to its 1351 // stream. Otherwise, pass back a scaled down version of the bitmap 1352 // directly in the extras. 1353 if (mSaveUri != null) { 1354 OutputStream outputStream = null; 1355 try { 1356 outputStream = mContentResolver.openOutputStream(mSaveUri); 1357 outputStream.write(data); 1358 outputStream.close(); 1359 1360 mActivity.setResultEx(Activity.RESULT_OK); 1361 mActivity.finish(); 1362 } catch (IOException ex) { 1363 // ignore exception 1364 } finally { 1365 Util.closeSilently(outputStream); 1366 } 1367 } else { 1368 int orientation = Exif.getOrientation(data); 1369 Bitmap bitmap = Util.makeBitmap(data, 50 * 1024); 1370 bitmap = Util.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 Util.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(CropExtras.KEY_RETURN_DATA, true); 1406 } 1407 if (mActivity.isSecureCamera()) { 1408 newExtras.putBoolean(CropExtras.KEY_SHOW_WHEN_LOCKED, true); 1409 } 1410 1411 Intent cropIntent = new Intent(FilterShowActivity.CROP_ACTION); 1412 1413 cropIntent.setData(tempUri); 1414 cropIntent.putExtras(newExtras); 1415 1416 mActivity.startActivityForResult(cropIntent, REQUEST_CROP); 1417 } 1418 } 1419 1420 private void doCancel() { 1421 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent()); 1422 mActivity.finish(); 1423 } 1424 1425 @Override 1426 public void onShutterButtonFocus(boolean pressed) { 1427 if (mPaused || collapseCameraControls() 1428 || (mCameraState == SNAPSHOT_IN_PROGRESS) 1429 || (mCameraState == PREVIEW_STOPPED)) return; 1430 1431 // Do not do focus if there is not enough storage. 1432 if (pressed && !canTakePicture()) return; 1433 1434 if (pressed) { 1435 if (mSceneMode == Util.SCENE_MODE_HDR) { 1436 mActivity.hideSwitcher(); 1437 mActivity.setSwipingEnabled(false); 1438 } 1439 mFocusManager.onShutterDown(); 1440 } else { 1441 mFocusManager.onShutterUp(); 1442 } 1443 } 1444 1445 @Override 1446 public void onShutterButtonClick() { 1447 if (mPaused || collapseCameraControls() 1448 || (mCameraState == SWITCHING_CAMERA) 1449 || (mCameraState == PREVIEW_STOPPED)) return; 1450 1451 // Do not take the picture if there is not enough storage. 1452 if (mActivity.getStorageSpace() <= Storage.LOW_STORAGE_THRESHOLD) { 1453 Log.i(TAG, "Not enough space or storage not ready. remaining=" 1454 + mActivity.getStorageSpace()); 1455 return; 1456 } 1457 Log.v(TAG, "onShutterButtonClick: mCameraState=" + mCameraState); 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 && !mIsImageCaptureIntent) { 1466 mSnapshotOnIdle = true; 1467 return; 1468 } 1469 1470 String timer = mPreferences.getString( 1471 CameraSettings.KEY_TIMER, 1472 mActivity.getString(R.string.pref_camera_timer_default)); 1473 boolean playSound = mPreferences.getString(CameraSettings.KEY_TIMER_SOUND_EFFECTS, 1474 mActivity.getString(R.string.pref_camera_timer_sound_default)) 1475 .equals(mActivity.getString(R.string.setting_on_value)); 1476 1477 int seconds = Integer.parseInt(timer); 1478 // When shutter button is pressed, check whether the previous countdown is 1479 // finished. If not, cancel the previous countdown and start a new one. 1480 if (mCountDownView.isCountingDown()) { 1481 mCountDownView.cancelCountDown(); 1482 mCountDownView.startCountDown(seconds, playSound); 1483 } else if (seconds > 0) { 1484 mCountDownView.startCountDown(seconds, playSound); 1485 } else { 1486 mSnapshotOnIdle = false; 1487 mFocusManager.doSnap(); 1488 } 1489 } 1490 1491 @Override 1492 public void installIntentFilter() { 1493 } 1494 1495 @Override 1496 public boolean updateStorageHintOnResume() { 1497 return mFirstTimeInitialized; 1498 } 1499 1500 @Override 1501 public void updateCameraAppView() { 1502 } 1503 1504 @Override 1505 public void onResumeBeforeSuper() { 1506 mPaused = false; 1507 } 1508 1509 @Override 1510 public void onResumeAfterSuper() { 1511 if (mOpenCameraFail || mCameraDisabled) return; 1512 1513 mJpegPictureCallbackTime = 0; 1514 mZoomValue = 0; 1515 1516 // Start the preview if it is not started. 1517 if (mCameraState == PREVIEW_STOPPED && mCameraStartUpThread == null) { 1518 resetExposureCompensation(); 1519 mCameraStartUpThread = new CameraStartUpThread(); 1520 mCameraStartUpThread.start(); 1521 } 1522 1523 // If first time initialization is not finished, put it in the 1524 // message queue. 1525 if (!mFirstTimeInitialized) { 1526 mHandler.sendEmptyMessage(FIRST_TIME_INIT); 1527 } else { 1528 initializeSecondTime(); 1529 } 1530 keepScreenOnAwhile(); 1531 1532 // Dismiss open menu if exists. 1533 PopupManager.getInstance(mActivity).notifyShowPopup(null); 1534 } 1535 1536 void waitCameraStartUpThread() { 1537 try { 1538 if (mCameraStartUpThread != null) { 1539 mCameraStartUpThread.cancel(); 1540 mCameraStartUpThread.join(); 1541 mCameraStartUpThread = null; 1542 setCameraState(IDLE); 1543 } 1544 } catch (InterruptedException e) { 1545 // ignore 1546 } 1547 } 1548 1549 @Override 1550 public void onPauseBeforeSuper() { 1551 mPaused = true; 1552 } 1553 1554 @Override 1555 public void onPauseAfterSuper() { 1556 // Wait the camera start up thread to finish. 1557 waitCameraStartUpThread(); 1558 1559 // When camera is started from secure lock screen for the first time 1560 // after screen on, the activity gets onCreate->onResume->onPause->onResume. 1561 // To reduce the latency, keep the camera for a short time so it does 1562 // not need to be opened again. 1563 if (mCameraDevice != null && mActivity.isSecureCamera() 1564 && ActivityBase.isFirstStartAfterScreenOn()) { 1565 ActivityBase.resetFirstStartAfterScreenOn(); 1566 CameraHolder.instance().keep(KEEP_CAMERA_TIMEOUT); 1567 } 1568 // Reset the focus first. Camera CTS does not guarantee that 1569 // cancelAutoFocus is allowed after preview stops. 1570 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) { 1571 mCameraDevice.cancelAutoFocus(); 1572 } 1573 stopPreview(); 1574 mCountDownView.cancelCountDown(); 1575 // Close the camera now because other activities may need to use it. 1576 closeCamera(); 1577 if (mSurfaceTexture != null) { 1578 ((CameraScreenNail) mActivity.mCameraScreenNail).releaseSurfaceTexture(); 1579 mSurfaceTexture = null; 1580 } 1581 resetScreenOn(); 1582 1583 // Clear UI. 1584 collapseCameraControls(); 1585 if (mFaceView != null) mFaceView.clear(); 1586 1587 mNamedImages = null; 1588 1589 if (mLocationManager != null) mLocationManager.recordLocation(false); 1590 1591 // If we are in an image capture intent and has taken 1592 // a picture, we just clear it in onPause. 1593 mJpegImageData = null; 1594 1595 // Remove the messages in the event queue. 1596 mHandler.removeMessages(SETUP_PREVIEW); 1597 mHandler.removeMessages(FIRST_TIME_INIT); 1598 mHandler.removeMessages(CHECK_DISPLAY_ROTATION); 1599 mHandler.removeMessages(SWITCH_CAMERA); 1600 mHandler.removeMessages(SWITCH_CAMERA_START_ANIMATION); 1601 mHandler.removeMessages(CAMERA_OPEN_DONE); 1602 mHandler.removeMessages(START_PREVIEW_DONE); 1603 mHandler.removeMessages(OPEN_CAMERA_FAIL); 1604 mHandler.removeMessages(CAMERA_DISABLED); 1605 1606 mPendingSwitchCameraId = -1; 1607 if (mFocusManager != null) mFocusManager.removeMessages(); 1608 MediaSaveService s = mActivity.getMediaSaveService(); 1609 if (s != null) { 1610 s.setListener(null); 1611 } 1612 } 1613 1614 private void initializeControlByIntent() { 1615 mBlocker = mRootView.findViewById(R.id.blocker); 1616 mMenu = mRootView.findViewById(R.id.menu); 1617 mMenu.setOnClickListener(new OnClickListener() { 1618 @Override 1619 public void onClick(View v) { 1620 if (mPieRenderer != null) { 1621 // If autofocus is not finished, cancel autofocus so that the 1622 // subsequent touch can be handled by PreviewGestures 1623 if (mCameraState == FOCUSING) cancelAutoFocus(); 1624 mPieRenderer.showInCenter(); 1625 } 1626 } 1627 }); 1628 if (mIsImageCaptureIntent) { 1629 1630 mActivity.hideSwitcher(); 1631 // Cannot use RotateImageView for "done" and "cancel" button because 1632 // the tablet layout uses RotateLayout, which cannot be cast to 1633 // RotateImageView. 1634 mReviewDoneButton = (Rotatable) mRootView.findViewById(R.id.btn_done); 1635 mReviewCancelButton = (Rotatable) mRootView.findViewById(R.id.btn_cancel); 1636 mReviewRetakeButton = mRootView.findViewById(R.id.btn_retake); 1637 ((View) mReviewCancelButton).setVisibility(View.VISIBLE); 1638 1639 ((View) mReviewDoneButton).setOnClickListener(new OnClickListener() { 1640 @Override 1641 public void onClick(View v) { 1642 onReviewDoneClicked(v); 1643 } 1644 }); 1645 ((View) mReviewCancelButton).setOnClickListener(new OnClickListener() { 1646 @Override 1647 public void onClick(View v) { 1648 onReviewCancelClicked(v); 1649 } 1650 }); 1651 1652 mReviewRetakeButton.setOnClickListener(new OnClickListener() { 1653 @Override 1654 public void onClick(View v) { 1655 onReviewRetakeClicked(v); 1656 } 1657 }); 1658 1659 // Not grayed out upon disabled, to make the follow-up fade-out 1660 // effect look smooth. Note that the review done button in tablet 1661 // layout is not a TwoStateImageView. 1662 if (mReviewDoneButton instanceof TwoStateImageView) { 1663 ((TwoStateImageView) mReviewDoneButton).enableFilter(false); 1664 } 1665 1666 setupCaptureParams(); 1667 } 1668 } 1669 1670 /** 1671 * The focus manager is the first UI related element to get initialized, 1672 * and it requires the RenderOverlay, so initialize it here 1673 */ 1674 private void initializeFocusManager() { 1675 // Create FocusManager object. startPreview needs it. 1676 mRenderOverlay = (RenderOverlay) mRootView.findViewById(R.id.render_overlay); 1677 // if mFocusManager not null, reuse it 1678 // otherwise create a new instance 1679 if (mFocusManager != null) { 1680 mFocusManager.removeMessages(); 1681 } else { 1682 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId]; 1683 boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT); 1684 String[] defaultFocusModes = mActivity.getResources().getStringArray( 1685 R.array.pref_camera_focusmode_default_array); 1686 mFocusManager = new FocusOverlayManager(mPreferences, defaultFocusModes, 1687 mInitialParams, this, mirror, 1688 mActivity.getMainLooper()); 1689 } 1690 } 1691 1692 private void initializeMiscControls() { 1693 // startPreview needs this. 1694 mPreviewFrameLayout = (PreviewFrameLayout) mRootView.findViewById(R.id.frame); 1695 // Set touch focus listener. 1696 mActivity.setSingleTapUpListener(mPreviewFrameLayout); 1697 1698 mFaceView = (FaceView) mRootView.findViewById(R.id.face_view); 1699 mPreviewFrameLayout.setOnSizeChangedListener(this); 1700 mPreviewFrameLayout.setOnLayoutChangeListener(mActivity); 1701 if (!ApiHelper.HAS_SURFACE_TEXTURE) { 1702 mPreviewSurfaceView = 1703 (PreviewSurfaceView) mRootView.findViewById(R.id.preview_surface_view); 1704 mPreviewSurfaceView.setVisibility(View.VISIBLE); 1705 mPreviewSurfaceView.getHolder().addCallback(this); 1706 } 1707 } 1708 1709 @Override 1710 public void onConfigurationChanged(Configuration newConfig) { 1711 Log.v(TAG, "onConfigurationChanged"); 1712 setDisplayOrientation(); 1713 1714 // Only the views in photo_module_content need to be removed and recreated 1715 // i.e. CountDownView won't be recreated 1716 ViewGroup viewGroup = (ViewGroup) mRootView.findViewById(R.id.camera_app); 1717 viewGroup.removeAllViews(); 1718 LayoutInflater inflater = mActivity.getLayoutInflater(); 1719 inflater.inflate(R.layout.photo_module_content, (ViewGroup) viewGroup); 1720 1721 // from onCreate() 1722 initializeControlByIntent(); 1723 1724 initializeFocusManager(); 1725 initializeMiscControls(); 1726 loadCameraPreferences(); 1727 1728 // from initializeFirstTime() 1729 mShutterButton = mActivity.getShutterButton(); 1730 mShutterButton.setOnShutterButtonListener(this); 1731 initializeZoom(); 1732 initOnScreenIndicator(); 1733 updateOnScreenIndicators(); 1734 if (mFaceView != null) { 1735 mFaceView.clear(); 1736 mFaceView.setVisibility(View.VISIBLE); 1737 mFaceView.setDisplayOrientation(mDisplayOrientation); 1738 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId]; 1739 mFaceView.setMirror(info.facing == CameraInfo.CAMERA_FACING_FRONT); 1740 mFaceView.resume(); 1741 mFocusManager.setFaceView(mFaceView); 1742 } 1743 initializeRenderOverlay(); 1744 onFullScreenChanged(mActivity.isInCameraApp()); 1745 if (mJpegImageData != null) { // Jpeg data found, picture has been taken. 1746 showPostCaptureAlert(); 1747 } 1748 } 1749 1750 @Override 1751 public void onActivityResult( 1752 int requestCode, int resultCode, Intent data) { 1753 switch (requestCode) { 1754 case REQUEST_CROP: { 1755 Intent intent = new Intent(); 1756 if (data != null) { 1757 Bundle extras = data.getExtras(); 1758 if (extras != null) { 1759 intent.putExtras(extras); 1760 } 1761 } 1762 mActivity.setResultEx(resultCode, intent); 1763 mActivity.finish(); 1764 1765 File path = mActivity.getFileStreamPath(sTempCropFilename); 1766 path.delete(); 1767 1768 break; 1769 } 1770 } 1771 } 1772 1773 private boolean canTakePicture() { 1774 return isCameraIdle() && (mActivity.getStorageSpace() > Storage.LOW_STORAGE_THRESHOLD); 1775 } 1776 1777 @Override 1778 public void autoFocus() { 1779 mFocusStartTime = System.currentTimeMillis(); 1780 mCameraDevice.autoFocus(mAutoFocusCallback); 1781 setCameraState(FOCUSING); 1782 } 1783 1784 @Override 1785 public void cancelAutoFocus() { 1786 mCameraDevice.cancelAutoFocus(); 1787 setCameraState(IDLE); 1788 setCameraParameters(UPDATE_PARAM_PREFERENCE); 1789 } 1790 1791 // Preview area is touched. Handle touch focus. 1792 @Override 1793 public void onSingleTapUp(View view, int x, int y) { 1794 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized 1795 || mCameraState == SNAPSHOT_IN_PROGRESS 1796 || mCameraState == SWITCHING_CAMERA 1797 || mCameraState == PREVIEW_STOPPED) { 1798 return; 1799 } 1800 1801 // Do not trigger touch focus if popup window is opened. 1802 if (removeTopLevelPopup()) return; 1803 1804 // Check if metering area or focus area is supported. 1805 if (!mFocusAreaSupported && !mMeteringAreaSupported) return; 1806 mFocusManager.onSingleTapUp(x, y); 1807 } 1808 1809 @Override 1810 public boolean onBackPressed() { 1811 if (mPieRenderer != null && mPieRenderer.showsItems()) { 1812 mPieRenderer.hide(); 1813 return true; 1814 } 1815 // In image capture mode, back button should: 1816 // 1) if there is any popup, dismiss them, 2) otherwise, get out of image capture 1817 if (mIsImageCaptureIntent) { 1818 if (!removeTopLevelPopup()) { 1819 // no popup to dismiss, cancel image capture 1820 doCancel(); 1821 } 1822 return true; 1823 } else if (!isCameraIdle()) { 1824 // ignore backs while we're taking a picture 1825 return true; 1826 } else { 1827 return removeTopLevelPopup(); 1828 } 1829 } 1830 1831 @Override 1832 public boolean onKeyDown(int keyCode, KeyEvent event) { 1833 switch (keyCode) { 1834 case KeyEvent.KEYCODE_VOLUME_UP: 1835 case KeyEvent.KEYCODE_VOLUME_DOWN: 1836 case KeyEvent.KEYCODE_FOCUS: 1837 if (mActivity.isInCameraApp() && mFirstTimeInitialized) { 1838 if (event.getRepeatCount() == 0) { 1839 onShutterButtonFocus(true); 1840 } 1841 return true; 1842 } 1843 return false; 1844 case KeyEvent.KEYCODE_CAMERA: 1845 if (mFirstTimeInitialized && event.getRepeatCount() == 0) { 1846 onShutterButtonClick(); 1847 } 1848 return true; 1849 case KeyEvent.KEYCODE_DPAD_CENTER: 1850 // If we get a dpad center event without any focused view, move 1851 // the focus to the shutter button and press it. 1852 if (mFirstTimeInitialized && event.getRepeatCount() == 0) { 1853 // Start auto-focus immediately to reduce shutter lag. After 1854 // the shutter button gets the focus, onShutterButtonFocus() 1855 // will be called again but it is fine. 1856 if (removeTopLevelPopup()) return true; 1857 onShutterButtonFocus(true); 1858 if (mShutterButton.isInTouchMode()) { 1859 mShutterButton.requestFocusFromTouch(); 1860 } else { 1861 mShutterButton.requestFocus(); 1862 } 1863 mShutterButton.setPressed(true); 1864 } 1865 return true; 1866 } 1867 return false; 1868 } 1869 1870 @Override 1871 public boolean onKeyUp(int keyCode, KeyEvent event) { 1872 switch (keyCode) { 1873 case KeyEvent.KEYCODE_VOLUME_UP: 1874 case KeyEvent.KEYCODE_VOLUME_DOWN: 1875 if (mActivity.isInCameraApp() && mFirstTimeInitialized) { 1876 onShutterButtonClick(); 1877 return true; 1878 } 1879 return false; 1880 case KeyEvent.KEYCODE_FOCUS: 1881 if (mFirstTimeInitialized) { 1882 onShutterButtonFocus(false); 1883 } 1884 return true; 1885 } 1886 return false; 1887 } 1888 1889 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH) 1890 private void closeCamera() { 1891 if (mCameraDevice != null) { 1892 mCameraDevice.setZoomChangeListener(null); 1893 if(ApiHelper.HAS_FACE_DETECTION) { 1894 mCameraDevice.setFaceDetectionListener(null); 1895 } 1896 mCameraDevice.setErrorCallback(null); 1897 CameraHolder.instance().release(); 1898 mFaceDetectionStarted = false; 1899 mCameraDevice = null; 1900 setCameraState(PREVIEW_STOPPED); 1901 mFocusManager.onCameraReleased(); 1902 } 1903 } 1904 1905 private void setDisplayOrientation() { 1906 mDisplayRotation = Util.getDisplayRotation(mActivity); 1907 mDisplayOrientation = Util.getDisplayOrientation(mDisplayRotation, mCameraId); 1908 mCameraDisplayOrientation = Util.getDisplayOrientation(0, mCameraId); 1909 if (mFaceView != null) { 1910 mFaceView.setDisplayOrientation(mDisplayOrientation); 1911 } 1912 if (mFocusManager != null) { 1913 mFocusManager.setDisplayOrientation(mDisplayOrientation); 1914 } 1915 // GLRoot also uses the DisplayRotation, and needs to be told to layout to update 1916 mActivity.getGLRoot().requestLayoutContentPane(); 1917 } 1918 1919 // Only called by UI thread. 1920 private void setupPreview() { 1921 mFocusManager.resetTouchFocus(); 1922 startPreview(); 1923 setCameraState(IDLE); 1924 startFaceDetection(); 1925 } 1926 1927 // This can be called by UI Thread or CameraStartUpThread. So this should 1928 // not modify the views. 1929 private void startPreview() { 1930 mCameraDevice.setErrorCallback(mErrorCallback); 1931 1932 // ICS camera frameworks has a bug. Face detection state is not cleared 1933 // after taking a picture. Stop the preview to work around it. The bug 1934 // was fixed in JB. 1935 if (mCameraState != PREVIEW_STOPPED) stopPreview(); 1936 1937 setDisplayOrientation(); 1938 1939 if (!mSnapshotOnIdle) { 1940 // If the focus mode is continuous autofocus, call cancelAutoFocus to 1941 // resume it because it may have been paused by autoFocus call. 1942 if (Util.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusManager.getFocusMode())) { 1943 mCameraDevice.cancelAutoFocus(); 1944 } 1945 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB. 1946 } 1947 setCameraParameters(UPDATE_PARAM_ALL); 1948 1949 if (ApiHelper.HAS_SURFACE_TEXTURE) { 1950 CameraScreenNail screenNail = (CameraScreenNail) mActivity.mCameraScreenNail; 1951 if (mSurfaceTexture == null) { 1952 Size size = mParameters.getPreviewSize(); 1953 if (mCameraDisplayOrientation % 180 == 0) { 1954 screenNail.setSize(size.width, size.height); 1955 } else { 1956 screenNail.setSize(size.height, size.width); 1957 } 1958 screenNail.enableAspectRatioClamping(); 1959 mActivity.notifyScreenNailChanged(); 1960 screenNail.acquireSurfaceTexture(); 1961 CameraStartUpThread t = mCameraStartUpThread; 1962 if (t != null && t.isCanceled()) { 1963 return; // Exiting, so no need to get the surface texture. 1964 } 1965 mSurfaceTexture = screenNail.getSurfaceTexture(); 1966 } 1967 mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation); 1968 if (mSurfaceTexture != null) { 1969 mCameraDevice.setPreviewTextureAsync((SurfaceTexture) mSurfaceTexture); 1970 } 1971 } else { 1972 mCameraDevice.setDisplayOrientation(mDisplayOrientation); 1973 mCameraDevice.setPreviewDisplayAsync(mCameraSurfaceHolder); 1974 } 1975 1976 Log.v(TAG, "startPreview"); 1977 mCameraDevice.startPreviewAsync(); 1978 1979 mFocusManager.onPreviewStarted(); 1980 1981 if (mSnapshotOnIdle) { 1982 mHandler.post(mDoSnapRunnable); 1983 } 1984 } 1985 1986 private void stopPreview() { 1987 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) { 1988 Log.v(TAG, "stopPreview"); 1989 mCameraDevice.stopPreview(); 1990 mFaceDetectionStarted = false; 1991 } 1992 setCameraState(PREVIEW_STOPPED); 1993 if (mFocusManager != null) mFocusManager.onPreviewStopped(); 1994 } 1995 1996 @SuppressWarnings("deprecation") 1997 private void updateCameraParametersInitialize() { 1998 // Reset preview frame rate to the maximum because it may be lowered by 1999 // video camera application. 2000 List<Integer> frameRates = mParameters.getSupportedPreviewFrameRates(); 2001 if (frameRates != null) { 2002 Integer max = Collections.max(frameRates); 2003 mParameters.setPreviewFrameRate(max); 2004 } 2005 2006 mParameters.set(Util.RECORDING_HINT, Util.FALSE); 2007 2008 // Disable video stabilization. Convenience methods not available in API 2009 // level <= 14 2010 String vstabSupported = mParameters.get("video-stabilization-supported"); 2011 if ("true".equals(vstabSupported)) { 2012 mParameters.set("video-stabilization", "false"); 2013 } 2014 } 2015 2016 private void updateCameraParametersZoom() { 2017 // Set zoom. 2018 if (mParameters.isZoomSupported()) { 2019 mParameters.setZoom(mZoomValue); 2020 } 2021 } 2022 2023 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN) 2024 private void setAutoExposureLockIfSupported() { 2025 if (mAeLockSupported) { 2026 mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock()); 2027 } 2028 } 2029 2030 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN) 2031 private void setAutoWhiteBalanceLockIfSupported() { 2032 if (mAwbLockSupported) { 2033 mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock()); 2034 } 2035 } 2036 2037 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH) 2038 private void setFocusAreasIfSupported() { 2039 if (mFocusAreaSupported) { 2040 mParameters.setFocusAreas(mFocusManager.getFocusAreas()); 2041 } 2042 } 2043 2044 @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH) 2045 private void setMeteringAreasIfSupported() { 2046 if (mMeteringAreaSupported) { 2047 // Use the same area for focus and metering. 2048 mParameters.setMeteringAreas(mFocusManager.getMeteringAreas()); 2049 } 2050 } 2051 2052 private void updateCameraParametersPreference() { 2053 setAutoExposureLockIfSupported(); 2054 setAutoWhiteBalanceLockIfSupported(); 2055 setFocusAreasIfSupported(); 2056 setMeteringAreasIfSupported(); 2057 2058 // Set picture size. 2059 String pictureSize = mPreferences.getString( 2060 CameraSettings.KEY_PICTURE_SIZE, null); 2061 if (pictureSize == null) { 2062 CameraSettings.initialCameraPictureSize(mActivity, mParameters); 2063 } else { 2064 List<Size> supported = mParameters.getSupportedPictureSizes(); 2065 CameraSettings.setCameraPictureSize( 2066 pictureSize, supported, mParameters); 2067 } 2068 Size size = mParameters.getPictureSize(); 2069 2070 // Set a preview size that is closest to the viewfinder height and has 2071 // the right aspect ratio. 2072 List<Size> sizes = mParameters.getSupportedPreviewSizes(); 2073 Size optimalSize = Util.getOptimalPreviewSize(mActivity, sizes, 2074 (double) size.width / size.height); 2075 Size original = mParameters.getPreviewSize(); 2076 if (!original.equals(optimalSize)) { 2077 mParameters.setPreviewSize(optimalSize.width, optimalSize.height); 2078 2079 // Zoom related settings will be changed for different preview 2080 // sizes, so set and read the parameters to get latest values 2081 mCameraDevice.setParameters(mParameters); 2082 mParameters = mCameraDevice.getParameters(); 2083 } 2084 Log.v(TAG, "Preview size is " + optimalSize.width + "x" + optimalSize.height); 2085 2086 // Since changing scene mode may change supported values, set scene mode 2087 // first. HDR is a scene mode. To promote it in UI, it is stored in a 2088 // separate preference. 2089 String hdr = mPreferences.getString(CameraSettings.KEY_CAMERA_HDR, 2090 mActivity.getString(R.string.pref_camera_hdr_default)); 2091 if (mActivity.getString(R.string.setting_on_value).equals(hdr)) { 2092 mSceneMode = Util.SCENE_MODE_HDR; 2093 } else { 2094 mSceneMode = mPreferences.getString( 2095 CameraSettings.KEY_SCENE_MODE, 2096 mActivity.getString(R.string.pref_camera_scenemode_default)); 2097 } 2098 if (Util.isSupported(mSceneMode, mParameters.getSupportedSceneModes())) { 2099 if (!mParameters.getSceneMode().equals(mSceneMode)) { 2100 mParameters.setSceneMode(mSceneMode); 2101 2102 // Setting scene mode will change the settings of flash mode, 2103 // white balance, and focus mode. Here we read back the 2104 // parameters, so we can know those settings. 2105 mCameraDevice.setParameters(mParameters); 2106 mParameters = mCameraDevice.getParameters(); 2107 } 2108 } else { 2109 mSceneMode = mParameters.getSceneMode(); 2110 if (mSceneMode == null) { 2111 mSceneMode = Parameters.SCENE_MODE_AUTO; 2112 } 2113 } 2114 2115 // Set JPEG quality. 2116 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId, 2117 CameraProfile.QUALITY_HIGH); 2118 mParameters.setJpegQuality(jpegQuality); 2119 2120 // For the following settings, we need to check if the settings are 2121 // still supported by latest driver, if not, ignore the settings. 2122 2123 // Set exposure compensation 2124 int value = CameraSettings.readExposure(mPreferences); 2125 int max = mParameters.getMaxExposureCompensation(); 2126 int min = mParameters.getMinExposureCompensation(); 2127 if (value >= min && value <= max) { 2128 mParameters.setExposureCompensation(value); 2129 } else { 2130 Log.w(TAG, "invalid exposure range: " + value); 2131 } 2132 2133 if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) { 2134 // Set flash mode. 2135 String flashMode = mPreferences.getString( 2136 CameraSettings.KEY_FLASH_MODE, 2137 mActivity.getString(R.string.pref_camera_flashmode_default)); 2138 List<String> supportedFlash = mParameters.getSupportedFlashModes(); 2139 if (Util.isSupported(flashMode, supportedFlash)) { 2140 mParameters.setFlashMode(flashMode); 2141 } else { 2142 flashMode = mParameters.getFlashMode(); 2143 if (flashMode == null) { 2144 flashMode = mActivity.getString( 2145 R.string.pref_camera_flashmode_no_flash); 2146 } 2147 } 2148 2149 // Set white balance parameter. 2150 String whiteBalance = mPreferences.getString( 2151 CameraSettings.KEY_WHITE_BALANCE, 2152 mActivity.getString(R.string.pref_camera_whitebalance_default)); 2153 if (Util.isSupported(whiteBalance, 2154 mParameters.getSupportedWhiteBalance())) { 2155 mParameters.setWhiteBalance(whiteBalance); 2156 } else { 2157 whiteBalance = mParameters.getWhiteBalance(); 2158 if (whiteBalance == null) { 2159 whiteBalance = Parameters.WHITE_BALANCE_AUTO; 2160 } 2161 } 2162 2163 // Set focus mode. 2164 mFocusManager.overrideFocusMode(null); 2165 mParameters.setFocusMode(mFocusManager.getFocusMode()); 2166 } else { 2167 mFocusManager.overrideFocusMode(mParameters.getFocusMode()); 2168 } 2169 2170 if (mContinousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) { 2171 updateAutoFocusMoveCallback(); 2172 } 2173 } 2174 2175 @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN) 2176 private void updateAutoFocusMoveCallback() { 2177 if (mParameters.getFocusMode().equals(Util.FOCUS_MODE_CONTINUOUS_PICTURE)) { 2178 mCameraDevice.setAutoFocusMoveCallback( 2179 (AutoFocusMoveCallback) mAutoFocusMoveCallback); 2180 } else { 2181 mCameraDevice.setAutoFocusMoveCallback(null); 2182 } 2183 } 2184 2185 // We separate the parameters into several subsets, so we can update only 2186 // the subsets actually need updating. The PREFERENCE set needs extra 2187 // locking because the preference can be changed from GLThread as well. 2188 private void setCameraParameters(int updateSet) { 2189 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) { 2190 updateCameraParametersInitialize(); 2191 } 2192 2193 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) { 2194 updateCameraParametersZoom(); 2195 } 2196 2197 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) { 2198 updateCameraParametersPreference(); 2199 } 2200 2201 mCameraDevice.setParameters(mParameters); 2202 } 2203 2204 // If the Camera is idle, update the parameters immediately, otherwise 2205 // accumulate them in mUpdateSet and update later. 2206 private void setCameraParametersWhenIdle(int additionalUpdateSet) { 2207 mUpdateSet |= additionalUpdateSet; 2208 if (mCameraDevice == null) { 2209 // We will update all the parameters when we open the device, so 2210 // we don't need to do anything now. 2211 mUpdateSet = 0; 2212 return; 2213 } else if (isCameraIdle()) { 2214 setCameraParameters(mUpdateSet); 2215 updateSceneModeUI(); 2216 mUpdateSet = 0; 2217 } else { 2218 if (!mHandler.hasMessages(SET_CAMERA_PARAMETERS_WHEN_IDLE)) { 2219 mHandler.sendEmptyMessageDelayed( 2220 SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000); 2221 } 2222 } 2223 } 2224 2225 private boolean isCameraIdle() { 2226 return (mCameraState == IDLE) || 2227 (mCameraState == PREVIEW_STOPPED) || 2228 ((mFocusManager != null) && mFocusManager.isFocusCompleted() 2229 && (mCameraState != SWITCHING_CAMERA)); 2230 } 2231 2232 private boolean isImageCaptureIntent() { 2233 String action = mActivity.getIntent().getAction(); 2234 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action) 2235 || ActivityBase.ACTION_IMAGE_CAPTURE_SECURE.equals(action)); 2236 } 2237 2238 private void setupCaptureParams() { 2239 Bundle myExtras = mActivity.getIntent().getExtras(); 2240 if (myExtras != null) { 2241 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT); 2242 mCropValue = myExtras.getString("crop"); 2243 } 2244 } 2245 2246 private void showPostCaptureAlert() { 2247 if (mIsImageCaptureIntent) { 2248 mOnScreenIndicators.setVisibility(View.GONE); 2249 mMenu.setVisibility(View.GONE); 2250 Util.fadeIn((View) mReviewDoneButton); 2251 mShutterButton.setVisibility(View.INVISIBLE); 2252 Util.fadeIn(mReviewRetakeButton); 2253 } 2254 } 2255 2256 private void hidePostCaptureAlert() { 2257 if (mIsImageCaptureIntent) { 2258 mOnScreenIndicators.setVisibility(View.VISIBLE); 2259 mMenu.setVisibility(View.VISIBLE); 2260 Util.fadeOut((View) mReviewDoneButton); 2261 mShutterButton.setVisibility(View.VISIBLE); 2262 Util.fadeOut(mReviewRetakeButton); 2263 } 2264 } 2265 2266 @Override 2267 public void onSharedPreferenceChanged() { 2268 // ignore the events after "onPause()" 2269 if (mPaused) return; 2270 2271 boolean recordLocation = RecordLocationPreference.get( 2272 mPreferences, mContentResolver); 2273 mLocationManager.recordLocation(recordLocation); 2274 2275 setCameraParametersWhenIdle(UPDATE_PARAM_PREFERENCE); 2276 setPreviewFrameLayoutAspectRatio(); 2277 updateOnScreenIndicators(); 2278 } 2279 2280 @Override 2281 public void onCameraPickerClicked(int cameraId) { 2282 if (mPaused || mPendingSwitchCameraId != -1) return; 2283 2284 mPendingSwitchCameraId = cameraId; 2285 if (ApiHelper.HAS_SURFACE_TEXTURE) { 2286 Log.v(TAG, "Start to copy texture. cameraId=" + cameraId); 2287 // We need to keep a preview frame for the animation before 2288 // releasing the camera. This will trigger onPreviewTextureCopied. 2289 ((CameraScreenNail) mActivity.mCameraScreenNail).copyTexture(); 2290 // Disable all camera controls. 2291 setCameraState(SWITCHING_CAMERA); 2292 } else { 2293 switchCamera(); 2294 } 2295 } 2296 2297 private void switchCamera() { 2298 if (mPaused) return; 2299 2300 Log.v(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId); 2301 mCameraId = mPendingSwitchCameraId; 2302 mPendingSwitchCameraId = -1; 2303 mPhotoControl.setCameraId(mCameraId); 2304 2305 // from onPause 2306 closeCamera(); 2307 collapseCameraControls(); 2308 if (mFaceView != null) mFaceView.clear(); 2309 if (mFocusManager != null) mFocusManager.removeMessages(); 2310 2311 // Restart the camera and initialize the UI. From onCreate. 2312 mPreferences.setLocalId(mActivity, mCameraId); 2313 CameraSettings.upgradeLocalPreferences(mPreferences.getLocal()); 2314 try { 2315 mCameraDevice = Util.openCamera(mActivity, mCameraId); 2316 mParameters = mCameraDevice.getParameters(); 2317 } catch (CameraHardwareException e) { 2318 Util.showErrorAndFinish(mActivity, R.string.cannot_connect_camera); 2319 return; 2320 } catch (CameraDisabledException e) { 2321 Util.showErrorAndFinish(mActivity, R.string.camera_disabled); 2322 return; 2323 } 2324 initializeCapabilities(); 2325 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId]; 2326 boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT); 2327 mFocusManager.setMirror(mirror); 2328 mFocusManager.setParameters(mInitialParams); 2329 setupPreview(); 2330 loadCameraPreferences(); 2331 initializePhotoControl(); 2332 2333 // from initializeFirstTime 2334 initializeZoom(); 2335 updateOnScreenIndicators(); 2336 showTapToFocusToastIfNeeded(); 2337 2338 if (ApiHelper.HAS_SURFACE_TEXTURE) { 2339 // Start switch camera animation. Post a message because 2340 // onFrameAvailable from the old camera may already exist. 2341 mHandler.sendEmptyMessage(SWITCH_CAMERA_START_ANIMATION); 2342 } 2343 } 2344 2345 @Override 2346 public void onPieOpened(int centerX, int centerY) { 2347 mActivity.cancelActivityTouchHandling(); 2348 mActivity.setSwipingEnabled(false); 2349 if (mFaceView != null) { 2350 mFaceView.setBlockDraw(true); 2351 } 2352 } 2353 2354 @Override 2355 public void onPieClosed() { 2356 mActivity.setSwipingEnabled(true); 2357 if (mFaceView != null) { 2358 mFaceView.setBlockDraw(false); 2359 } 2360 } 2361 2362 // Preview texture has been copied. Now camera can be released and the 2363 // animation can be started. 2364 @Override 2365 public void onPreviewTextureCopied() { 2366 mHandler.sendEmptyMessage(SWITCH_CAMERA); 2367 } 2368 2369 @Override 2370 public void onCaptureTextureCopied() { 2371 } 2372 2373 @Override 2374 public void onUserInteraction() { 2375 if (!mActivity.isFinishing()) keepScreenOnAwhile(); 2376 } 2377 2378 private void resetScreenOn() { 2379 mHandler.removeMessages(CLEAR_SCREEN_DELAY); 2380 mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 2381 } 2382 2383 private void keepScreenOnAwhile() { 2384 mHandler.removeMessages(CLEAR_SCREEN_DELAY); 2385 mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 2386 mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY); 2387 } 2388 2389 // TODO: Delete this function after old camera code is removed 2390 @Override 2391 public void onRestorePreferencesClicked() { 2392 } 2393 2394 @Override 2395 public void onOverriddenPreferencesClicked() { 2396 if (mPaused) return; 2397 if (mNotSelectableToast == null) { 2398 String str = mActivity.getResources().getString(R.string.not_selectable_in_scene_mode); 2399 mNotSelectableToast = Toast.makeText(mActivity, str, Toast.LENGTH_SHORT); 2400 } 2401 mNotSelectableToast.show(); 2402 } 2403 2404 private void showTapToFocusToast() { 2405 // TODO: Use a toast? 2406 new RotateTextToast(mActivity, R.string.tap_to_focus, 0).show(); 2407 // Clear the preference. 2408 Editor editor = mPreferences.edit(); 2409 editor.putBoolean(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, false); 2410 editor.apply(); 2411 } 2412 2413 private void initializeCapabilities() { 2414 mInitialParams = mCameraDevice.getParameters(); 2415 mFocusAreaSupported = Util.isFocusAreaSupported(mInitialParams); 2416 mMeteringAreaSupported = Util.isMeteringAreaSupported(mInitialParams); 2417 mAeLockSupported = Util.isAutoExposureLockSupported(mInitialParams); 2418 mAwbLockSupported = Util.isAutoWhiteBalanceLockSupported(mInitialParams); 2419 mContinousFocusSupported = mInitialParams.getSupportedFocusModes().contains( 2420 Util.FOCUS_MODE_CONTINUOUS_PICTURE); 2421 } 2422 2423 // PreviewFrameLayout size has changed. 2424 @Override 2425 public void onSizeChanged(int width, int height) { 2426 if (mFocusManager != null) mFocusManager.setPreviewSize(width, height); 2427 } 2428 2429 @Override 2430 public void onCountDownFinished() { 2431 mSnapshotOnIdle = false; 2432 mFocusManager.doSnap(); 2433 } 2434 2435 void setPreviewFrameLayoutAspectRatio() { 2436 // Set the preview frame aspect ratio according to the picture size. 2437 Size size = mParameters.getPictureSize(); 2438 mPreviewFrameLayout.setAspectRatio((double) size.width / size.height); 2439 } 2440 2441 @Override 2442 public boolean needsSwitcher() { 2443 return !mIsImageCaptureIntent; 2444 } 2445 2446 public void showPopup(AbstractSettingPopup popup) { 2447 mActivity.hideUI(); 2448 mBlocker.setVisibility(View.INVISIBLE); 2449 setShowMenu(false); 2450 mPopup = popup; 2451 mPopup.setVisibility(View.VISIBLE); 2452 FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, 2453 LayoutParams.WRAP_CONTENT); 2454 lp.gravity = Gravity.CENTER; 2455 ((FrameLayout) mRootView).addView(mPopup, lp); 2456 } 2457 2458 public void dismissPopup(boolean topPopupOnly) { 2459 dismissPopup(topPopupOnly, true); 2460 } 2461 2462 private void dismissPopup(boolean topOnly, boolean fullScreen) { 2463 if (fullScreen) { 2464 mActivity.showUI(); 2465 mBlocker.setVisibility(View.VISIBLE); 2466 } 2467 setShowMenu(fullScreen); 2468 if (mPopup != null) { 2469 ((FrameLayout) mRootView).removeView(mPopup); 2470 mPopup = null; 2471 } 2472 mPhotoControl.popupDismissed(topOnly); 2473 } 2474 2475 @Override 2476 public void onShowSwitcherPopup() { 2477 if (mPieRenderer != null && mPieRenderer.showsItems()) { 2478 mPieRenderer.hide(); 2479 } 2480 } 2481 2482 @Override 2483 public void onQueueAvailable() { 2484 if (mShutterButton != null) mShutterButton.enableTouch(true); 2485 } 2486 2487 @Override 2488 public void onQueueFull() { 2489 if (mShutterButton != null) mShutterButton.enableTouch(false); 2490 } 2491 2492 @Override 2493 public void onMediaSaveServiceConnected(MediaSaveService s) { 2494 // We set the listener only when both service and shutterbutton 2495 // are initialized. 2496 if (mShutterButton != null) s.setListener(this); 2497 } 2498} 2499