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