Camera.java revision fd140e054faaf9bf45c4dfe226e7f6ca8154c575
1/* 2 * Copyright (C) 2007 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 com.android.camera.ui.CameraPicker; 20import com.android.camera.ui.FaceView; 21import com.android.camera.ui.IndicatorControlContainer; 22import com.android.camera.ui.RotateImageView; 23import com.android.camera.ui.RotateLayout; 24import com.android.camera.ui.SharePopup; 25import com.android.camera.ui.ZoomControl; 26 27import android.app.Activity; 28import android.content.BroadcastReceiver; 29import android.content.ContentProviderClient; 30import android.content.ContentResolver; 31import android.content.Context; 32import android.content.Intent; 33import android.content.IntentFilter; 34import android.content.SharedPreferences.Editor; 35import android.graphics.Bitmap; 36import android.hardware.Camera.CameraInfo; 37import android.hardware.Camera.Face; 38import android.hardware.Camera.FaceDetectionListener; 39import android.hardware.Camera.Parameters; 40import android.hardware.Camera.PictureCallback; 41import android.hardware.Camera.Size; 42import android.location.Location; 43import android.media.CameraProfile; 44import android.net.Uri; 45import android.os.Bundle; 46import android.os.Handler; 47import android.os.Looper; 48import android.os.Message; 49import android.os.MessageQueue; 50import android.os.SystemClock; 51import android.provider.MediaStore; 52import android.util.Log; 53import android.view.GestureDetector; 54import android.view.Gravity; 55import android.view.KeyEvent; 56import android.view.Menu; 57import android.view.MenuItem; 58import android.view.MenuItem.OnMenuItemClickListener; 59import android.view.MotionEvent; 60import android.view.OrientationEventListener; 61import android.view.SurfaceHolder; 62import android.view.SurfaceView; 63import android.view.View; 64import android.view.WindowManager; 65import android.view.animation.AnimationUtils; 66import android.widget.TextView; 67import android.widget.Toast; 68 69import java.io.File; 70import java.io.FileNotFoundException; 71import java.io.FileOutputStream; 72import java.io.IOException; 73import java.io.OutputStream; 74import java.util.ArrayList; 75import java.util.Collections; 76import java.util.Formatter; 77import java.util.List; 78 79/** The Camera activity which can preview and take pictures. */ 80public class Camera extends ActivityBase implements FocusManager.Listener, 81 View.OnTouchListener, ShutterButton.OnShutterButtonListener, 82 SurfaceHolder.Callback, ModePicker.OnModeChangeListener, 83 FaceDetectionListener, CameraPreference.OnPreferenceChangedListener, 84 LocationManager.Listener { 85 86 private static final String TAG = "camera"; 87 88 private static final int CROP_MSG = 1; 89 private static final int FIRST_TIME_INIT = 2; 90 private static final int CLEAR_SCREEN_DELAY = 3; 91 private static final int SET_CAMERA_PARAMETERS_WHEN_IDLE = 4; 92 private static final int CHECK_DISPLAY_ROTATION = 5; 93 private static final int SHOW_TAP_TO_FOCUS_TOAST = 6; 94 private static final int DISMISS_TAP_TO_FOCUS_TOAST = 7; 95 96 // The subset of parameters we need to update in setCameraParameters(). 97 private static final int UPDATE_PARAM_INITIALIZE = 1; 98 private static final int UPDATE_PARAM_ZOOM = 2; 99 private static final int UPDATE_PARAM_PREFERENCE = 4; 100 private static final int UPDATE_PARAM_ALL = -1; 101 102 // When setCameraParametersWhenIdle() is called, we accumulate the subsets 103 // needed to be updated in mUpdateSet. 104 private int mUpdateSet; 105 106 private static final int SCREEN_DELAY = 2 * 60 * 1000; 107 108 private static final int ZOOM_STOPPED = 0; 109 private static final int ZOOM_START = 1; 110 private static final int ZOOM_STOPPING = 2; 111 112 private int mZoomState = ZOOM_STOPPED; 113 private boolean mSmoothZoomSupported = false; 114 private int mZoomValue; // The current zoom value. 115 private int mZoomMax; 116 private int mTargetZoomValue; 117 private ZoomControl mZoomControl; 118 119 private Parameters mParameters; 120 private Parameters mInitialParams; 121 private boolean mFocusAreaSupported; 122 private boolean mMeteringAreaSupported; 123 124 private MyOrientationEventListener mOrientationListener; 125 // The degrees of the device rotated clockwise from its natural orientation. 126 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN; 127 // The orientation compensation for icons and thumbnails. Ex: if the value 128 // is 90, the UI components should be rotated 90 degrees counter-clockwise. 129 private int mOrientationCompensation = 0; 130 private ComboPreferences mPreferences; 131 132 private static final String sTempCropFilename = "crop-temp"; 133 134 private android.hardware.Camera mCameraDevice; 135 private ContentProviderClient mMediaProviderClient; 136 private SurfaceHolder mSurfaceHolder = null; 137 private ShutterButton mShutterButton; 138 private GestureDetector mPopupGestureDetector; 139 private boolean mOpenCameraFail = false; 140 private boolean mCameraDisabled = false; 141 142 private View mPreviewPanel; // The container of PreviewFrameLayout. 143 private PreviewFrameLayout mPreviewFrameLayout; 144 private View mPreviewFrame; // Preview frame area. 145 146 // A popup window that contains a bigger thumbnail and a list of apps to share. 147 private SharePopup mSharePopup; 148 // The bitmap of the last captured picture thumbnail and the URI of the 149 // original picture. 150 private Thumbnail mThumbnail; 151 // An imageview showing showing the last captured picture thumbnail. 152 private RotateImageView mThumbnailView; 153 private ModePicker mModePicker; 154 private FaceView mFaceView; 155 private RotateLayout mFocusIndicator; 156 157 // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true. 158 private String mCropValue; 159 private Uri mSaveUri; 160 161 // On-screen indicator 162 private View mGpsNoSignalIndicator; 163 private View mGpsHasSignalIndicator; 164 private TextView mExposureIndicator; 165 166 private final StringBuilder mBuilder = new StringBuilder(); 167 private final Formatter mFormatter = new Formatter(mBuilder); 168 private final Object[] mFormatterArgs = new Object[1]; 169 170 /** 171 * An unpublished intent flag requesting to return as soon as capturing 172 * is completed. 173 * 174 * TODO: consider publishing by moving into MediaStore. 175 */ 176 private final static String EXTRA_QUICK_CAPTURE = 177 "android.intent.extra.quickCapture"; 178 179 // The display rotation in degrees. This is only valid when mCameraState is 180 // not PREVIEW_STOPPED. 181 private int mDisplayRotation; 182 // The value for android.hardware.Camera.setDisplayOrientation. 183 private int mDisplayOrientation; 184 private boolean mPausing; 185 private boolean mFirstTimeInitialized; 186 private boolean mIsImageCaptureIntent; 187 188 private static final int PREVIEW_STOPPED = 0; 189 private static final int IDLE = 1; // preview is active 190 // Focus is in progress. The exact focus state is in Focus.java. 191 private static final int FOCUSING = 2; 192 private static final int SNAPSHOT_IN_PROGRESS = 3; 193 private int mCameraState = PREVIEW_STOPPED; 194 195 private ContentResolver mContentResolver; 196 private boolean mDidRegister = false; 197 198 private final ArrayList<MenuItem> mGalleryItems = new ArrayList<MenuItem>(); 199 200 private LocationManager mLocationManager; 201 202 private final ShutterCallback mShutterCallback = new ShutterCallback(); 203 private final PostViewPictureCallback mPostViewPictureCallback = 204 new PostViewPictureCallback(); 205 private final RawPictureCallback mRawPictureCallback = 206 new RawPictureCallback(); 207 private final AutoFocusCallback mAutoFocusCallback = 208 new AutoFocusCallback(); 209 private final ZoomListener mZoomListener = new ZoomListener(); 210 private final CameraErrorCallback mErrorCallback = new CameraErrorCallback(); 211 212 private long mFocusStartTime; 213 private long mCaptureStartTime; 214 private long mShutterCallbackTime; 215 private long mPostViewPictureCallbackTime; 216 private long mRawPictureCallbackTime; 217 private long mJpegPictureCallbackTime; 218 private long mOnResumeTime; 219 private long mPicturesRemaining; 220 private byte[] mJpegImageData; 221 222 // These latency time are for the CameraLatency test. 223 public long mAutoFocusTime; 224 public long mShutterLag; 225 public long mShutterToPictureDisplayedTime; 226 public long mPictureDisplayedToJpegCallbackTime; 227 public long mJpegCallbackFinishTime; 228 229 // This handles everything about focus. 230 private FocusManager mFocusManager; 231 private String mSceneMode; 232 private Toast mNotSelectableToast; 233 private Toast mNoShareToast; 234 235 private final Handler mHandler = new MainHandler(); 236 private IndicatorControlContainer mIndicatorControlContainer; 237 private PreferenceGroup mPreferenceGroup; 238 239 // multiple cameras support 240 private int mNumberOfCameras; 241 private int mCameraId; 242 private int mFrontCameraId; 243 private int mBackCameraId; 244 245 private boolean mQuickCapture; 246 247 /** 248 * This Handler is used to post message back onto the main thread of the 249 * application 250 */ 251 private class MainHandler extends Handler { 252 @Override 253 public void handleMessage(Message msg) { 254 switch (msg.what) { 255 case CLEAR_SCREEN_DELAY: { 256 getWindow().clearFlags( 257 WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 258 break; 259 } 260 261 case FIRST_TIME_INIT: { 262 initializeFirstTime(); 263 break; 264 } 265 266 case SET_CAMERA_PARAMETERS_WHEN_IDLE: { 267 setCameraParametersWhenIdle(0); 268 break; 269 } 270 271 case CHECK_DISPLAY_ROTATION: { 272 // Restart the preview if display rotation has changed. 273 // Sometimes this happens when the device is held upside 274 // down and camera app is opened. Rotation animation will 275 // take some time and the rotation value we have got may be 276 // wrong. Framework does not have a callback for this now. 277 if (Util.getDisplayRotation(Camera.this) != mDisplayRotation 278 && isCameraIdle()) { 279 startPreview(); 280 } 281 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) { 282 mHandler.sendEmptyMessageDelayed(CHECK_DISPLAY_ROTATION, 100); 283 } 284 break; 285 } 286 287 case SHOW_TAP_TO_FOCUS_TOAST: { 288 showTapToFocusToast(); 289 break; 290 } 291 292 case DISMISS_TAP_TO_FOCUS_TOAST: { 293 View v = findViewById(R.id.tap_to_focus_prompt); 294 v.setVisibility(View.GONE); 295 v.setAnimation(AnimationUtils.loadAnimation(Camera.this, 296 R.anim.on_screen_hint_exit)); 297 break; 298 } 299 } 300 } 301 } 302 303 private void resetExposureCompensation() { 304 String value = mPreferences.getString(CameraSettings.KEY_EXPOSURE, 305 CameraSettings.EXPOSURE_DEFAULT_VALUE); 306 if (!CameraSettings.EXPOSURE_DEFAULT_VALUE.equals(value)) { 307 Editor editor = mPreferences.edit(); 308 editor.putString(CameraSettings.KEY_EXPOSURE, "0"); 309 editor.apply(); 310 if (mIndicatorControlContainer != null) { 311 mIndicatorControlContainer.reloadPreferences(); 312 } 313 } 314 } 315 316 private void keepMediaProviderInstance() { 317 // We want to keep a reference to MediaProvider in camera's lifecycle. 318 // TODO: Utilize mMediaProviderClient instance to replace 319 // ContentResolver calls. 320 if (mMediaProviderClient == null) { 321 mMediaProviderClient = getContentResolver() 322 .acquireContentProviderClient(MediaStore.AUTHORITY); 323 } 324 } 325 326 // Snapshots can only be taken after this is called. It should be called 327 // once only. We could have done these things in onCreate() but we want to 328 // make preview screen appear as soon as possible. 329 private void initializeFirstTime() { 330 if (mFirstTimeInitialized) return; 331 332 // Create orientation listenter. This should be done first because it 333 // takes some time to get first orientation. 334 mOrientationListener = new MyOrientationEventListener(Camera.this); 335 mOrientationListener.enable(); 336 337 // Initialize location sevice. 338 boolean recordLocation = RecordLocationPreference.get( 339 mPreferences, getContentResolver()); 340 initOnScreenIndicator(); 341 mLocationManager.recordLocation(recordLocation); 342 343 keepMediaProviderInstance(); 344 checkStorage(); 345 346 // Initialize last picture button. 347 mContentResolver = getContentResolver(); 348 if (!mIsImageCaptureIntent) { // no thumbnail in image capture intent 349 initThumbnailButton(); 350 } 351 352 // Initialize shutter button. 353 mShutterButton = (ShutterButton) findViewById(R.id.shutter_button); 354 mShutterButton.setOnShutterButtonListener(this); 355 mShutterButton.setVisibility(View.VISIBLE); 356 357 // Initialize focus UI. 358 mPreviewFrame = findViewById(R.id.camera_preview); 359 mPreviewFrame.setOnTouchListener(this); 360 mFocusIndicator = (RotateLayout) findViewById(R.id.focus_indicator_rotate_layout); 361 mFocusManager.initialize(mFocusIndicator, mPreviewFrame, mFaceView, this); 362 mFocusManager.initializeToneGenerator(); 363 Util.initializeScreenBrightness(getWindow(), getContentResolver()); 364 installIntentFilter(); 365 initializeZoom(); 366 // Show the tap to focus toast if this is the first start. 367 if (mFocusAreaSupported && 368 mPreferences.getBoolean(CameraSettings.KEY_TAP_TO_FOCUS_PROMPT_SHOWN, true)) { 369 // Delay the toast for one second to wait for orientation. 370 mHandler.sendEmptyMessageDelayed(SHOW_TAP_TO_FOCUS_TOAST, 1000); 371 } 372 373 mFirstTimeInitialized = true; 374 addIdleHandler(); 375 } 376 377 private void addIdleHandler() { 378 MessageQueue queue = Looper.myQueue(); 379 queue.addIdleHandler(new MessageQueue.IdleHandler() { 380 public boolean queueIdle() { 381 Storage.ensureOSXCompatible(); 382 return false; 383 } 384 }); 385 } 386 387 private void initThumbnailButton() { 388 // Load the thumbnail from the disk. 389 mThumbnail = Thumbnail.loadFrom(new File(getFilesDir(), Thumbnail.LAST_THUMB_FILENAME)); 390 updateThumbnailButton(); 391 } 392 393 private void updateThumbnailButton() { 394 // Update last image if URI is invalid and the storage is ready. 395 if ((mThumbnail == null || !Util.isUriValid(mThumbnail.getUri(), mContentResolver)) 396 && mPicturesRemaining >= 0) { 397 mThumbnail = Thumbnail.getLastThumbnail(mContentResolver); 398 } 399 if (mThumbnail != null) { 400 mThumbnailView.setBitmap(mThumbnail.getBitmap()); 401 } else { 402 mThumbnailView.setBitmap(null); 403 } 404 } 405 406 // If the activity is paused and resumed, this method will be called in 407 // onResume. 408 private void initializeSecondTime() { 409 // Start orientation listener as soon as possible because it takes 410 // some time to get first orientation. 411 mOrientationListener.enable(); 412 413 // Start location update if needed. 414 boolean recordLocation = RecordLocationPreference.get( 415 mPreferences, getContentResolver()); 416 mLocationManager.recordLocation(recordLocation); 417 418 installIntentFilter(); 419 mFocusManager.initializeToneGenerator(); 420 initializeZoom(); 421 keepMediaProviderInstance(); 422 checkStorage(); 423 hidePostCaptureAlert(); 424 425 if (!mIsImageCaptureIntent) { 426 updateThumbnailButton(); 427 mModePicker.setCurrentMode(ModePicker.MODE_CAMERA); 428 } 429 } 430 431 private class ZoomChangeListener implements ZoomControl.OnZoomChangedListener { 432 // only for immediate zoom 433 @Override 434 public void onZoomValueChanged(int index) { 435 Camera.this.onZoomValueChanged(index); 436 } 437 438 // only for smooth zoom 439 @Override 440 public void onZoomStateChanged(int state) { 441 if (mPausing) return; 442 443 Log.v(TAG, "zoom picker state=" + state); 444 if (state == ZoomControl.ZOOM_IN) { 445 Camera.this.onZoomValueChanged(mZoomMax); 446 } else if (state == ZoomControl.ZOOM_OUT) { 447 Camera.this.onZoomValueChanged(0); 448 } else { 449 mTargetZoomValue = -1; 450 if (mZoomState == ZOOM_START) { 451 mZoomState = ZOOM_STOPPING; 452 mCameraDevice.stopSmoothZoom(); 453 } 454 } 455 } 456 } 457 458 private void initializeZoom() { 459 if (!mParameters.isZoomSupported()) return; 460 mZoomMax = mParameters.getMaxZoom(); 461 // Currently we use immediate zoom for fast zooming to get better UX and 462 // there is no plan to take advantage of the smooth zoom. 463 mZoomControl.setZoomMax(mZoomMax); 464 mZoomControl.setZoomIndex(mParameters.getZoom()); 465 mZoomControl.setSmoothZoomSupported(mSmoothZoomSupported); 466 mZoomControl.setOnZoomChangeListener(new ZoomChangeListener()); 467 mCameraDevice.setZoomChangeListener(mZoomListener); 468 } 469 470 private void onZoomValueChanged(int index) { 471 // Not useful to change zoom value when the activity is paused. 472 if (mPausing) return; 473 474 if (mSmoothZoomSupported) { 475 if (mTargetZoomValue != index && mZoomState != ZOOM_STOPPED) { 476 mTargetZoomValue = index; 477 if (mZoomState == ZOOM_START) { 478 mZoomState = ZOOM_STOPPING; 479 mCameraDevice.stopSmoothZoom(); 480 } 481 } else if (mZoomState == ZOOM_STOPPED && mZoomValue != index) { 482 mTargetZoomValue = index; 483 mCameraDevice.startSmoothZoom(index); 484 mZoomState = ZOOM_START; 485 } 486 } else { 487 mZoomValue = index; 488 setCameraParametersWhenIdle(UPDATE_PARAM_ZOOM); 489 } 490 } 491 492 @Override 493 public void startFaceDetection() { 494 if (mParameters.getMaxNumDetectedFaces() > 0) { 495 mFaceView = (FaceView) findViewById(R.id.face_view); 496 mFaceView.clear(); 497 mFaceView.setVisibility(View.VISIBLE); 498 mFaceView.setDisplayOrientation(mDisplayOrientation); 499 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId]; 500 mFaceView.setMirror(info.facing == CameraInfo.CAMERA_FACING_FRONT); 501 mFaceView.resume(); 502 mCameraDevice.setFaceDetectionListener(this); 503 mCameraDevice.startFaceDetection(); 504 } 505 } 506 507 @Override 508 public void stopFaceDetection() { 509 if (mParameters.getMaxNumDetectedFaces() > 0) { 510 mCameraDevice.setFaceDetectionListener(null); 511 mCameraDevice.stopFaceDetection(); 512 if (mFaceView != null) mFaceView.clear(); 513 } 514 } 515 516 private class PopupGestureListener 517 extends GestureDetector.SimpleOnGestureListener { 518 @Override 519 public boolean onDown(MotionEvent e) { 520 // Check if the popup window is visible. 521 View popup = mIndicatorControlContainer.getActiveSettingPopup(); 522 if (popup == null) return false; 523 524 525 // Let popup window, indicator control or preview frame handle the 526 // event by themselves. Dismiss the popup window if users touch on 527 // other areas. 528 if (!Util.pointInView(e.getX(), e.getY(), popup) 529 && !Util.pointInView(e.getX(), e.getY(), mIndicatorControlContainer) 530 && !Util.pointInView(e.getX(), e.getY(), mPreviewFrame)) { 531 mIndicatorControlContainer.dismissSettingPopup(); 532 // Let event fall through. 533 } 534 return false; 535 } 536 } 537 538 @Override 539 public boolean dispatchTouchEvent(MotionEvent m) { 540 // Check if the popup window should be dismissed first. 541 if (mPopupGestureDetector != null && mPopupGestureDetector.onTouchEvent(m)) { 542 return true; 543 } 544 545 return super.dispatchTouchEvent(m); 546 } 547 548 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 549 @Override 550 public void onReceive(Context context, Intent intent) { 551 String action = intent.getAction(); 552 Log.d(TAG, "Received intent action=" + action); 553 if (action.equals(Intent.ACTION_MEDIA_MOUNTED) 554 || action.equals(Intent.ACTION_MEDIA_UNMOUNTED) 555 || action.equals(Intent.ACTION_MEDIA_CHECKING)) { 556 checkStorage(); 557 } else if (action.equals(Intent.ACTION_MEDIA_SCANNER_FINISHED)) { 558 checkStorage(); 559 if (!mIsImageCaptureIntent) { 560 updateThumbnailButton(); 561 } 562 } 563 } 564 }; 565 566 private void initOnScreenIndicator() { 567 mGpsNoSignalIndicator = findViewById(R.id.onscreen_gps_indicator_no_signal); 568 mGpsHasSignalIndicator = findViewById(R.id.onscreen_gps_indicator_on); 569 mExposureIndicator = (TextView) findViewById(R.id.onscreen_exposure_indicator); 570 } 571 572 @Override 573 public void showGpsOnScreenIndicator(boolean hasSignal) { 574 if (hasSignal) { 575 if (mGpsNoSignalIndicator != null) { 576 mGpsNoSignalIndicator.setVisibility(View.GONE); 577 } 578 if (mGpsHasSignalIndicator != null) { 579 mGpsHasSignalIndicator.setVisibility(View.VISIBLE); 580 } 581 } else { 582 if (mGpsNoSignalIndicator != null) { 583 mGpsNoSignalIndicator.setVisibility(View.VISIBLE); 584 } 585 if (mGpsHasSignalIndicator != null) { 586 mGpsHasSignalIndicator.setVisibility(View.GONE); 587 } 588 } 589 } 590 591 @Override 592 public void hideGpsOnScreenIndicator() { 593 if (mGpsNoSignalIndicator != null) mGpsNoSignalIndicator.setVisibility(View.GONE); 594 if (mGpsHasSignalIndicator != null) mGpsHasSignalIndicator.setVisibility(View.GONE); 595 } 596 597 private void updateExposureOnScreenIndicator(int value) { 598 if (mExposureIndicator == null) return; 599 600 if (value == 0) { 601 mExposureIndicator.setText(""); 602 mExposureIndicator.setVisibility(View.GONE); 603 } else { 604 float step = mParameters.getExposureCompensationStep(); 605 mFormatterArgs[0] = value * step; 606 mBuilder.delete(0, mBuilder.length()); 607 mFormatter.format("%+1.1f", mFormatterArgs); 608 String exposure = mFormatter.toString(); 609 mExposureIndicator.setText(exposure); 610 mExposureIndicator.setVisibility(View.VISIBLE); 611 } 612 } 613 614 private final class ShutterCallback 615 implements android.hardware.Camera.ShutterCallback { 616 public void onShutter() { 617 mShutterCallbackTime = System.currentTimeMillis(); 618 mShutterLag = mShutterCallbackTime - mCaptureStartTime; 619 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms"); 620 mFocusManager.onShutter(); 621 } 622 } 623 624 private final class PostViewPictureCallback implements PictureCallback { 625 public void onPictureTaken( 626 byte [] data, android.hardware.Camera camera) { 627 mPostViewPictureCallbackTime = System.currentTimeMillis(); 628 Log.v(TAG, "mShutterToPostViewCallbackTime = " 629 + (mPostViewPictureCallbackTime - mShutterCallbackTime) 630 + "ms"); 631 } 632 } 633 634 private final class RawPictureCallback implements PictureCallback { 635 public void onPictureTaken( 636 byte [] rawData, android.hardware.Camera camera) { 637 mRawPictureCallbackTime = System.currentTimeMillis(); 638 Log.v(TAG, "mShutterToRawCallbackTime = " 639 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms"); 640 } 641 } 642 643 private final class JpegPictureCallback implements PictureCallback { 644 Location mLocation; 645 646 public JpegPictureCallback(Location loc) { 647 mLocation = loc; 648 } 649 650 public void onPictureTaken( 651 final byte [] jpegData, final android.hardware.Camera camera) { 652 if (mPausing) { 653 return; 654 } 655 656 mJpegPictureCallbackTime = System.currentTimeMillis(); 657 // If postview callback has arrived, the captured image is displayed 658 // in postview callback. If not, the captured image is displayed in 659 // raw picture callback. 660 if (mPostViewPictureCallbackTime != 0) { 661 mShutterToPictureDisplayedTime = 662 mPostViewPictureCallbackTime - mShutterCallbackTime; 663 mPictureDisplayedToJpegCallbackTime = 664 mJpegPictureCallbackTime - mPostViewPictureCallbackTime; 665 } else { 666 mShutterToPictureDisplayedTime = 667 mRawPictureCallbackTime - mShutterCallbackTime; 668 mPictureDisplayedToJpegCallbackTime = 669 mJpegPictureCallbackTime - mRawPictureCallbackTime; 670 } 671 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = " 672 + mPictureDisplayedToJpegCallbackTime + "ms"); 673 674 if (!mIsImageCaptureIntent) { 675 enableCameraControls(true); 676 677 startPreview(); 678 } 679 680 if (!mIsImageCaptureIntent) { 681 storeImage(jpegData, mLocation); 682 } else { 683 mJpegImageData = jpegData; 684 if (!mQuickCapture) { 685 showPostCaptureAlert(); 686 } else { 687 doAttach(); 688 } 689 } 690 691 // Check this in advance of each shot so we don't add to shutter 692 // latency. It's true that someone else could write to the SD card in 693 // the mean time and fill it, but that could have happened between the 694 // shutter press and saving the JPEG too. 695 checkStorage(); 696 697 long now = System.currentTimeMillis(); 698 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime; 699 Log.v(TAG, "mJpegCallbackFinishTime = " 700 + mJpegCallbackFinishTime + "ms"); 701 mJpegPictureCallbackTime = 0; 702 } 703 } 704 705 private final class AutoFocusCallback 706 implements android.hardware.Camera.AutoFocusCallback { 707 public void onAutoFocus( 708 boolean focused, android.hardware.Camera camera) { 709 if (mPausing) return; 710 711 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime; 712 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms"); 713 mFocusManager.onAutoFocus(focused); 714 // If focus completes and the snapshot is not started, enable the 715 // controls. 716 if (mFocusManager.isFocusCompleted()) { 717 enableCameraControls(true); 718 } 719 } 720 } 721 722 private final class ZoomListener 723 implements android.hardware.Camera.OnZoomChangeListener { 724 @Override 725 public void onZoomChange( 726 int value, boolean stopped, android.hardware.Camera camera) { 727 Log.v(TAG, "Zoom changed: value=" + value + ". stopped="+ stopped); 728 mZoomValue = value; 729 730 // Update the UI when we get zoom value. 731 mZoomControl.setZoomIndex(value); 732 733 // Keep mParameters up to date. We do not getParameter again in 734 // takePicture. If we do not do this, wrong zoom value will be set. 735 mParameters.setZoom(value); 736 737 if (stopped && mZoomState != ZOOM_STOPPED) { 738 if (mTargetZoomValue != -1 && value != mTargetZoomValue) { 739 mCameraDevice.startSmoothZoom(mTargetZoomValue); 740 mZoomState = ZOOM_START; 741 } else { 742 mZoomState = ZOOM_STOPPED; 743 } 744 } 745 } 746 } 747 748 private void storeImage(final byte[] data, Location loc) { 749 long dateTaken = System.currentTimeMillis(); 750 String title = Util.createJpegName(dateTaken); 751 int orientation = Exif.getOrientation(data); 752 Uri uri = Storage.addImage(mContentResolver, title, dateTaken, 753 loc, orientation, data); 754 if (uri != null) { 755 // Create a thumbnail whose width is equal or bigger than that of the preview. 756 int ratio = (int) Math.ceil((double) mParameters.getPictureSize().width 757 / mPreviewFrameLayout.getWidth()); 758 int inSampleSize = Integer.highestOneBit(ratio); 759 mThumbnail = Thumbnail.createThumbnail(data, orientation, inSampleSize, uri); 760 if (mThumbnail != null) { 761 mThumbnailView.setBitmap(mThumbnail.getBitmap()); 762 } 763 Util.broadcastNewPicture(this, uri); 764 } 765 } 766 767 @Override 768 public boolean capture() { 769 // If we are already in the middle of taking a snapshot then ignore. 770 if (mCameraState == SNAPSHOT_IN_PROGRESS || mCameraDevice == null) { 771 return false; 772 } 773 mCaptureStartTime = System.currentTimeMillis(); 774 mPostViewPictureCallbackTime = 0; 775 enableCameraControls(false); 776 mJpegImageData = null; 777 778 // Set rotation and gps data. 779 Util.setRotationParameter(mParameters, mCameraId, mOrientation); 780 Location loc = mLocationManager.getCurrentLocation(); 781 Util.setGpsParameters(mParameters, loc); 782 mCameraDevice.setParameters(mParameters); 783 784 mCameraDevice.takePicture(mShutterCallback, mRawPictureCallback, 785 mPostViewPictureCallback, new JpegPictureCallback(loc)); 786 mCameraState = SNAPSHOT_IN_PROGRESS; 787 return true; 788 } 789 790 @Override 791 public void setFocusParameters() { 792 setCameraParameters(UPDATE_PARAM_PREFERENCE); 793 } 794 795 private boolean saveDataToFile(String filePath, byte[] data) { 796 FileOutputStream f = null; 797 try { 798 f = new FileOutputStream(filePath); 799 f.write(data); 800 } catch (IOException e) { 801 return false; 802 } finally { 803 Util.closeSilently(f); 804 } 805 return true; 806 } 807 808 @Override 809 public void onCreate(Bundle icicle) { 810 super.onCreate(icicle); 811 812 mIsImageCaptureIntent = isImageCaptureIntent(); 813 setContentView(R.layout.camera); 814 if (mIsImageCaptureIntent) { 815 findViewById(R.id.btn_cancel).setVisibility(View.VISIBLE); 816 } else { 817 mThumbnailView = (RotateImageView) findViewById(R.id.thumbnail); 818 mThumbnailView.setVisibility(View.VISIBLE); 819 } 820 821 mPreferences = new ComboPreferences(this); 822 CameraSettings.upgradeGlobalPreferences(mPreferences.getGlobal()); 823 mFocusManager = new FocusManager(mPreferences, 824 getString(R.string.pref_camera_focusmode_default)); 825 826 mCameraId = CameraSettings.readPreferredCameraId(mPreferences); 827 828 // Testing purpose. Launch a specific camera through the intent extras. 829 int intentCameraId = Util.getCameraFacingIntentExtras(this); 830 if (intentCameraId != -1) { 831 mCameraId = intentCameraId; 832 } 833 834 mPreferences.setLocalId(this, mCameraId); 835 CameraSettings.upgradeLocalPreferences(mPreferences.getLocal()); 836 837 mNumberOfCameras = CameraHolder.instance().getNumberOfCameras(); 838 mQuickCapture = getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false); 839 840 // we need to reset exposure for the preview 841 resetExposureCompensation(); 842 843 /* 844 * To reduce startup time, we start the preview in another thread. 845 * We make sure the preview is started at the end of onCreate. 846 */ 847 Thread startPreviewThread = new Thread(new Runnable() { 848 public void run() { 849 try { 850 mCameraDevice = Util.openCamera(Camera.this, mCameraId); 851 initializeCapabilities(); 852 startPreview(); 853 } catch (CameraHardwareException e) { 854 mOpenCameraFail = true; 855 } catch (CameraDisabledException e) { 856 mCameraDisabled = true; 857 } 858 } 859 }); 860 startPreviewThread.start(); 861 862 Util.enterLightsOutMode(getWindow()); 863 864 // don't set mSurfaceHolder here. We have it set ONLY within 865 // surfaceChanged / surfaceDestroyed, other parts of the code 866 // assume that when it is set, the surface is also set. 867 SurfaceView preview = (SurfaceView) findViewById(R.id.camera_preview); 868 SurfaceHolder holder = preview.getHolder(); 869 holder.addCallback(this); 870 holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 871 872 if (mIsImageCaptureIntent) { 873 setupCaptureParams(); 874 } else { 875 mModePicker = (ModePicker) findViewById(R.id.mode_picker); 876 mModePicker.setVisibility(View.VISIBLE); 877 mModePicker.setOnModeChangeListener(this); 878 mModePicker.setCurrentMode(ModePicker.MODE_CAMERA); 879 } 880 881 mZoomControl = (ZoomControl) findViewById(R.id.zoom_control); 882 mLocationManager = new LocationManager(this, this); 883 884 // Make sure preview is started. 885 try { 886 startPreviewThread.join(); 887 if (mOpenCameraFail) { 888 Util.showErrorAndFinish(this, R.string.cannot_connect_camera); 889 return; 890 } else if (mCameraDisabled) { 891 Util.showErrorAndFinish(this, R.string.camera_disabled); 892 return; 893 } 894 } catch (InterruptedException ex) { 895 // ignore 896 } 897 898 mBackCameraId = CameraHolder.instance().getBackCameraId(); 899 mFrontCameraId = CameraHolder.instance().getFrontCameraId(); 900 901 // Do this after starting preview because it depends on camera 902 // parameters. 903 initializeIndicatorControl(); 904 } 905 906 private void overrideCameraSettings(final String flashMode, 907 final String whiteBalance, final String focusMode) { 908 if (mIndicatorControlContainer != null) { 909 mIndicatorControlContainer.overrideSettings( 910 CameraSettings.KEY_FLASH_MODE, flashMode, 911 CameraSettings.KEY_WHITE_BALANCE, whiteBalance, 912 CameraSettings.KEY_FOCUS_MODE, focusMode); 913 } 914 } 915 916 private void updateSceneModeUI() { 917 // If scene mode is set, we cannot set flash mode, white balance, and 918 // focus mode, instead, we read it from driver 919 if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) { 920 overrideCameraSettings(mParameters.getFlashMode(), 921 mParameters.getWhiteBalance(), mParameters.getFocusMode()); 922 } else { 923 overrideCameraSettings(null, null, null); 924 } 925 } 926 927 private void loadCameraPreferences() { 928 CameraSettings settings = new CameraSettings(this, mInitialParams, 929 mCameraId, CameraHolder.instance().getCameraInfo()); 930 mPreferenceGroup = settings.getPreferenceGroup(R.xml.camera_preferences); 931 } 932 933 private void initializeIndicatorControl() { 934 // setting the indicator buttons. 935 mIndicatorControlContainer = 936 (IndicatorControlContainer) findViewById(R.id.indicator_control); 937 if (mIndicatorControlContainer == null) return; 938 loadCameraPreferences(); 939 final String[] SETTING_KEYS = { 940 CameraSettings.KEY_FLASH_MODE, 941 CameraSettings.KEY_WHITE_BALANCE, 942 CameraSettings.KEY_SCENE_MODE}; 943 final String[] OTHER_SETTING_KEYS = { 944 CameraSettings.KEY_RECORD_LOCATION, 945 CameraSettings.KEY_FOCUS_MODE, 946 CameraSettings.KEY_EXPOSURE, 947 CameraSettings.KEY_PICTURE_SIZE}; 948 949 CameraPicker.setImageResourceId(R.drawable.ic_switch_photo_facing_holo_light); 950 mIndicatorControlContainer.initialize(this, mPreferenceGroup, 951 mParameters.isZoomSupported(), 952 SETTING_KEYS, OTHER_SETTING_KEYS); 953 updateSceneModeUI(); 954 mIndicatorControlContainer.setListener(this); 955 } 956 957 private boolean collapseCameraControls() { 958 if ((mIndicatorControlContainer != null) 959 && mIndicatorControlContainer.dismissSettingPopup()) { 960 return true; 961 } 962 return false; 963 } 964 965 private void enableCameraControls(boolean enable) { 966 if (mIndicatorControlContainer != null) { 967 mIndicatorControlContainer.setEnabled(enable); 968 } 969 if (mModePicker != null) mModePicker.setEnabled(enable); 970 if (mZoomControl != null) mZoomControl.setEnabled(enable); 971 } 972 973 public static int roundOrientation(int orientation) { 974 return ((orientation + 45) / 90 * 90) % 360; 975 } 976 977 private class MyOrientationEventListener 978 extends OrientationEventListener { 979 public MyOrientationEventListener(Context context) { 980 super(context); 981 } 982 983 @Override 984 public void onOrientationChanged(int orientation) { 985 // We keep the last known orientation. So if the user first orient 986 // the camera then point the camera to floor or sky, we still have 987 // the correct orientation. 988 if (orientation == ORIENTATION_UNKNOWN) return; 989 mOrientation = roundOrientation(orientation); 990 // When the screen is unlocked, display rotation may change. Always 991 // calculate the up-to-date orientationCompensation. 992 int orientationCompensation = mOrientation 993 + Util.getDisplayRotation(Camera.this); 994 if (mOrientationCompensation != orientationCompensation) { 995 mOrientationCompensation = orientationCompensation; 996 setOrientationIndicator(mOrientationCompensation); 997 } 998 999 // Show the toast after getting the first orientation changed. 1000 if (mHandler.hasMessages(SHOW_TAP_TO_FOCUS_TOAST)) { 1001 mHandler.removeMessages(SHOW_TAP_TO_FOCUS_TOAST); 1002 showTapToFocusToast(); 1003 } 1004 } 1005 } 1006 1007 private void setOrientationIndicator(int degree) { 1008 if (mThumbnailView != null) mThumbnailView.setDegree(degree); 1009 if (mModePicker != null) mModePicker.setDegree(degree); 1010 if (mSharePopup != null) mSharePopup.setOrientation(degree); 1011 if (mIndicatorControlContainer != null) mIndicatorControlContainer.setDegree(degree); 1012 if (mZoomControl != null) mZoomControl.setDegree(degree); 1013 if (mFocusIndicator != null) mFocusIndicator.setOrientation(degree); 1014 if (mFaceView != null) mFaceView.setOrientation(degree); 1015 } 1016 1017 @Override 1018 public void onStop() { 1019 super.onStop(); 1020 if (mMediaProviderClient != null) { 1021 mMediaProviderClient.release(); 1022 mMediaProviderClient = null; 1023 } 1024 } 1025 1026 private void checkStorage() { 1027 mPicturesRemaining = Storage.getAvailableSpace(); 1028 if (mPicturesRemaining > 0) { 1029 mPicturesRemaining /= 1500000; 1030 } 1031 updateStorageHint(); 1032 } 1033 1034 @OnClickAttr 1035 public void onThumbnailClicked(View v) { 1036 if (isCameraIdle() && mThumbnail != null) { 1037 showSharePopup(); 1038 } 1039 } 1040 1041 @OnClickAttr 1042 public void onRetakeButtonClicked(View v) { 1043 hidePostCaptureAlert(); 1044 startPreview(); 1045 } 1046 1047 @OnClickAttr 1048 public void onDoneButtonClicked(View v) { 1049 doAttach(); 1050 } 1051 1052 @OnClickAttr 1053 public void onCancelButtonClicked(View v) { 1054 doCancel(); 1055 } 1056 1057 private void doAttach() { 1058 if (mPausing) { 1059 return; 1060 } 1061 1062 byte[] data = mJpegImageData; 1063 1064 if (mCropValue == null) { 1065 // First handle the no crop case -- just return the value. If the 1066 // caller specifies a "save uri" then write the data to it's 1067 // stream. Otherwise, pass back a scaled down version of the bitmap 1068 // directly in the extras. 1069 if (mSaveUri != null) { 1070 OutputStream outputStream = null; 1071 try { 1072 outputStream = mContentResolver.openOutputStream(mSaveUri); 1073 outputStream.write(data); 1074 outputStream.close(); 1075 1076 setResultEx(RESULT_OK); 1077 finish(); 1078 } catch (IOException ex) { 1079 // ignore exception 1080 } finally { 1081 Util.closeSilently(outputStream); 1082 } 1083 } else { 1084 int orientation = Exif.getOrientation(data); 1085 Bitmap bitmap = Util.makeBitmap(data, 50 * 1024); 1086 bitmap = Util.rotate(bitmap, orientation); 1087 setResultEx(RESULT_OK, 1088 new Intent("inline-data").putExtra("data", bitmap)); 1089 finish(); 1090 } 1091 } else { 1092 // Save the image to a temp file and invoke the cropper 1093 Uri tempUri = null; 1094 FileOutputStream tempStream = null; 1095 try { 1096 File path = getFileStreamPath(sTempCropFilename); 1097 path.delete(); 1098 tempStream = openFileOutput(sTempCropFilename, 0); 1099 tempStream.write(data); 1100 tempStream.close(); 1101 tempUri = Uri.fromFile(path); 1102 } catch (FileNotFoundException ex) { 1103 setResultEx(Activity.RESULT_CANCELED); 1104 finish(); 1105 return; 1106 } catch (IOException ex) { 1107 setResultEx(Activity.RESULT_CANCELED); 1108 finish(); 1109 return; 1110 } finally { 1111 Util.closeSilently(tempStream); 1112 } 1113 1114 Bundle newExtras = new Bundle(); 1115 if (mCropValue.equals("circle")) { 1116 newExtras.putString("circleCrop", "true"); 1117 } 1118 if (mSaveUri != null) { 1119 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri); 1120 } else { 1121 newExtras.putBoolean("return-data", true); 1122 } 1123 1124 Intent cropIntent = new Intent("com.android.camera.action.CROP"); 1125 1126 cropIntent.setData(tempUri); 1127 cropIntent.putExtras(newExtras); 1128 1129 startActivityForResult(cropIntent, CROP_MSG); 1130 } 1131 } 1132 1133 private void doCancel() { 1134 setResultEx(RESULT_CANCELED, new Intent()); 1135 finish(); 1136 } 1137 1138 public void onShutterButtonFocus(ShutterButton button, boolean pressed) { 1139 switch (button.getId()) { 1140 case R.id.shutter_button: 1141 doFocus(pressed); 1142 break; 1143 } 1144 } 1145 1146 public void onShutterButtonClick(ShutterButton button) { 1147 switch (button.getId()) { 1148 case R.id.shutter_button: 1149 doSnap(); 1150 break; 1151 } 1152 } 1153 1154 private OnScreenHint mStorageHint; 1155 1156 private void updateStorageHint() { 1157 String noStorageText = null; 1158 1159 if (mPicturesRemaining == Storage.UNAVAILABLE) { 1160 noStorageText = getString(R.string.no_storage); 1161 } else if (mPicturesRemaining == Storage.PREPARING) { 1162 noStorageText = getString(R.string.preparing_sd); 1163 } else if (mPicturesRemaining == Storage.UNKNOWN_SIZE) { 1164 noStorageText = getString(R.string.access_sd_fail); 1165 } else if (mPicturesRemaining < 1L) { 1166 noStorageText = getString(R.string.not_enough_space); 1167 } 1168 1169 if (noStorageText != null) { 1170 if (mStorageHint == null) { 1171 mStorageHint = OnScreenHint.makeText(this, noStorageText); 1172 } else { 1173 mStorageHint.setText(noStorageText); 1174 } 1175 mStorageHint.show(); 1176 } else if (mStorageHint != null) { 1177 mStorageHint.cancel(); 1178 mStorageHint = null; 1179 } 1180 } 1181 1182 private void installIntentFilter() { 1183 // install an intent filter to receive SD card related events. 1184 IntentFilter intentFilter = 1185 new IntentFilter(Intent.ACTION_MEDIA_MOUNTED); 1186 intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED); 1187 intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED); 1188 intentFilter.addAction(Intent.ACTION_MEDIA_CHECKING); 1189 intentFilter.addDataScheme("file"); 1190 registerReceiver(mReceiver, intentFilter); 1191 mDidRegister = true; 1192 } 1193 1194 @Override 1195 protected void onResume() { 1196 super.onResume(); 1197 mPausing = false; 1198 if (mOpenCameraFail || mCameraDisabled) return; 1199 1200 mJpegPictureCallbackTime = 0; 1201 mZoomValue = 0; 1202 1203 // Start the preview if it is not started. 1204 if (mCameraState == PREVIEW_STOPPED) { 1205 try { 1206 mCameraDevice = Util.openCamera(this, mCameraId); 1207 initializeCapabilities(); 1208 resetExposureCompensation(); 1209 startPreview(); 1210 } catch(CameraHardwareException e) { 1211 Util.showErrorAndFinish(this, R.string.cannot_connect_camera); 1212 return; 1213 } catch(CameraDisabledException e) { 1214 Util.showErrorAndFinish(this, R.string.camera_disabled); 1215 return; 1216 } 1217 } 1218 1219 if (mSurfaceHolder != null) { 1220 // If first time initialization is not finished, put it in the 1221 // message queue. 1222 if (!mFirstTimeInitialized) { 1223 mHandler.sendEmptyMessage(FIRST_TIME_INIT); 1224 } else { 1225 initializeSecondTime(); 1226 } 1227 } 1228 keepScreenOnAwhile(); 1229 1230 if (mCameraState == IDLE) { 1231 mOnResumeTime = SystemClock.uptimeMillis(); 1232 mHandler.sendEmptyMessageDelayed(CHECK_DISPLAY_ROTATION, 100); 1233 } 1234 } 1235 1236 @Override 1237 protected void onPause() { 1238 mPausing = true; 1239 stopPreview(); 1240 // Close the camera now because other activities may need to use it. 1241 closeCamera(); 1242 resetScreenOn(); 1243 1244 // Clear UI. 1245 collapseCameraControls(); 1246 if (mSharePopup != null) mSharePopup.dismiss(); 1247 if (mFaceView != null) mFaceView.clear(); 1248 1249 if (mFirstTimeInitialized) { 1250 mOrientationListener.disable(); 1251 if (!mIsImageCaptureIntent) { 1252 if (mThumbnail != null) { 1253 mThumbnail.saveTo(new File(getFilesDir(), Thumbnail.LAST_THUMB_FILENAME)); 1254 } 1255 } 1256 } 1257 1258 if (mDidRegister) { 1259 unregisterReceiver(mReceiver); 1260 mDidRegister = false; 1261 } 1262 mLocationManager.recordLocation(false); 1263 updateExposureOnScreenIndicator(0); 1264 1265 mFocusManager.releaseToneGenerator(); 1266 1267 if (mStorageHint != null) { 1268 mStorageHint.cancel(); 1269 mStorageHint = null; 1270 } 1271 1272 // If we are in an image capture intent and has taken 1273 // a picture, we just clear it in onPause. 1274 mJpegImageData = null; 1275 1276 // Remove the messages in the event queue. 1277 mHandler.removeMessages(FIRST_TIME_INIT); 1278 mHandler.removeMessages(CHECK_DISPLAY_ROTATION); 1279 mFocusManager.removeMessages(); 1280 1281 super.onPause(); 1282 } 1283 1284 @Override 1285 protected void onActivityResult( 1286 int requestCode, int resultCode, Intent data) { 1287 switch (requestCode) { 1288 case CROP_MSG: { 1289 Intent intent = new Intent(); 1290 if (data != null) { 1291 Bundle extras = data.getExtras(); 1292 if (extras != null) { 1293 intent.putExtras(extras); 1294 } 1295 } 1296 setResultEx(resultCode, intent); 1297 finish(); 1298 1299 File path = getFileStreamPath(sTempCropFilename); 1300 path.delete(); 1301 1302 break; 1303 } 1304 } 1305 } 1306 1307 private boolean canTakePicture() { 1308 return isCameraIdle() && (mPicturesRemaining > 0); 1309 } 1310 1311 @Override 1312 public void autoFocus() { 1313 mFocusStartTime = System.currentTimeMillis(); 1314 mCameraDevice.autoFocus(mAutoFocusCallback); 1315 mCameraState = FOCUSING; 1316 enableCameraControls(false); 1317 } 1318 1319 @Override 1320 public void cancelAutoFocus() { 1321 mCameraDevice.cancelAutoFocus(); 1322 mCameraState = IDLE; 1323 enableCameraControls(true); 1324 setCameraParameters(UPDATE_PARAM_PREFERENCE); 1325 } 1326 1327 // Preview area is touched. Handle touch focus. 1328 @Override 1329 public boolean onTouch(View v, MotionEvent e) { 1330 if (mPausing || mCameraDevice == null || !mFirstTimeInitialized 1331 || mCameraState == SNAPSHOT_IN_PROGRESS) { 1332 return false; 1333 } 1334 1335 // Do not trigger touch focus if popup window is opened. 1336 if (collapseCameraControls()) return false; 1337 1338 // Check if metering area or focus area is supported. 1339 if (!mFocusAreaSupported && !mMeteringAreaSupported) return false; 1340 1341 return mFocusManager.onTouch(e); 1342 } 1343 1344 @Override 1345 public void onBackPressed() { 1346 if (!isCameraIdle()) { 1347 // ignore backs while we're taking a picture 1348 return; 1349 } else if (!collapseCameraControls()) { 1350 super.onBackPressed(); 1351 } 1352 } 1353 1354 @Override 1355 public boolean onKeyDown(int keyCode, KeyEvent event) { 1356 switch (keyCode) { 1357 case KeyEvent.KEYCODE_FOCUS: 1358 if (mFirstTimeInitialized && event.getRepeatCount() == 0) { 1359 doFocus(true); 1360 } 1361 return true; 1362 case KeyEvent.KEYCODE_CAMERA: 1363 if (mFirstTimeInitialized && event.getRepeatCount() == 0) { 1364 doSnap(); 1365 } 1366 return true; 1367 case KeyEvent.KEYCODE_DPAD_CENTER: 1368 // If we get a dpad center event without any focused view, move 1369 // the focus to the shutter button and press it. 1370 if (mFirstTimeInitialized && event.getRepeatCount() == 0) { 1371 // Start auto-focus immediately to reduce shutter lag. After 1372 // the shutter button gets the focus, doFocus() will be 1373 // called again but it is fine. 1374 if (collapseCameraControls()) return true; 1375 doFocus(true); 1376 if (mShutterButton.isInTouchMode()) { 1377 mShutterButton.requestFocusFromTouch(); 1378 } else { 1379 mShutterButton.requestFocus(); 1380 } 1381 mShutterButton.setPressed(true); 1382 } 1383 return true; 1384 } 1385 1386 return super.onKeyDown(keyCode, event); 1387 } 1388 1389 @Override 1390 public boolean onKeyUp(int keyCode, KeyEvent event) { 1391 switch (keyCode) { 1392 case KeyEvent.KEYCODE_FOCUS: 1393 if (mFirstTimeInitialized) { 1394 doFocus(false); 1395 } 1396 return true; 1397 } 1398 return super.onKeyUp(keyCode, event); 1399 } 1400 1401 private void doSnap() { 1402 if (mPausing || collapseCameraControls()) return; 1403 1404 // Do not take the picture if there is not enough storage. 1405 if (mPicturesRemaining <= 0) { 1406 Log.i(TAG, "Not enough space or storage not ready. remaining=" + mPicturesRemaining); 1407 return; 1408 } 1409 1410 Log.v(TAG, "doSnap: mCameraState=" + mCameraState); 1411 mFocusManager.doSnap(); 1412 } 1413 1414 private void doFocus(boolean pressed) { 1415 if (mPausing || collapseCameraControls()) return; 1416 1417 // Do not do focus if there is not enough storage. 1418 if (pressed && !canTakePicture()) return; 1419 1420 mFocusManager.doFocus(pressed); 1421 } 1422 1423 public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { 1424 // Make sure we have a surface in the holder before proceeding. 1425 if (holder.getSurface() == null) { 1426 Log.d(TAG, "holder.getSurface() == null"); 1427 return; 1428 } 1429 1430 Log.v(TAG, "surfaceChanged. w=" + w + ". h=" + h); 1431 1432 // We need to save the holder for later use, even when the mCameraDevice 1433 // is null. This could happen if onResume() is invoked after this 1434 // function. 1435 mSurfaceHolder = holder; 1436 1437 // The mCameraDevice will be null if it fails to connect to the camera 1438 // hardware. In this case we will show a dialog and then finish the 1439 // activity, so it's OK to ignore it. 1440 if (mCameraDevice == null) return; 1441 1442 // Sometimes surfaceChanged is called after onPause or before onResume. 1443 // Ignore it. 1444 if (mPausing || isFinishing()) return; 1445 1446 // Set preview display if the surface is being created. Preview was 1447 // already started. Also restart the preview if display rotation has 1448 // changed. Sometimes this happens when the device is held in portrait 1449 // and camera app is opened. Rotation animation takes some time and 1450 // display rotation in onCreate may not be what we want. 1451 if (mCameraState != PREVIEW_STOPPED 1452 && (Util.getDisplayRotation(this) == mDisplayRotation) 1453 && holder.isCreating()) { 1454 // Set preview display if the surface is being created and preview 1455 // was already started. That means preview display was set to null 1456 // and we need to set it now. 1457 setPreviewDisplay(holder); 1458 } else { 1459 // 1. Restart the preview if the size of surface was changed. The 1460 // framework may not support changing preview display on the fly. 1461 // 2. Start the preview now if surface was destroyed and preview 1462 // stopped. 1463 startPreview(); 1464 } 1465 1466 // If first time initialization is not finished, send a message to do 1467 // it later. We want to finish surfaceChanged as soon as possible to let 1468 // user see preview first. 1469 if (!mFirstTimeInitialized) { 1470 mHandler.sendEmptyMessage(FIRST_TIME_INIT); 1471 } else { 1472 initializeSecondTime(); 1473 } 1474 } 1475 1476 public void surfaceCreated(SurfaceHolder holder) { 1477 } 1478 1479 public void surfaceDestroyed(SurfaceHolder holder) { 1480 stopPreview(); 1481 mSurfaceHolder = null; 1482 } 1483 1484 private void closeCamera() { 1485 if (mCameraDevice != null) { 1486 CameraHolder.instance().release(); 1487 mCameraDevice.setZoomChangeListener(null); 1488 mCameraDevice.setFaceDetectionListener(null); 1489 mCameraDevice = null; 1490 mCameraState = PREVIEW_STOPPED; 1491 mFocusManager.onCameraReleased(); 1492 } 1493 } 1494 1495 private void setPreviewDisplay(SurfaceHolder holder) { 1496 try { 1497 mCameraDevice.setPreviewDisplay(holder); 1498 } catch (Throwable ex) { 1499 closeCamera(); 1500 throw new RuntimeException("setPreviewDisplay failed", ex); 1501 } 1502 } 1503 1504 private void startPreview() { 1505 if (mPausing || isFinishing()) return; 1506 1507 mFocusManager.resetTouchFocus(); 1508 1509 mCameraDevice.setErrorCallback(mErrorCallback); 1510 1511 // If we're previewing already, stop the preview first (this will blank 1512 // the screen). 1513 if (mCameraState != PREVIEW_STOPPED) stopPreview(); 1514 1515 setPreviewDisplay(mSurfaceHolder); 1516 mDisplayRotation = Util.getDisplayRotation(this); 1517 mDisplayOrientation = Util.getDisplayOrientation(mDisplayRotation, mCameraId); 1518 mCameraDevice.setDisplayOrientation(mDisplayOrientation); 1519 if (mFaceView != null) { 1520 mFaceView.setDisplayOrientation(mDisplayOrientation); 1521 } 1522 setCameraParameters(UPDATE_PARAM_ALL); 1523 1524 try { 1525 Log.v(TAG, "startPreview"); 1526 mCameraDevice.startPreview(); 1527 } catch (Throwable ex) { 1528 closeCamera(); 1529 throw new RuntimeException("startPreview failed", ex); 1530 } 1531 1532 startFaceDetection(); 1533 mZoomState = ZOOM_STOPPED; 1534 mCameraState = IDLE; 1535 mFocusManager.onPreviewStarted(); 1536 } 1537 1538 private void stopPreview() { 1539 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) { 1540 Log.v(TAG, "stopPreview"); 1541 mCameraDevice.stopPreview(); 1542 } 1543 mCameraState = PREVIEW_STOPPED; 1544 mFocusManager.onPreviewStopped(); 1545 } 1546 1547 private static boolean isSupported(String value, List<String> supported) { 1548 return supported == null ? false : supported.indexOf(value) >= 0; 1549 } 1550 1551 private void updateCameraParametersInitialize() { 1552 // Reset preview frame rate to the maximum because it may be lowered by 1553 // video camera application. 1554 List<Integer> frameRates = mParameters.getSupportedPreviewFrameRates(); 1555 if (frameRates != null) { 1556 Integer max = Collections.max(frameRates); 1557 mParameters.setPreviewFrameRate(max); 1558 } 1559 1560 mParameters.setRecordingHint(false); 1561 } 1562 1563 private void updateCameraParametersZoom() { 1564 // Set zoom. 1565 if (mParameters.isZoomSupported()) { 1566 mParameters.setZoom(mZoomValue); 1567 } 1568 } 1569 1570 private void updateCameraParametersPreference() { 1571 if (mFocusAreaSupported) { 1572 mParameters.setFocusAreas(mFocusManager.getTapArea()); 1573 } 1574 1575 if (mMeteringAreaSupported) { 1576 // Use the same area for focus and metering. 1577 mParameters.setMeteringAreas(mFocusManager.getTapArea()); 1578 } 1579 1580 // Set picture size. 1581 String pictureSize = mPreferences.getString( 1582 CameraSettings.KEY_PICTURE_SIZE, null); 1583 if (pictureSize == null) { 1584 CameraSettings.initialCameraPictureSize(this, mParameters); 1585 } else { 1586 List<Size> supported = mParameters.getSupportedPictureSizes(); 1587 CameraSettings.setCameraPictureSize( 1588 pictureSize, supported, mParameters); 1589 } 1590 1591 // Set the preview frame aspect ratio according to the picture size. 1592 Size size = mParameters.getPictureSize(); 1593 1594 mPreviewPanel = findViewById(R.id.frame_layout); 1595 mPreviewFrameLayout = (PreviewFrameLayout) findViewById(R.id.frame); 1596 mPreviewFrameLayout.setAspectRatio((double) size.width / size.height); 1597 1598 // Set a preview size that is closest to the viewfinder height and has 1599 // the right aspect ratio. 1600 List<Size> sizes = mParameters.getSupportedPreviewSizes(); 1601 Size optimalSize = Util.getOptimalPreviewSize(this, 1602 sizes, (double) size.width / size.height); 1603 Size original = mParameters.getPreviewSize(); 1604 if (!original.equals(optimalSize)) { 1605 mParameters.setPreviewSize(optimalSize.width, optimalSize.height); 1606 1607 // Zoom related settings will be changed for different preview 1608 // sizes, so set and read the parameters to get lastest values 1609 mCameraDevice.setParameters(mParameters); 1610 mParameters = mCameraDevice.getParameters(); 1611 } 1612 Log.v(TAG, "Preview size is " + optimalSize.width + "x" + optimalSize.height); 1613 1614 // Since change scene mode may change supported values, 1615 // Set scene mode first, 1616 mSceneMode = mPreferences.getString( 1617 CameraSettings.KEY_SCENE_MODE, 1618 getString(R.string.pref_camera_scenemode_default)); 1619 if (isSupported(mSceneMode, mParameters.getSupportedSceneModes())) { 1620 if (!mParameters.getSceneMode().equals(mSceneMode)) { 1621 mParameters.setSceneMode(mSceneMode); 1622 mCameraDevice.setParameters(mParameters); 1623 1624 // Setting scene mode will change the settings of flash mode, 1625 // white balance, and focus mode. Here we read back the 1626 // parameters, so we can know those settings. 1627 mParameters = mCameraDevice.getParameters(); 1628 } 1629 } else { 1630 mSceneMode = mParameters.getSceneMode(); 1631 if (mSceneMode == null) { 1632 mSceneMode = Parameters.SCENE_MODE_AUTO; 1633 } 1634 } 1635 1636 // Set JPEG quality. 1637 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId, 1638 CameraProfile.QUALITY_HIGH); 1639 mParameters.setJpegQuality(jpegQuality); 1640 1641 // For the following settings, we need to check if the settings are 1642 // still supported by latest driver, if not, ignore the settings. 1643 1644 // Set exposure compensation 1645 int value = CameraSettings.readExposure(mPreferences); 1646 int max = mParameters.getMaxExposureCompensation(); 1647 int min = mParameters.getMinExposureCompensation(); 1648 if (value >= min && value <= max) { 1649 mParameters.setExposureCompensation(value); 1650 } else { 1651 Log.w(TAG, "invalid exposure range: " + value); 1652 } 1653 1654 if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) { 1655 // Set flash mode. 1656 String flashMode = mPreferences.getString( 1657 CameraSettings.KEY_FLASH_MODE, 1658 getString(R.string.pref_camera_flashmode_default)); 1659 List<String> supportedFlash = mParameters.getSupportedFlashModes(); 1660 if (isSupported(flashMode, supportedFlash)) { 1661 mParameters.setFlashMode(flashMode); 1662 } else { 1663 flashMode = mParameters.getFlashMode(); 1664 if (flashMode == null) { 1665 flashMode = getString( 1666 R.string.pref_camera_flashmode_no_flash); 1667 } 1668 } 1669 1670 // Set white balance parameter. 1671 String whiteBalance = mPreferences.getString( 1672 CameraSettings.KEY_WHITE_BALANCE, 1673 getString(R.string.pref_camera_whitebalance_default)); 1674 if (isSupported(whiteBalance, 1675 mParameters.getSupportedWhiteBalance())) { 1676 mParameters.setWhiteBalance(whiteBalance); 1677 } else { 1678 whiteBalance = mParameters.getWhiteBalance(); 1679 if (whiteBalance == null) { 1680 whiteBalance = Parameters.WHITE_BALANCE_AUTO; 1681 } 1682 } 1683 1684 // Set focus mode. 1685 mFocusManager.overrideFocusMode(null); 1686 mParameters.setFocusMode(mFocusManager.getFocusMode()); 1687 } else { 1688 mFocusManager.overrideFocusMode(mParameters.getFocusMode()); 1689 } 1690 } 1691 1692 // We separate the parameters into several subsets, so we can update only 1693 // the subsets actually need updating. The PREFERENCE set needs extra 1694 // locking because the preference can be changed from GLThread as well. 1695 private void setCameraParameters(int updateSet) { 1696 mParameters = mCameraDevice.getParameters(); 1697 1698 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) { 1699 updateCameraParametersInitialize(); 1700 } 1701 1702 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) { 1703 updateCameraParametersZoom(); 1704 } 1705 1706 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) { 1707 updateCameraParametersPreference(); 1708 } 1709 1710 mCameraDevice.setParameters(mParameters); 1711 } 1712 1713 // If the Camera is idle, update the parameters immediately, otherwise 1714 // accumulate them in mUpdateSet and update later. 1715 private void setCameraParametersWhenIdle(int additionalUpdateSet) { 1716 mUpdateSet |= additionalUpdateSet; 1717 if (mCameraDevice == null) { 1718 // We will update all the parameters when we open the device, so 1719 // we don't need to do anything now. 1720 mUpdateSet = 0; 1721 return; 1722 } else if (isCameraIdle()) { 1723 setCameraParameters(mUpdateSet); 1724 updateSceneModeUI(); 1725 mUpdateSet = 0; 1726 } else { 1727 if (!mHandler.hasMessages(SET_CAMERA_PARAMETERS_WHEN_IDLE)) { 1728 mHandler.sendEmptyMessageDelayed( 1729 SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000); 1730 } 1731 } 1732 } 1733 1734 private void gotoGallery() { 1735 MenuHelper.gotoCameraImageGallery(this); 1736 } 1737 1738 private boolean isCameraIdle() { 1739 return (mCameraState == IDLE) || (mFocusManager.isFocusCompleted()); 1740 } 1741 1742 private boolean isImageCaptureIntent() { 1743 String action = getIntent().getAction(); 1744 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)); 1745 } 1746 1747 private void setupCaptureParams() { 1748 Bundle myExtras = getIntent().getExtras(); 1749 if (myExtras != null) { 1750 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT); 1751 mCropValue = myExtras.getString("crop"); 1752 } 1753 } 1754 1755 private void showPostCaptureAlert() { 1756 if (mIsImageCaptureIntent) { 1757 Util.fadeOut(mIndicatorControlContainer); 1758 Util.fadeOut(mShutterButton); 1759 1760 int[] pickIds = {R.id.btn_retake, R.id.btn_done}; 1761 for (int id : pickIds) { 1762 Util.fadeIn(findViewById(id)); 1763 } 1764 } 1765 } 1766 1767 private void hidePostCaptureAlert() { 1768 if (mIsImageCaptureIntent) { 1769 enableCameraControls(true); 1770 1771 int[] pickIds = {R.id.btn_retake, R.id.btn_done}; 1772 for (int id : pickIds) { 1773 Util.fadeOut(findViewById(id)); 1774 } 1775 1776 Util.fadeIn(mShutterButton); 1777 Util.fadeIn(mIndicatorControlContainer); 1778 } 1779 } 1780 1781 @Override 1782 public boolean onPrepareOptionsMenu(Menu menu) { 1783 super.onPrepareOptionsMenu(menu); 1784 // Only show the menu when camera is idle. 1785 for (int i = 0; i < menu.size(); i++) { 1786 menu.getItem(i).setVisible(isCameraIdle()); 1787 } 1788 1789 return true; 1790 } 1791 1792 @Override 1793 public boolean onCreateOptionsMenu(Menu menu) { 1794 super.onCreateOptionsMenu(menu); 1795 1796 if (mIsImageCaptureIntent) { 1797 // No options menu for attach mode. 1798 return false; 1799 } else { 1800 addBaseMenuItems(menu); 1801 } 1802 return true; 1803 } 1804 1805 private void addBaseMenuItems(Menu menu) { 1806 MenuHelper.addSwitchModeMenuItem(menu, ModePicker.MODE_VIDEO, new Runnable() { 1807 public void run() { 1808 switchToOtherMode(ModePicker.MODE_VIDEO); 1809 } 1810 }); 1811 MenuHelper.addSwitchModeMenuItem(menu, ModePicker.MODE_PANORAMA, new Runnable() { 1812 public void run() { 1813 switchToOtherMode(ModePicker.MODE_PANORAMA); 1814 } 1815 }); 1816 MenuItem gallery = menu.add(R.string.camera_gallery_photos_text) 1817 .setOnMenuItemClickListener(new OnMenuItemClickListener() { 1818 public boolean onMenuItemClick(MenuItem item) { 1819 gotoGallery(); 1820 return true; 1821 } 1822 }); 1823 gallery.setIcon(android.R.drawable.ic_menu_gallery); 1824 mGalleryItems.add(gallery); 1825 1826 if (mNumberOfCameras > 1) { 1827 menu.add(R.string.switch_camera_id) 1828 .setOnMenuItemClickListener(new OnMenuItemClickListener() { 1829 public boolean onMenuItemClick(MenuItem item) { 1830 CameraSettings.writePreferredCameraId(mPreferences, 1831 ((mCameraId == mFrontCameraId) 1832 ? mBackCameraId : mFrontCameraId)); 1833 onSharedPreferenceChanged(); 1834 return true; 1835 } 1836 }).setIcon(android.R.drawable.ic_menu_camera); 1837 } 1838 } 1839 1840 private boolean switchToOtherMode(int mode) { 1841 if (isFinishing() || !isCameraIdle()) return false; 1842 MenuHelper.gotoMode(mode, Camera.this); 1843 mHandler.removeMessages(FIRST_TIME_INIT); 1844 finish(); 1845 return true; 1846 } 1847 1848 public boolean onModeChanged(int mode) { 1849 if (mode != ModePicker.MODE_CAMERA) { 1850 return switchToOtherMode(mode); 1851 } else { 1852 return true; 1853 } 1854 } 1855 1856 public void onSharedPreferenceChanged() { 1857 // ignore the events after "onPause()" 1858 if (mPausing) return; 1859 1860 boolean recordLocation = RecordLocationPreference.get( 1861 mPreferences, getContentResolver()); 1862 mLocationManager.recordLocation(recordLocation); 1863 1864 int cameraId = CameraSettings.readPreferredCameraId(mPreferences); 1865 if (mCameraId != cameraId) { 1866 // Restart the activity to have a crossfade animation. 1867 // TODO: Use SurfaceTexture to implement a better and faster 1868 // animation. 1869 if (mIsImageCaptureIntent) { 1870 // If the intent is camera capture, stay in camera capture mode. 1871 MenuHelper.gotoCameraMode(this, getIntent()); 1872 } else { 1873 MenuHelper.gotoCameraMode(this); 1874 } 1875 1876 finish(); 1877 } else { 1878 setCameraParametersWhenIdle(UPDATE_PARAM_PREFERENCE); 1879 } 1880 1881 int exposureValue = CameraSettings.readExposure(mPreferences); 1882 updateExposureOnScreenIndicator(exposureValue); 1883 } 1884 1885 @Override 1886 public void onUserInteraction() { 1887 super.onUserInteraction(); 1888 keepScreenOnAwhile(); 1889 } 1890 1891 private void resetScreenOn() { 1892 mHandler.removeMessages(CLEAR_SCREEN_DELAY); 1893 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 1894 } 1895 1896 private void keepScreenOnAwhile() { 1897 mHandler.removeMessages(CLEAR_SCREEN_DELAY); 1898 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 1899 mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY); 1900 } 1901 1902 public void onRestorePreferencesClicked() { 1903 if (mPausing) return; 1904 Runnable runnable = new Runnable() { 1905 public void run() { 1906 restorePreferences(); 1907 } 1908 }; 1909 MenuHelper.confirmAction(this, 1910 getString(R.string.confirm_restore_title), 1911 getString(R.string.confirm_restore_message), 1912 runnable); 1913 } 1914 1915 private void restorePreferences() { 1916 // Reset the zoom. Zoom value is not stored in preference. 1917 if (mParameters.isZoomSupported()) { 1918 mZoomValue = 0; 1919 setCameraParametersWhenIdle(UPDATE_PARAM_ZOOM); 1920 mZoomControl.setZoomIndex(0); 1921 } 1922 if (mIndicatorControlContainer != null) { 1923 mIndicatorControlContainer.dismissSettingPopup(); 1924 CameraSettings.restorePreferences(Camera.this, mPreferences, 1925 mParameters); 1926 mIndicatorControlContainer.reloadPreferences(); 1927 onSharedPreferenceChanged(); 1928 } 1929 } 1930 1931 public void onOverriddenPreferencesClicked() { 1932 if (mPausing) return; 1933 if (mNotSelectableToast == null) { 1934 String str = getResources().getString(R.string.not_selectable_in_scene_mode); 1935 mNotSelectableToast = Toast.makeText(Camera.this, str, Toast.LENGTH_SHORT); 1936 } 1937 mNotSelectableToast.show(); 1938 } 1939 1940 private void showSharePopup() { 1941 Uri uri = mThumbnail.getUri(); 1942 if (mSharePopup == null || !uri.equals(mSharePopup.getUri())) { 1943 // SharePopup window takes the mPreviewPanel as its size reference. 1944 mSharePopup = new SharePopup(this, uri, mThumbnail.getBitmap(), 1945 mOrientationCompensation, mPreviewPanel); 1946 } 1947 mSharePopup.showAtLocation(mThumbnailView, Gravity.NO_GRAVITY, 0, 0); 1948 } 1949 1950 @Override 1951 public void onFaceDetection(Face[] faces, android.hardware.Camera camera) { 1952 mFaceView.setFaces(faces); 1953 } 1954 1955 private void showTapToFocusToast() { 1956 // Show the toast. 1957 RotateLayout v = (RotateLayout) findViewById(R.id.tap_to_focus_prompt); 1958 v.setOrientation(mOrientationCompensation); 1959 v.startAnimation(AnimationUtils.loadAnimation(this, R.anim.on_screen_hint_enter)); 1960 v.setVisibility(View.VISIBLE); 1961 mHandler.sendEmptyMessageDelayed(DISMISS_TAP_TO_FOCUS_TOAST, 5000); 1962 // Clear the preference. 1963 Editor editor = mPreferences.edit(); 1964 editor.putBoolean(CameraSettings.KEY_TAP_TO_FOCUS_PROMPT_SHOWN, false); 1965 editor.apply(); 1966 } 1967 1968 private void initializeCapabilities() { 1969 mInitialParams = mCameraDevice.getParameters(); 1970 mFocusManager.initializeParameters(mInitialParams); 1971 mFocusAreaSupported = (mInitialParams.getMaxNumFocusAreas() > 0 1972 && isSupported(Parameters.FOCUS_MODE_AUTO, 1973 mInitialParams.getSupportedFocusModes())); 1974 mMeteringAreaSupported = (mInitialParams.getMaxNumMeteringAreas() > 0); 1975 } 1976} 1977