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