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