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