Camera.java revision d4a3fb30b2ef7ae03a01661041ff4735e1b4fbf1
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.CameraHeadUpDisplay; 20import com.android.camera.ui.CameraPicker; 21import com.android.camera.ui.FocusRectangle; 22import com.android.camera.ui.GLRootView; 23import com.android.camera.ui.HeadUpDisplay; 24import com.android.camera.ui.IndicatorWheel; 25import com.android.camera.ui.ZoomControllerListener; 26import com.android.camera.ui.ZoomPicker; 27 28import android.app.Activity; 29import android.content.ActivityNotFoundException; 30import android.content.BroadcastReceiver; 31import android.content.ContentProviderClient; 32import android.content.ContentResolver; 33import android.content.Context; 34import android.content.Intent; 35import android.content.IntentFilter; 36import android.content.SharedPreferences.Editor; 37import android.content.res.Configuration; 38import android.content.res.Resources; 39import android.database.Cursor; 40import android.graphics.Bitmap; 41import android.graphics.BitmapFactory; 42import android.hardware.Camera.CameraInfo; 43import android.hardware.Camera.Parameters; 44import android.hardware.Camera.PictureCallback; 45import android.hardware.Camera.Size; 46import android.location.Location; 47import android.location.LocationManager; 48import android.location.LocationProvider; 49import android.media.AudioManager; 50import android.media.ToneGenerator; 51import android.net.Uri; 52import android.os.Build; 53import android.os.Bundle; 54import android.os.Handler; 55import android.os.Looper; 56import android.os.Message; 57import android.os.MessageQueue; 58import android.provider.MediaStore; 59import android.provider.Settings; 60import android.provider.MediaStore.Images.ImageColumns; 61import android.provider.MediaStore.Images.Media; 62import android.util.Log; 63import android.view.GestureDetector; 64import android.view.KeyEvent; 65import android.view.LayoutInflater; 66import android.view.Menu; 67import android.view.MenuItem; 68import android.view.MotionEvent; 69import android.view.OrientationEventListener; 70import android.view.SurfaceHolder; 71import android.view.SurfaceView; 72import android.view.View; 73import android.view.ViewGroup; 74import android.view.ViewStub; 75import android.view.Window; 76import android.view.WindowManager; 77import android.view.MenuItem.OnMenuItemClickListener; 78import android.widget.Button; 79import android.widget.Toast; 80 81import java.io.File; 82import java.io.FileNotFoundException; 83import java.io.FileOutputStream; 84import java.io.IOException; 85import java.io.OutputStream; 86import java.text.SimpleDateFormat; 87import java.util.ArrayList; 88import java.util.Collections; 89import java.util.Date; 90import java.util.HashMap; 91import java.util.List; 92 93/** The Camera activity which can preview and take pictures. */ 94public class Camera extends NoSearchActivity implements View.OnClickListener, 95 ShutterButton.OnShutterButtonListener, SurfaceHolder.Callback, 96 Switcher.OnSwitchListener { 97 98 private static final String TAG = "camera"; 99 100 private static final String LAST_THUMB_PATH = 101 Storage.THUMBNAILS + "/image_last_thumb"; 102 103 private static final int CROP_MSG = 1; 104 private static final int FIRST_TIME_INIT = 2; 105 private static final int RESTART_PREVIEW = 3; 106 private static final int CLEAR_SCREEN_DELAY = 4; 107 private static final int SET_CAMERA_PARAMETERS_WHEN_IDLE = 5; 108 109 // The subset of parameters we need to update in setCameraParameters(). 110 private static final int UPDATE_PARAM_INITIALIZE = 1; 111 private static final int UPDATE_PARAM_ZOOM = 2; 112 private static final int UPDATE_PARAM_PREFERENCE = 4; 113 private static final int UPDATE_PARAM_ALL = -1; 114 115 // When setCameraParametersWhenIdle() is called, we accumulate the subsets 116 // needed to be updated in mUpdateSet. 117 private int mUpdateSet; 118 119 // The brightness settings used when it is set to automatic in the system. 120 // The reason why it is set to 0.7 is just because 1.0 is too bright. 121 private static final float DEFAULT_CAMERA_BRIGHTNESS = 0.7f; 122 123 private static final int SCREEN_DELAY = 2 * 60 * 1000; 124 private static final int FOCUS_BEEP_VOLUME = 100; 125 126 private static final int ZOOM_STOPPED = 0; 127 private static final int ZOOM_START = 1; 128 private static final int ZOOM_STOPPING = 2; 129 130 private int mZoomState = ZOOM_STOPPED; 131 private boolean mSmoothZoomSupported = false; 132 private int mZoomValue; // The current zoom value. 133 private int mZoomMax; 134 private int mTargetZoomValue; 135 private ZoomPicker mZoomPicker; 136 137 private Parameters mParameters; 138 private Parameters mInitialParams; 139 140 private MyOrientationEventListener mOrientationListener; 141 // The device orientation in degrees. Default is unknown. 142 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN; 143 // The orientation compensation for icons and thumbnails. 144 private int mOrientationCompensation = 0; 145 private ComboPreferences mPreferences; 146 147 private static final int IDLE = 1; 148 private static final int SNAPSHOT_IN_PROGRESS = 2; 149 150 private static final boolean SWITCH_CAMERA = true; 151 private static final boolean SWITCH_VIDEO = false; 152 153 private int mStatus = IDLE; 154 private static final String sTempCropFilename = "crop-temp"; 155 156 private android.hardware.Camera mCameraDevice; 157 private ContentProviderClient mMediaProviderClient; 158 private SurfaceView mSurfaceView; 159 private SurfaceHolder mSurfaceHolder = null; 160 private ShutterButton mShutterButton; 161 private FocusRectangle mFocusRectangle; 162 private ToneGenerator mFocusToneGenerator; 163 private GestureDetector mPopupGestureDetector; 164 private SwitcherSet mSwitcher; 165 private boolean mStartPreviewFail = false; 166 167 private GLRootView mGLRootView; 168 169 // The last captured picture. 170 private RotateImageView mThumbnailButton; 171 172 // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true. 173 private String mCropValue; 174 private Uri mSaveUri; 175 176 // GPS on-screen indicator 177 private View mGpsNoSignalView; 178 private View mGpsHasSignalView; 179 180 // Front/Back camera pciker for xlarge layout 181 private CameraPicker mCameraPicker; 182 183 /** 184 * An unpublished intent flag requesting to return as soon as capturing 185 * is completed. 186 * 187 * TODO: consider publishing by moving into MediaStore. 188 */ 189 private final static String EXTRA_QUICK_CAPTURE = 190 "android.intent.extra.quickCapture"; 191 192 private boolean mPreviewing; 193 // The display rotation in degrees. This is only valid when mPreviewing is 194 // true. 195 private int mDisplayRotation; 196 private boolean mPausing; 197 private boolean mFirstTimeInitialized; 198 private boolean mIsImageCaptureIntent; 199 private boolean mRecordLocation; 200 201 private static final int FOCUS_NOT_STARTED = 0; 202 private static final int FOCUSING = 1; 203 private static final int FOCUSING_SNAP_ON_FINISH = 2; 204 private static final int FOCUS_SUCCESS = 3; 205 private static final int FOCUS_FAIL = 4; 206 private int mFocusState = FOCUS_NOT_STARTED; 207 208 private ContentResolver mContentResolver; 209 private boolean mDidRegister = false; 210 211 private final ArrayList<MenuItem> mGalleryItems = new ArrayList<MenuItem>(); 212 213 private LocationManager mLocationManager = null; 214 215 private final ShutterCallback mShutterCallback = new ShutterCallback(); 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 ZoomListener mZoomListener = new ZoomListener(); 223 private final CameraErrorCallback mErrorCallback = new CameraErrorCallback(); 224 225 private long mFocusStartTime; 226 private long mFocusCallbackTime; 227 private long mCaptureStartTime; 228 private long mShutterCallbackTime; 229 private long mPostViewPictureCallbackTime; 230 private long mRawPictureCallbackTime; 231 private long mJpegPictureCallbackTime; 232 private long mPicturesRemaining; 233 private byte[] mJpegImageData; 234 235 // These latency time are for the CameraLatency test. 236 public long mAutoFocusTime; 237 public long mShutterLag; 238 public long mShutterToPictureDisplayedTime; 239 public long mPictureDisplayedToJpegCallbackTime; 240 public long mJpegCallbackFinishTime; 241 242 // Focus mode. Options are pref_camera_focusmode_entryvalues. 243 private String mFocusMode; 244 private String mSceneMode; 245 private Toast mNotSelectableToast; 246 247 private final Handler mHandler = new MainHandler(); 248 // xlarge devices use indicator wheel. Other devices use head-up display. 249 private CameraHeadUpDisplay mHeadUpDisplay; 250 private IndicatorWheel mIndicatorWheel; 251 private PreferenceGroup mPreferenceGroup; 252 253 // multiple cameras support 254 private int mNumberOfCameras; 255 private int mCameraId; 256 private int mFrontCameraId; 257 private int mBackCameraId; 258 259 private boolean mQuickCapture; 260 261 /** 262 * This Handler is used to post message back onto the main thread of the 263 * application 264 */ 265 private class MainHandler extends Handler { 266 @Override 267 public void handleMessage(Message msg) { 268 switch (msg.what) { 269 case RESTART_PREVIEW: { 270 restartPreview(); 271 if (mJpegPictureCallbackTime != 0) { 272 long now = System.currentTimeMillis(); 273 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime; 274 Log.v(TAG, "mJpegCallbackFinishTime = " 275 + mJpegCallbackFinishTime + "ms"); 276 mJpegPictureCallbackTime = 0; 277 } 278 break; 279 } 280 281 case CLEAR_SCREEN_DELAY: { 282 getWindow().clearFlags( 283 WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 284 break; 285 } 286 287 case FIRST_TIME_INIT: { 288 initializeFirstTime(); 289 break; 290 } 291 292 case SET_CAMERA_PARAMETERS_WHEN_IDLE: { 293 setCameraParametersWhenIdle(0); 294 break; 295 } 296 } 297 } 298 } 299 300 private void resetExposureCompensation() { 301 String value = mPreferences.getString(CameraSettings.KEY_EXPOSURE, 302 CameraSettings.EXPOSURE_DEFAULT_VALUE); 303 if (!CameraSettings.EXPOSURE_DEFAULT_VALUE.equals(value)) { 304 Editor editor = mPreferences.edit(); 305 editor.putString(CameraSettings.KEY_EXPOSURE, "0"); 306 editor.apply(); 307 if (mHeadUpDisplay != null) { 308 mHeadUpDisplay.reloadPreferences(); 309 } 310 } 311 } 312 313 private void keepMediaProviderInstance() { 314 // We want to keep a reference to MediaProvider in camera's lifecycle. 315 // TODO: Utilize mMediaProviderClient instance to replace 316 // ContentResolver calls. 317 if (mMediaProviderClient == null) { 318 mMediaProviderClient = getContentResolver() 319 .acquireContentProviderClient(MediaStore.AUTHORITY); 320 } 321 } 322 323 // Snapshots can only be taken after this is called. It should be called 324 // once only. We could have done these things in onCreate() but we want to 325 // make preview screen appear as soon as possible. 326 private void initializeFirstTime() { 327 if (mFirstTimeInitialized) return; 328 329 // Create orientation listenter. This should be done first because it 330 // takes some time to get first orientation. 331 mOrientationListener = new MyOrientationEventListener(Camera.this); 332 mOrientationListener.enable(); 333 334 // Initialize location sevice. 335 mLocationManager = (LocationManager) 336 getSystemService(Context.LOCATION_SERVICE); 337 mRecordLocation = RecordLocationPreference.get( 338 mPreferences, getContentResolver()); 339 initGpsOnScreenIndicator(); 340 if (mRecordLocation) startReceivingLocationUpdates(); 341 342 keepMediaProviderInstance(); 343 checkStorage(); 344 345 // Initialize last picture button. 346 mContentResolver = getContentResolver(); 347 if (!mIsImageCaptureIntent) { 348 findViewById(R.id.camera_switch).setOnClickListener(this); 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 mFocusRectangle = (FocusRectangle) findViewById(R.id.focus_rectangle); 358 updateFocusIndicator(); 359 360 initializeScreenBrightness(); 361 installIntentFilter(); 362 initializeFocusTone(); 363 initializeZoom(); 364 // xlarge devices use indicator wheel. Other devices use head-up display. 365 if (mIndicatorWheel == null) { 366 mHeadUpDisplay = new CameraHeadUpDisplay(this); 367 mHeadUpDisplay.setListener(new MyHeadUpDisplayListener()); 368 initializeHeadUpDisplay(); 369 } 370 mFirstTimeInitialized = true; 371 changeHeadUpDisplayState(); 372 addIdleHandler(); 373 } 374 375 private void addIdleHandler() { 376 MessageQueue queue = Looper.myQueue(); 377 queue.addIdleHandler(new MessageQueue.IdleHandler() { 378 public boolean queueIdle() { 379 Storage.ensureOSXCompatible(); 380 return false; 381 } 382 }); 383 } 384 385 private void initThumbnailButton() { 386 mThumbnailButton = 387 (RotateImageView) findViewById(R.id.review_thumbnail); 388 if (mThumbnailButton != null) { 389 mThumbnailButton.setOnClickListener(this); 390 mThumbnailButton.loadData(LAST_THUMB_PATH); 391 updateThumbnailButton(); 392 } 393 } 394 395 private void updateThumbnailButton() { 396 if (mThumbnailButton == null) return; 397 // Update last image if URI is invalid and the storage is ready. 398 if (!mThumbnailButton.isUriValid() && mPicturesRemaining >= 0) { 399 Storage.Thumbnail thumbnail = 400 Storage.getLastImageThumbnail(mContentResolver); 401 if (thumbnail != null) { 402 mThumbnailButton.setData(thumbnail.getOriginalUri(), 403 thumbnail.getBitmap(mContentResolver)); 404 } else { 405 mThumbnailButton.setData(null, null); 406 } 407 } 408 mThumbnailButton.setVisibility( 409 (mThumbnailButton.getUri() != null) ? View.VISIBLE : View.GONE); 410 } 411 412 // If the activity is paused and resumed, this method will be called in 413 // onResume. 414 private void initializeSecondTime() { 415 // Start orientation listener as soon as possible because it takes 416 // some time to get first orientation. 417 mOrientationListener.enable(); 418 419 // Start location update if needed. 420 mRecordLocation = RecordLocationPreference.get( 421 mPreferences, getContentResolver()); 422 if (mRecordLocation) startReceivingLocationUpdates(); 423 424 installIntentFilter(); 425 initializeFocusTone(); 426 initializeZoom(); 427 changeHeadUpDisplayState(); 428 429 keepMediaProviderInstance(); 430 checkStorage(); 431 432 if (!mIsImageCaptureIntent) { 433 updateThumbnailButton(); 434 } 435 } 436 437 private void initializeZoom() { 438 if (!mParameters.isZoomSupported()) return; 439 440 mZoomMax = mParameters.getMaxZoom(); 441 mSmoothZoomSupported = mParameters.isSmoothZoomSupported(); 442 if (mZoomPicker != null) { 443 mZoomPicker.setZoomRatios(getZoomRatios()); 444 mZoomPicker.setZoomIndex(mParameters.getZoom()); 445 mZoomPicker.setSmoothZoomSupported(mSmoothZoomSupported); 446 mZoomPicker.setOnZoomChangeListener( 447 new ZoomPicker.OnZoomChangedListener() { 448 // only for immediate zoom 449 public void onZoomValueChanged(int index) { 450 Camera.this.onZoomValueChanged(index); 451 } 452 453 // only for smooth zoom 454 public void onZoomStateChanged(int state) { 455 if (mPausing) return; 456 457 Log.v(TAG, "zoom picker state=" + state); 458 if (state == mZoomPicker.ZOOM_IN) { 459 Camera.this.onZoomValueChanged(mZoomMax); 460 } else if (state == mZoomPicker.ZOOM_OUT){ 461 Camera.this.onZoomValueChanged(0); 462 } else { 463 mTargetZoomValue = -1; 464 if (mZoomState == ZOOM_START) { 465 mZoomState = ZOOM_STOPPING; 466 mCameraDevice.stopSmoothZoom(); 467 } 468 } 469 } 470 }); 471 } 472 473 mCameraDevice.setZoomChangeListener(mZoomListener); 474 } 475 476 private void onZoomValueChanged(int index) { 477 // Not useful to change zoom value when the activity is paused. 478 if (mPausing) return; 479 480 if (mSmoothZoomSupported) { 481 if (mTargetZoomValue != index && mZoomState != ZOOM_STOPPED) { 482 mTargetZoomValue = index; 483 if (mZoomState == ZOOM_START) { 484 mZoomState = ZOOM_STOPPING; 485 mCameraDevice.stopSmoothZoom(); 486 } 487 } else if (mZoomState == ZOOM_STOPPED && mZoomValue != index) { 488 mTargetZoomValue = index; 489 mCameraDevice.startSmoothZoom(index); 490 mZoomState = ZOOM_START; 491 } 492 } else { 493 mZoomValue = index; 494 setCameraParametersWhenIdle(UPDATE_PARAM_ZOOM); 495 } 496 } 497 498 private float[] getZoomRatios() { 499 if(!mParameters.isZoomSupported()) return null; 500 List<Integer> zoomRatios = mParameters.getZoomRatios(); 501 float result[] = new float[zoomRatios.size()]; 502 for (int i = 0, n = result.length; i < n; ++i) { 503 result[i] = (float) zoomRatios.get(i) / 100f; 504 } 505 return result; 506 } 507 508 private int mLocation[] = new int[2]; 509 private class PopupGestureListener extends 510 GestureDetector.SimpleOnGestureListener { 511 public boolean onDown(MotionEvent e) { 512 // Check if the popup window is visible. 513 View v = mIndicatorWheel.getActivePopupWindow(); 514 if (v == null) return false; 515 516 int x = Math.round(e.getX()); 517 int y = Math.round(e.getY()); 518 519 // Dismiss the popup window if users touch on the outside. 520 v.getLocationOnScreen(mLocation); 521 if (x < mLocation[0] || (x > mLocation[0] + v.getWidth()) 522 || y < mLocation[1] || (y > mLocation[1] + v.getHeight())) { 523 // Let indicator wheel handle its own event. 524 mIndicatorWheel.getLocationOnScreen(mLocation); 525 if (x < mLocation[0] || (x > mLocation[0] + mIndicatorWheel.getWidth()) 526 || y < mLocation[1] || (y > mLocation[1] + mIndicatorWheel.getHeight())) { 527 mIndicatorWheel.dismissSettingPopup(); 528 } 529 // Let event fall through. 530 } 531 return false; 532 } 533 } 534 535 @Override 536 public boolean dispatchTouchEvent(MotionEvent m) { 537 // Check if the popup window should be dismissed first. 538 if (mPopupGestureDetector != null && mPopupGestureDetector.onTouchEvent(m)) { 539 return true; 540 } 541 542 return super.dispatchTouchEvent(m); 543 } 544 545 LocationListener [] mLocationListeners = new LocationListener[] { 546 new LocationListener(LocationManager.GPS_PROVIDER), 547 new LocationListener(LocationManager.NETWORK_PROVIDER) 548 }; 549 550 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 551 @Override 552 public void onReceive(Context context, Intent intent) { 553 String action = intent.getAction(); 554 if (action.equals(Intent.ACTION_MEDIA_MOUNTED) 555 || action.equals(Intent.ACTION_MEDIA_UNMOUNTED) 556 || action.equals(Intent.ACTION_MEDIA_CHECKING)) { 557 checkStorage(); 558 } else if (action.equals(Intent.ACTION_MEDIA_SCANNER_FINISHED)) { 559 checkStorage(); 560 if (!mIsImageCaptureIntent) { 561 updateThumbnailButton(); 562 } 563 } 564 } 565 }; 566 567 private void initializeCameraPicker() { 568 mCameraPicker = (CameraPicker) findViewById(R.id.camera_picker); 569 if (mCameraPicker != null) { 570 mCameraPicker.setImageResource(R.drawable.camera_toggle); 571 ListPreference pref = mPreferenceGroup.findPreference( 572 CameraSettings.KEY_CAMERA_ID); 573 if (pref != null) { 574 mCameraPicker.initialize(pref); 575 mCameraPicker.setListener(new MyCameraPickerListener()); 576 } 577 } 578 } 579 580 private void initGpsOnScreenIndicator() { 581 mGpsNoSignalView = findViewById(R.id.onscreen_gps_indicator_no_signal); 582 mGpsHasSignalView = findViewById(R.id.onscreen_gps_indicator_on); 583 } 584 585 private void showGpsOnScreenIndicator(boolean hasSignal) { 586 if (hasSignal) { 587 if (mGpsNoSignalView != null) mGpsNoSignalView.setVisibility(View.INVISIBLE); 588 if (mGpsHasSignalView != null) mGpsHasSignalView.setVisibility(View.VISIBLE); 589 } else { 590 if (mGpsNoSignalView != null) mGpsNoSignalView.setVisibility(View.VISIBLE); 591 if (mGpsHasSignalView != null) mGpsHasSignalView.setVisibility(View.INVISIBLE); 592 } 593 } 594 595 private void hideGpsOnScreenIndicator() { 596 if (mGpsNoSignalView != null) mGpsNoSignalView.setVisibility(View.INVISIBLE); 597 if (mGpsHasSignalView != null) mGpsHasSignalView.setVisibility(View.INVISIBLE); 598 } 599 600 private class LocationListener 601 implements android.location.LocationListener { 602 Location mLastLocation; 603 boolean mValid = false; 604 String mProvider; 605 606 public LocationListener(String provider) { 607 mProvider = provider; 608 mLastLocation = new Location(mProvider); 609 } 610 611 public void onLocationChanged(Location newLocation) { 612 if (newLocation.getLatitude() == 0.0 613 && newLocation.getLongitude() == 0.0) { 614 // Hack to filter out 0.0,0.0 locations 615 return; 616 } 617 // If GPS is available before start camera, we won't get status 618 // update so update GPS indicator when we receive data. 619 if (mRecordLocation 620 && LocationManager.GPS_PROVIDER.equals(mProvider)) { 621 if (mHeadUpDisplay != null) { 622 mHeadUpDisplay.setGpsHasSignal(true); 623 } 624 showGpsOnScreenIndicator(true); 625 } 626 if (!mValid) { 627 Log.d(TAG, "Got first location."); 628 } 629 mLastLocation.set(newLocation); 630 mValid = true; 631 } 632 633 public void onProviderEnabled(String provider) { 634 } 635 636 public void onProviderDisabled(String provider) { 637 mValid = false; 638 } 639 640 public void onStatusChanged( 641 String provider, int status, Bundle extras) { 642 switch(status) { 643 case LocationProvider.OUT_OF_SERVICE: 644 case LocationProvider.TEMPORARILY_UNAVAILABLE: { 645 mValid = false; 646 if (mRecordLocation && 647 LocationManager.GPS_PROVIDER.equals(provider)) { 648 if (mHeadUpDisplay != null) { 649 mHeadUpDisplay.setGpsHasSignal(false); 650 } 651 showGpsOnScreenIndicator(false); 652 } 653 break; 654 } 655 } 656 } 657 658 public Location current() { 659 return mValid ? mLastLocation : null; 660 } 661 } 662 663 private final class ShutterCallback 664 implements android.hardware.Camera.ShutterCallback { 665 public void onShutter() { 666 mShutterCallbackTime = System.currentTimeMillis(); 667 mShutterLag = mShutterCallbackTime - mCaptureStartTime; 668 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms"); 669 clearFocusState(); 670 } 671 } 672 673 private final class PostViewPictureCallback implements PictureCallback { 674 public void onPictureTaken( 675 byte [] data, android.hardware.Camera camera) { 676 mPostViewPictureCallbackTime = System.currentTimeMillis(); 677 Log.v(TAG, "mShutterToPostViewCallbackTime = " 678 + (mPostViewPictureCallbackTime - mShutterCallbackTime) 679 + "ms"); 680 } 681 } 682 683 private final class RawPictureCallback implements PictureCallback { 684 public void onPictureTaken( 685 byte [] rawData, android.hardware.Camera camera) { 686 mRawPictureCallbackTime = System.currentTimeMillis(); 687 Log.v(TAG, "mShutterToRawCallbackTime = " 688 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms"); 689 } 690 } 691 692 private final class JpegPictureCallback implements PictureCallback { 693 Location mLocation; 694 695 public JpegPictureCallback(Location loc) { 696 mLocation = loc; 697 } 698 699 public void onPictureTaken( 700 final byte [] jpegData, final android.hardware.Camera camera) { 701 if (mPausing) { 702 return; 703 } 704 705 mJpegPictureCallbackTime = System.currentTimeMillis(); 706 // If postview callback has arrived, the captured image is displayed 707 // in postview callback. If not, the captured image is displayed in 708 // raw picture callback. 709 if (mPostViewPictureCallbackTime != 0) { 710 mShutterToPictureDisplayedTime = 711 mPostViewPictureCallbackTime - mShutterCallbackTime; 712 mPictureDisplayedToJpegCallbackTime = 713 mJpegPictureCallbackTime - mPostViewPictureCallbackTime; 714 } else { 715 mShutterToPictureDisplayedTime = 716 mRawPictureCallbackTime - mShutterCallbackTime; 717 mPictureDisplayedToJpegCallbackTime = 718 mJpegPictureCallbackTime - mRawPictureCallbackTime; 719 } 720 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = " 721 + mPictureDisplayedToJpegCallbackTime + "ms"); 722 723 if (!mIsImageCaptureIntent) { 724 enableCameraControls(true); 725 726 // We want to show the taken picture for a while, so we wait 727 // for at least 1.2 second before restarting the preview. 728 long delay = 1200 - mPictureDisplayedToJpegCallbackTime; 729 if (delay < 0) { 730 restartPreview(); 731 } else { 732 mHandler.sendEmptyMessageDelayed(RESTART_PREVIEW, delay); 733 } 734 } 735 storeImage(jpegData, camera, mLocation); 736 737 // Check this in advance of each shot so we don't add to shutter 738 // latency. It's true that someone else could write to the SD card in 739 // the mean time and fill it, but that could have happened between the 740 // shutter press and saving the JPEG too. 741 checkStorage(); 742 743 if (!mHandler.hasMessages(RESTART_PREVIEW)) { 744 long now = System.currentTimeMillis(); 745 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime; 746 Log.v(TAG, "mJpegCallbackFinishTime = " 747 + mJpegCallbackFinishTime + "ms"); 748 mJpegPictureCallbackTime = 0; 749 } 750 } 751 } 752 753 private final class AutoFocusCallback 754 implements android.hardware.Camera.AutoFocusCallback { 755 public void onAutoFocus( 756 boolean focused, android.hardware.Camera camera) { 757 mFocusCallbackTime = System.currentTimeMillis(); 758 mAutoFocusTime = mFocusCallbackTime - mFocusStartTime; 759 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms"); 760 if (mFocusState == FOCUSING_SNAP_ON_FINISH) { 761 // Take the picture no matter focus succeeds or fails. No need 762 // to play the AF sound if we're about to play the shutter 763 // sound. 764 if (focused) { 765 mFocusState = FOCUS_SUCCESS; 766 } else { 767 mFocusState = FOCUS_FAIL; 768 } 769 capture(); 770 } else if (mFocusState == FOCUSING) { 771 // User is half-pressing the focus key. Play the focus tone. 772 // Do not take the picture now. 773 if (focused) { 774 mFocusState = FOCUS_SUCCESS; 775 if (mFocusToneGenerator != null) { 776 mFocusToneGenerator.startTone(ToneGenerator.TONE_PROP_BEEP2); 777 } 778 } else { 779 mFocusState = FOCUS_FAIL; 780 } 781 } else if (mFocusState == FOCUS_NOT_STARTED) { 782 // User has released the focus key before focus completes. 783 // Do nothing. 784 } 785 updateFocusIndicator(); 786 } 787 } 788 789 private final class ZoomListener 790 implements android.hardware.Camera.OnZoomChangeListener { 791 public void onZoomChange( 792 int value, boolean stopped, android.hardware.Camera camera) { 793 Log.v(TAG, "Zoom changed: value=" + value + ". stopped="+ stopped); 794 mZoomValue = value; 795 796 // Update the UI when we get zoom value. 797 if (mZoomPicker != null) mZoomPicker.setZoomIndex(value); 798 799 // Keep mParameters up to date. We do not getParameter again in 800 // takePicture. If we do not do this, wrong zoom value will be set. 801 mParameters.setZoom(value); 802 803 if (stopped && mZoomState != ZOOM_STOPPED) { 804 if (mTargetZoomValue != -1 && value != mTargetZoomValue) { 805 mCameraDevice.startSmoothZoom(mTargetZoomValue); 806 mZoomState = ZOOM_START; 807 } else { 808 mZoomState = ZOOM_STOPPED; 809 } 810 } 811 } 812 } 813 814 public void storeImage(final byte[] data, 815 android.hardware.Camera camera, Location loc) { 816 if (!mIsImageCaptureIntent) { 817 long dateTaken = System.currentTimeMillis(); 818 String title = createName(dateTaken); 819 820 Storage.Thumbnail thumbnail = Storage.addImage( 821 mContentResolver, title, dateTaken, loc, data); 822 823 if (thumbnail != null && mThumbnailButton != null) { 824 mThumbnailButton.setData(thumbnail.getOriginalUri(), 825 thumbnail.getBitmap(mContentResolver)); 826 mThumbnailButton.setVisibility(View.VISIBLE); 827 828 sendBroadcast(new Intent("com.android.camera.NEW_PICTURE", 829 thumbnail.getOriginalUri())); 830 } 831 } else { 832 mJpegImageData = data; 833 if (!mQuickCapture) { 834 showPostCaptureAlert(); 835 } else { 836 doAttach(); 837 } 838 } 839 } 840 841 private void capture() { 842 // If we are already in the middle of taking a snapshot then ignore. 843 if (mPausing || mStatus == SNAPSHOT_IN_PROGRESS || mCameraDevice == null) { 844 return; 845 } 846 mCaptureStartTime = System.currentTimeMillis(); 847 mPostViewPictureCallbackTime = 0; 848 enableCameraControls(false); 849 mStatus = SNAPSHOT_IN_PROGRESS; 850 mJpegImageData = null; 851 852 // See android.hardware.Camera.Parameters.setRotation for 853 // documentation. 854 int rotation = 0; 855 if (mOrientation != OrientationEventListener.ORIENTATION_UNKNOWN) { 856 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId]; 857 if (info.facing == CameraInfo.CAMERA_FACING_FRONT) { 858 rotation = (info.orientation - mOrientation + 360) % 360; 859 } else { // back-facing camera 860 rotation = (info.orientation + mOrientation) % 360; 861 } 862 } 863 mParameters.setRotation(rotation); 864 865 // Clear previous GPS location from the parameters. 866 mParameters.removeGpsData(); 867 868 // We always encode GpsTimeStamp 869 mParameters.setGpsTimestamp(System.currentTimeMillis() / 1000); 870 871 // Set GPS location. 872 Location loc = mRecordLocation ? getCurrentLocation() : null; 873 if (loc != null) { 874 double lat = loc.getLatitude(); 875 double lon = loc.getLongitude(); 876 boolean hasLatLon = (lat != 0.0d) || (lon != 0.0d); 877 878 if (hasLatLon) { 879 Log.d(TAG, "Set gps location"); 880 mParameters.setGpsLatitude(lat); 881 mParameters.setGpsLongitude(lon); 882 mParameters.setGpsProcessingMethod(loc.getProvider().toUpperCase()); 883 if (loc.hasAltitude()) { 884 mParameters.setGpsAltitude(loc.getAltitude()); 885 } else { 886 // for NETWORK_PROVIDER location provider, we may have 887 // no altitude information, but the driver needs it, so 888 // we fake one. 889 mParameters.setGpsAltitude(0); 890 } 891 if (loc.getTime() != 0) { 892 // Location.getTime() is UTC in milliseconds. 893 // gps-timestamp is UTC in seconds. 894 long utcTimeSeconds = loc.getTime() / 1000; 895 mParameters.setGpsTimestamp(utcTimeSeconds); 896 } 897 } else { 898 loc = null; 899 } 900 } 901 902 mCameraDevice.setParameters(mParameters); 903 904 mCameraDevice.takePicture(mShutterCallback, mRawPictureCallback, 905 mPostViewPictureCallback, new JpegPictureCallback(loc)); 906 mPreviewing = false; 907 } 908 909 private boolean saveDataToFile(String filePath, byte[] data) { 910 FileOutputStream f = null; 911 try { 912 f = new FileOutputStream(filePath); 913 f.write(data); 914 } catch (IOException e) { 915 return false; 916 } finally { 917 Util.closeSilently(f); 918 } 919 return true; 920 } 921 922 private String createName(long dateTaken) { 923 Date date = new Date(dateTaken); 924 SimpleDateFormat dateFormat = new SimpleDateFormat( 925 getString(R.string.image_file_name_format)); 926 927 return dateFormat.format(date); 928 } 929 930 @Override 931 public void onCreate(Bundle icicle) { 932 super.onCreate(icicle); 933 934 mIsImageCaptureIntent = isImageCaptureIntent(); 935 if (mIsImageCaptureIntent) { 936 setContentView(R.layout.camera_attach); 937 } else { 938 setContentView(R.layout.camera); 939 } 940 mSurfaceView = (SurfaceView) findViewById(R.id.camera_preview); 941 942 mPreferences = new ComboPreferences(this); 943 CameraSettings.upgradeGlobalPreferences(mPreferences.getGlobal()); 944 945 mCameraId = CameraSettings.readPreferredCameraId(mPreferences); 946 947 //Testing purpose. Launch a specific camera through the intent extras. 948 int intentCameraId = Util.getCameraFacingIntentExtras(this); 949 if (intentCameraId != -1) { 950 mCameraId = intentCameraId; 951 } 952 953 mPreferences.setLocalId(this, mCameraId); 954 CameraSettings.upgradeLocalPreferences(mPreferences.getLocal()); 955 956 mNumberOfCameras = CameraHolder.instance().getNumberOfCameras(); 957 mQuickCapture = getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false); 958 959 // we need to reset exposure for the preview 960 resetExposureCompensation(); 961 /* 962 * To reduce startup time, we start the preview in another thread. 963 * We make sure the preview is started at the end of onCreate. 964 */ 965 Thread startPreviewThread = new Thread(new Runnable() { 966 public void run() { 967 try { 968 mStartPreviewFail = false; 969 startPreview(); 970 } catch (CameraHardwareException e) { 971 // In eng build, we throw the exception so that test tool 972 // can detect it and report it 973 if ("eng".equals(Build.TYPE)) { 974 throw new RuntimeException(e); 975 } 976 mStartPreviewFail = true; 977 } 978 } 979 }); 980 startPreviewThread.start(); 981 982 // don't set mSurfaceHolder here. We have it set ONLY within 983 // surfaceChanged / surfaceDestroyed, other parts of the code 984 // assume that when it is set, the surface is also set. 985 SurfaceHolder holder = mSurfaceView.getHolder(); 986 holder.addCallback(this); 987 holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 988 989 if (mIsImageCaptureIntent) { 990 setupCaptureParams(); 991 992 View reviewControl = findViewById(R.id.review_control); 993 reviewControl.setVisibility(View.VISIBLE); 994 reviewControl.findViewById(R.id.btn_cancel).setOnClickListener(this); 995 reviewControl.findViewById(R.id.btn_retake).setOnClickListener(this); 996 reviewControl.findViewById(R.id.btn_done).setOnClickListener(this); 997 } else { 998 mSwitcher = (SwitcherSet) findViewById(R.id.camera_switch); 999 mSwitcher.setVisibility(View.VISIBLE); 1000 mSwitcher.setOnSwitchListener(this); 1001 } 1002 1003 // Make sure preview is started. 1004 try { 1005 startPreviewThread.join(); 1006 if (mStartPreviewFail) { 1007 showCameraErrorAndFinish(); 1008 return; 1009 } 1010 } catch (InterruptedException ex) { 1011 // ignore 1012 } 1013 1014 mBackCameraId = CameraHolder.instance().getBackCameraId(); 1015 mFrontCameraId = CameraHolder.instance().getFrontCameraId(); 1016 1017 // Do this after starting preview because it depends on camera 1018 // parameters. 1019 initializeIndicatorWheel(); 1020 initializeCameraPicker(); 1021 1022 mZoomPicker = (ZoomPicker) findViewById(R.id.zoom_picker); 1023 if (mZoomPicker != null) mZoomPicker.setEnabled(true); // disabled initially in xml 1024 } 1025 1026 private void changeHeadUpDisplayState() { 1027 if (mHeadUpDisplay == null) return; 1028 // If the camera resumes behind the lock screen, the orientation 1029 // will be portrait. That causes OOM when we try to allocation GPU 1030 // memory for the GLSurfaceView again when the orientation changes. So, 1031 // we delayed initialization of HeadUpDisplay until the orientation 1032 // becomes landscape. 1033 Configuration config = getResources().getConfiguration(); 1034 if (config.orientation == Configuration.ORIENTATION_LANDSCAPE 1035 && !mPausing && mFirstTimeInitialized) { 1036 if (mGLRootView == null) attachHeadUpDisplay(); 1037 } else if (mGLRootView != null) { 1038 detachHeadUpDisplay(); 1039 } 1040 } 1041 1042 private void overrideCameraSettings(final String flashMode, 1043 final String whiteBalance, final String focusMode) { 1044 if (mHeadUpDisplay != null) { 1045 mHeadUpDisplay.overrideSettings( 1046 CameraSettings.KEY_FLASH_MODE, flashMode, 1047 CameraSettings.KEY_WHITE_BALANCE, whiteBalance, 1048 CameraSettings.KEY_FOCUS_MODE, focusMode); 1049 } 1050 if (mIndicatorWheel != null) { 1051 mIndicatorWheel.overrideSettings( 1052 CameraSettings.KEY_FLASH_MODE, flashMode, 1053 CameraSettings.KEY_WHITE_BALANCE, whiteBalance, 1054 CameraSettings.KEY_FOCUS_MODE, focusMode); 1055 } 1056 } 1057 1058 private void updateSceneModeUI() { 1059 // If scene mode is set, we cannot set flash mode, white balance, and 1060 // focus mode, instead, we read it from driver 1061 if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) { 1062 overrideCameraSettings(mParameters.getFlashMode(), 1063 mParameters.getWhiteBalance(), mParameters.getFocusMode()); 1064 } else { 1065 overrideCameraSettings(null, null, null); 1066 } 1067 } 1068 1069 private void loadCameraPreferences() { 1070 CameraSettings settings = new CameraSettings(this, mInitialParams, 1071 mCameraId, CameraHolder.instance().getCameraInfo()); 1072 mPreferenceGroup = settings.getPreferenceGroup(R.xml.camera_preferences); 1073 } 1074 1075 private void initializeIndicatorWheel() { 1076 mIndicatorWheel = (IndicatorWheel) findViewById(R.id.indicator_wheel); 1077 if (mIndicatorWheel == null) return; 1078 loadCameraPreferences(); 1079 1080 String[] keys = new String[]{CameraSettings.KEY_FLASH_MODE, 1081 CameraSettings.KEY_WHITE_BALANCE, 1082 CameraSettings.KEY_COLOR_EFFECT, 1083 CameraSettings.KEY_SCENE_MODE}; 1084 mIndicatorWheel.initialize(this, mPreferenceGroup, keys, true); 1085 mIndicatorWheel.setListener(new MyIndicatorWheelListener()); 1086 mPopupGestureDetector = new GestureDetector(this, 1087 new PopupGestureListener()); 1088 updateSceneModeUI(); 1089 } 1090 1091 private void initializeHeadUpDisplay() { 1092 if (mHeadUpDisplay == null) return; 1093 loadCameraPreferences(); 1094 1095 // If we have zoom picker, do not show zoom control on head-up display. 1096 float[] zoomRatios = null; 1097 if (mZoomPicker == null) zoomRatios = getZoomRatios(); 1098 mHeadUpDisplay.initialize(this, mPreferenceGroup, 1099 zoomRatios, mOrientationCompensation); 1100 if (mZoomPicker == null && mParameters.isZoomSupported()) { 1101 mHeadUpDisplay.setZoomListener(new ZoomControllerListener() { 1102 public void onZoomChanged( 1103 int index, float ratio, boolean isMoving) { 1104 onZoomValueChanged(index); 1105 } 1106 }); 1107 } 1108 updateSceneModeUI(); 1109 } 1110 1111 private void attachHeadUpDisplay() { 1112 mHeadUpDisplay.setOrientation(mOrientationCompensation); 1113 if (mParameters.isZoomSupported()) { 1114 mHeadUpDisplay.setZoomIndex(mZoomValue); 1115 } 1116 ViewGroup frame = (ViewGroup) findViewById(R.id.frame); 1117 mGLRootView = new GLRootView(this); 1118 mGLRootView.setContentPane(mHeadUpDisplay); 1119 frame.addView(mGLRootView); 1120 } 1121 1122 private void detachHeadUpDisplay() { 1123 mHeadUpDisplay.setGpsHasSignal(false); 1124 mHeadUpDisplay.collapse(); 1125 ((ViewGroup) mGLRootView.getParent()).removeView(mGLRootView); 1126 mGLRootView = null; 1127 } 1128 1129 private boolean collapseCameraControls() { 1130 if (mHeadUpDisplay != null && mHeadUpDisplay.collapse()) { 1131 return true; 1132 } 1133 if (mIndicatorWheel != null && mIndicatorWheel.dismissSettingPopup()) { 1134 return true; 1135 } 1136 return false; 1137 } 1138 1139 private void enableCameraControls(boolean enable) { 1140 if (mHeadUpDisplay != null) mHeadUpDisplay.setEnabled(enable); 1141 if (mIndicatorWheel != null) mIndicatorWheel.setEnabled(enable); 1142 if (mCameraPicker != null) mCameraPicker.setEnabled(enable); 1143 if (mZoomPicker != null) mZoomPicker.setEnabled(enable); 1144 if (mSwitcher != null) mSwitcher.setEnabled(enable); 1145 } 1146 1147 public static int roundOrientation(int orientation) { 1148 return ((orientation + 45) / 90 * 90) % 360; 1149 } 1150 1151 private class MyOrientationEventListener 1152 extends OrientationEventListener { 1153 public MyOrientationEventListener(Context context) { 1154 super(context); 1155 } 1156 1157 @Override 1158 public void onOrientationChanged(int orientation) { 1159 // We keep the last known orientation. So if the user first orient 1160 // the camera then point the camera to floor or sky, we still have 1161 // the correct orientation. 1162 if (orientation == ORIENTATION_UNKNOWN) return; 1163 mOrientation = roundOrientation(orientation); 1164 // When the screen is unlocked, display rotation may change. Always 1165 // calculate the up-to-date orientationCompensation. 1166 int orientationCompensation = mOrientation 1167 + Util.getDisplayRotation(Camera.this); 1168 if (mOrientationCompensation != orientationCompensation) { 1169 mOrientationCompensation = orientationCompensation; 1170 if (!mIsImageCaptureIntent) { 1171 setOrientationIndicator(mOrientationCompensation); 1172 } 1173 if (mHeadUpDisplay != null) { 1174 mHeadUpDisplay.setOrientation(mOrientationCompensation); 1175 } 1176 } 1177 } 1178 } 1179 1180 private void setOrientationIndicator(int degree) { 1181 RotateImageView icon = (RotateImageView) findViewById( 1182 R.id.review_thumbnail); 1183 if (icon != null) icon.setDegree(degree); 1184 1185 icon = (RotateImageView) findViewById(R.id.camera_switch_icon); 1186 if (icon != null) icon.setDegree(degree); 1187 icon = (RotateImageView) findViewById(R.id.video_switch_icon); 1188 if (icon != null) icon.setDegree(degree); 1189 } 1190 1191 @Override 1192 public void onStart() { 1193 super.onStart(); 1194 if (!mIsImageCaptureIntent) { 1195 mSwitcher.setSwitch(SWITCH_CAMERA); 1196 } 1197 } 1198 1199 @Override 1200 public void onStop() { 1201 super.onStop(); 1202 if (mMediaProviderClient != null) { 1203 mMediaProviderClient.release(); 1204 mMediaProviderClient = null; 1205 } 1206 } 1207 1208 private void checkStorage() { 1209 mPicturesRemaining = Storage.getAvailableSpace(); 1210 if (mPicturesRemaining > 0) { 1211 mPicturesRemaining /= 1500000; 1212 } 1213 updateStorageHint(); 1214 } 1215 1216 public void onClick(View v) { 1217 switch (v.getId()) { 1218 case R.id.btn_retake: 1219 hidePostCaptureAlert(); 1220 restartPreview(); 1221 break; 1222 case R.id.review_thumbnail: 1223 if (isCameraIdle()) { 1224 viewImage(mThumbnailButton); 1225 } 1226 break; 1227 case R.id.btn_done: 1228 doAttach(); 1229 break; 1230 case R.id.btn_cancel: 1231 doCancel(); 1232 break; 1233 case R.id.btn_gallery: 1234 gotoGallery(); 1235 break; 1236 } 1237 } 1238 1239 private void doAttach() { 1240 if (mPausing) { 1241 return; 1242 } 1243 1244 byte[] data = mJpegImageData; 1245 1246 if (mCropValue == null) { 1247 // First handle the no crop case -- just return the value. If the 1248 // caller specifies a "save uri" then write the data to it's 1249 // stream. Otherwise, pass back a scaled down version of the bitmap 1250 // directly in the extras. 1251 if (mSaveUri != null) { 1252 OutputStream outputStream = null; 1253 try { 1254 outputStream = mContentResolver.openOutputStream(mSaveUri); 1255 outputStream.write(data); 1256 outputStream.close(); 1257 1258 setResult(RESULT_OK); 1259 finish(); 1260 } catch (IOException ex) { 1261 // ignore exception 1262 } finally { 1263 Util.closeSilently(outputStream); 1264 } 1265 } else { 1266 int orientation = Exif.getOrientation(data); 1267 Bitmap bitmap = Util.makeBitmap(data, 50 * 1024); 1268 bitmap = Util.rotate(bitmap, orientation); 1269 setResult(RESULT_OK, 1270 new Intent("inline-data").putExtra("data", bitmap)); 1271 finish(); 1272 } 1273 } else { 1274 // Save the image to a temp file and invoke the cropper 1275 Uri tempUri = null; 1276 FileOutputStream tempStream = null; 1277 try { 1278 File path = getFileStreamPath(sTempCropFilename); 1279 path.delete(); 1280 tempStream = openFileOutput(sTempCropFilename, 0); 1281 tempStream.write(data); 1282 tempStream.close(); 1283 tempUri = Uri.fromFile(path); 1284 } catch (FileNotFoundException ex) { 1285 setResult(Activity.RESULT_CANCELED); 1286 finish(); 1287 return; 1288 } catch (IOException ex) { 1289 setResult(Activity.RESULT_CANCELED); 1290 finish(); 1291 return; 1292 } finally { 1293 Util.closeSilently(tempStream); 1294 } 1295 1296 Bundle newExtras = new Bundle(); 1297 if (mCropValue.equals("circle")) { 1298 newExtras.putString("circleCrop", "true"); 1299 } 1300 if (mSaveUri != null) { 1301 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri); 1302 } else { 1303 newExtras.putBoolean("return-data", true); 1304 } 1305 1306 Intent cropIntent = new Intent("com.android.camera.action.CROP"); 1307 1308 cropIntent.setData(tempUri); 1309 cropIntent.putExtras(newExtras); 1310 1311 startActivityForResult(cropIntent, CROP_MSG); 1312 } 1313 } 1314 1315 private void doCancel() { 1316 setResult(RESULT_CANCELED, new Intent()); 1317 finish(); 1318 } 1319 1320 public void onShutterButtonFocus(ShutterButton button, boolean pressed) { 1321 if (mPausing) { 1322 return; 1323 } 1324 switch (button.getId()) { 1325 case R.id.shutter_button: 1326 doFocus(pressed); 1327 break; 1328 } 1329 } 1330 1331 public void onShutterButtonClick(ShutterButton button) { 1332 if (mPausing) { 1333 return; 1334 } 1335 switch (button.getId()) { 1336 case R.id.shutter_button: 1337 doSnap(); 1338 break; 1339 } 1340 } 1341 1342 private OnScreenHint mStorageHint; 1343 1344 private void updateStorageHint() { 1345 String noStorageText = null; 1346 1347 if (mPicturesRemaining == Storage.UNAVAILABLE) { 1348 noStorageText = getString(R.string.no_storage); 1349 } else if (mPicturesRemaining == Storage.PREPARING) { 1350 noStorageText = getString(R.string.preparing_sd); 1351 } else if (mPicturesRemaining == Storage.UNKNOWN_SIZE) { 1352 noStorageText = getString(R.string.access_sd_fail); 1353 } else if (mPicturesRemaining < 1L) { 1354 noStorageText = getString(R.string.not_enough_space); 1355 } 1356 1357 if (noStorageText != null) { 1358 if (mStorageHint == null) { 1359 mStorageHint = OnScreenHint.makeText(this, noStorageText); 1360 } else { 1361 mStorageHint.setText(noStorageText); 1362 } 1363 mStorageHint.show(); 1364 } else if (mStorageHint != null) { 1365 mStorageHint.cancel(); 1366 mStorageHint = null; 1367 } 1368 } 1369 1370 private void installIntentFilter() { 1371 // install an intent filter to receive SD card related events. 1372 IntentFilter intentFilter = 1373 new IntentFilter(Intent.ACTION_MEDIA_MOUNTED); 1374 intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED); 1375 intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED); 1376 intentFilter.addAction(Intent.ACTION_MEDIA_CHECKING); 1377 intentFilter.addDataScheme("file"); 1378 registerReceiver(mReceiver, intentFilter); 1379 mDidRegister = true; 1380 } 1381 1382 private void initializeFocusTone() { 1383 // Initialize focus tone generator. 1384 try { 1385 mFocusToneGenerator = new ToneGenerator( 1386 AudioManager.STREAM_SYSTEM, FOCUS_BEEP_VOLUME); 1387 } catch (Throwable ex) { 1388 Log.w(TAG, "Exception caught while creating tone generator: ", ex); 1389 mFocusToneGenerator = null; 1390 } 1391 } 1392 1393 private void initializeScreenBrightness() { 1394 Window win = getWindow(); 1395 // Overright the brightness settings if it is automatic 1396 int mode = Settings.System.getInt( 1397 getContentResolver(), 1398 Settings.System.SCREEN_BRIGHTNESS_MODE, 1399 Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL); 1400 if (mode == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC) { 1401 WindowManager.LayoutParams winParams = win.getAttributes(); 1402 winParams.screenBrightness = DEFAULT_CAMERA_BRIGHTNESS; 1403 win.setAttributes(winParams); 1404 } 1405 } 1406 1407 @Override 1408 protected void onResume() { 1409 super.onResume(); 1410 1411 mPausing = false; 1412 mJpegPictureCallbackTime = 0; 1413 mZoomValue = 0; 1414 1415 // Start the preview if it is not started. 1416 if (!mPreviewing && !mStartPreviewFail) { 1417 resetExposureCompensation(); 1418 if (!restartPreview()) return; 1419 } 1420 1421 if (mSurfaceHolder != null) { 1422 // If first time initialization is not finished, put it in the 1423 // message queue. 1424 if (!mFirstTimeInitialized) { 1425 mHandler.sendEmptyMessage(FIRST_TIME_INIT); 1426 } else { 1427 initializeSecondTime(); 1428 } 1429 } 1430 keepScreenOnAwhile(); 1431 } 1432 1433 @Override 1434 public void onConfigurationChanged(Configuration config) { 1435 super.onConfigurationChanged(config); 1436 changeHeadUpDisplayState(); 1437 } 1438 1439 @Override 1440 protected void onPause() { 1441 mPausing = true; 1442 stopPreview(); 1443 // Close the camera now because other activities may need to use it. 1444 closeCamera(); 1445 resetScreenOn(); 1446 collapseCameraControls(); 1447 changeHeadUpDisplayState(); 1448 1449 if (mFirstTimeInitialized) { 1450 mOrientationListener.disable(); 1451 if (!mIsImageCaptureIntent) { 1452 if (mThumbnailButton != null) { 1453 mThumbnailButton.storeData(LAST_THUMB_PATH); 1454 } 1455 } 1456 hidePostCaptureAlert(); 1457 } 1458 1459 if (mDidRegister) { 1460 unregisterReceiver(mReceiver); 1461 mDidRegister = false; 1462 } 1463 stopReceivingLocationUpdates(); 1464 1465 if (mFocusToneGenerator != null) { 1466 mFocusToneGenerator.release(); 1467 mFocusToneGenerator = null; 1468 } 1469 1470 if (mStorageHint != null) { 1471 mStorageHint.cancel(); 1472 mStorageHint = null; 1473 } 1474 1475 // If we are in an image capture intent and has taken 1476 // a picture, we just clear it in onPause. 1477 mJpegImageData = null; 1478 1479 // Remove the messages in the event queue. 1480 mHandler.removeMessages(RESTART_PREVIEW); 1481 mHandler.removeMessages(FIRST_TIME_INIT); 1482 1483 super.onPause(); 1484 } 1485 1486 @Override 1487 protected void onActivityResult( 1488 int requestCode, int resultCode, Intent data) { 1489 switch (requestCode) { 1490 case CROP_MSG: { 1491 Intent intent = new Intent(); 1492 if (data != null) { 1493 Bundle extras = data.getExtras(); 1494 if (extras != null) { 1495 intent.putExtras(extras); 1496 } 1497 } 1498 setResult(resultCode, intent); 1499 finish(); 1500 1501 File path = getFileStreamPath(sTempCropFilename); 1502 path.delete(); 1503 1504 break; 1505 } 1506 } 1507 } 1508 1509 private boolean canTakePicture() { 1510 return isCameraIdle() && mPreviewing && (mPicturesRemaining > 0); 1511 } 1512 1513 private void autoFocus() { 1514 // Initiate autofocus only when preview is started and snapshot is not 1515 // in progress. 1516 if (canTakePicture()) { 1517 enableCameraControls(false); 1518 Log.v(TAG, "Start autofocus."); 1519 mFocusStartTime = System.currentTimeMillis(); 1520 mFocusState = FOCUSING; 1521 updateFocusIndicator(); 1522 mCameraDevice.autoFocus(mAutoFocusCallback); 1523 } 1524 } 1525 1526 private void cancelAutoFocus() { 1527 // User releases half-pressed focus key. 1528 if (mStatus != SNAPSHOT_IN_PROGRESS && (mFocusState == FOCUSING 1529 || mFocusState == FOCUS_SUCCESS || mFocusState == FOCUS_FAIL)) { 1530 Log.v(TAG, "Cancel autofocus."); 1531 enableCameraControls(true); 1532 mCameraDevice.cancelAutoFocus(); 1533 } 1534 if (mFocusState != FOCUSING_SNAP_ON_FINISH) { 1535 clearFocusState(); 1536 } 1537 } 1538 1539 private void clearFocusState() { 1540 mFocusState = FOCUS_NOT_STARTED; 1541 updateFocusIndicator(); 1542 } 1543 1544 private void updateFocusIndicator() { 1545 if (mFocusRectangle == null) return; 1546 1547 if (mFocusState == FOCUSING || mFocusState == FOCUSING_SNAP_ON_FINISH) { 1548 mFocusRectangle.showStart(); 1549 } else if (mFocusState == FOCUS_SUCCESS) { 1550 mFocusRectangle.showSuccess(); 1551 } else if (mFocusState == FOCUS_FAIL) { 1552 mFocusRectangle.showFail(); 1553 } else { 1554 mFocusRectangle.clear(); 1555 } 1556 } 1557 1558 @Override 1559 public void onBackPressed() { 1560 if (!isCameraIdle()) { 1561 // ignore backs while we're taking a picture 1562 return; 1563 } else if (!collapseCameraControls()) { 1564 super.onBackPressed(); 1565 } 1566 } 1567 1568 @Override 1569 public boolean onKeyDown(int keyCode, KeyEvent event) { 1570 switch (keyCode) { 1571 case KeyEvent.KEYCODE_FOCUS: 1572 if (mFirstTimeInitialized && event.getRepeatCount() == 0) { 1573 doFocus(true); 1574 } 1575 return true; 1576 case KeyEvent.KEYCODE_CAMERA: 1577 if (mFirstTimeInitialized && event.getRepeatCount() == 0) { 1578 doSnap(); 1579 } 1580 return true; 1581 case KeyEvent.KEYCODE_DPAD_CENTER: 1582 // If we get a dpad center event without any focused view, move 1583 // the focus to the shutter button and press it. 1584 if (mFirstTimeInitialized && event.getRepeatCount() == 0) { 1585 // Start auto-focus immediately to reduce shutter lag. After 1586 // the shutter button gets the focus, doFocus() will be 1587 // called again but it is fine. 1588 if (collapseCameraControls()) return true; 1589 doFocus(true); 1590 if (mShutterButton.isInTouchMode()) { 1591 mShutterButton.requestFocusFromTouch(); 1592 } else { 1593 mShutterButton.requestFocus(); 1594 } 1595 mShutterButton.setPressed(true); 1596 } 1597 return true; 1598 } 1599 1600 return super.onKeyDown(keyCode, event); 1601 } 1602 1603 @Override 1604 public boolean onKeyUp(int keyCode, KeyEvent event) { 1605 switch (keyCode) { 1606 case KeyEvent.KEYCODE_FOCUS: 1607 if (mFirstTimeInitialized) { 1608 doFocus(false); 1609 } 1610 return true; 1611 } 1612 return super.onKeyUp(keyCode, event); 1613 } 1614 1615 private void doSnap() { 1616 if (collapseCameraControls()) return; 1617 1618 Log.v(TAG, "doSnap: mFocusState=" + mFocusState); 1619 // If the user has half-pressed the shutter and focus is completed, we 1620 // can take the photo right away. If the focus mode is infinity, we can 1621 // also take the photo. 1622 if (mFocusMode.equals(Parameters.FOCUS_MODE_INFINITY) 1623 || mFocusMode.equals(Parameters.FOCUS_MODE_FIXED) 1624 || mFocusMode.equals(Parameters.FOCUS_MODE_EDOF) 1625 || (mFocusState == FOCUS_SUCCESS 1626 || mFocusState == FOCUS_FAIL)) { 1627 capture(); 1628 } else if (mFocusState == FOCUSING) { 1629 // Half pressing the shutter (i.e. the focus button event) will 1630 // already have requested AF for us, so just request capture on 1631 // focus here. 1632 mFocusState = FOCUSING_SNAP_ON_FINISH; 1633 } else if (mFocusState == FOCUS_NOT_STARTED) { 1634 // Focus key down event is dropped for some reasons. Just ignore. 1635 } 1636 } 1637 1638 private void doFocus(boolean pressed) { 1639 // Do the focus if the mode is not infinity. 1640 if (collapseCameraControls()) return; 1641 if (!(mFocusMode.equals(Parameters.FOCUS_MODE_INFINITY) 1642 || mFocusMode.equals(Parameters.FOCUS_MODE_FIXED) 1643 || mFocusMode.equals(Parameters.FOCUS_MODE_EDOF))) { 1644 if (pressed) { // Focus key down. 1645 autoFocus(); 1646 } else { // Focus key up. 1647 cancelAutoFocus(); 1648 } 1649 } 1650 } 1651 1652 public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { 1653 // Make sure we have a surface in the holder before proceeding. 1654 if (holder.getSurface() == null) { 1655 Log.d(TAG, "holder.getSurface() == null"); 1656 return; 1657 } 1658 1659 Log.v(TAG, "surfaceChanged. w=" + w + ". h=" + h); 1660 1661 // We need to save the holder for later use, even when the mCameraDevice 1662 // is null. This could happen if onResume() is invoked after this 1663 // function. 1664 mSurfaceHolder = holder; 1665 1666 // The mCameraDevice will be null if it fails to connect to the camera 1667 // hardware. In this case we will show a dialog and then finish the 1668 // activity, so it's OK to ignore it. 1669 if (mCameraDevice == null) return; 1670 1671 // Sometimes surfaceChanged is called after onPause or before onResume. 1672 // Ignore it. 1673 if (mPausing || isFinishing()) return; 1674 1675 // Set preview display if the surface is being created. Preview was 1676 // already started. Also restart the preview if display rotation has 1677 // changed. Sometimes this happens when the device is held in portrait 1678 // and camera app is opened. Rotation animation takes some time and 1679 // display rotation in onCreate may not be what we want. 1680 if (mPreviewing && (Util.getDisplayRotation(this) == mDisplayRotation) 1681 && holder.isCreating()) { 1682 // Set preview display if the surface is being created and preview 1683 // was already started. That means preview display was set to null 1684 // and we need to set it now. 1685 setPreviewDisplay(holder); 1686 } else { 1687 // 1. Restart the preview if the size of surface was changed. The 1688 // framework may not support changing preview display on the fly. 1689 // 2. Start the preview now if surface was destroyed and preview 1690 // stopped. 1691 restartPreview(); 1692 } 1693 1694 // If first time initialization is not finished, send a message to do 1695 // it later. We want to finish surfaceChanged as soon as possible to let 1696 // user see preview first. 1697 if (!mFirstTimeInitialized) { 1698 mHandler.sendEmptyMessage(FIRST_TIME_INIT); 1699 } else { 1700 initializeSecondTime(); 1701 } 1702 } 1703 1704 public void surfaceCreated(SurfaceHolder holder) { 1705 } 1706 1707 public void surfaceDestroyed(SurfaceHolder holder) { 1708 stopPreview(); 1709 mSurfaceHolder = null; 1710 } 1711 1712 private void closeCamera() { 1713 if (mCameraDevice != null) { 1714 CameraHolder.instance().release(); 1715 mCameraDevice.setZoomChangeListener(null); 1716 mCameraDevice = null; 1717 mPreviewing = false; 1718 } 1719 } 1720 1721 private void ensureCameraDevice() throws CameraHardwareException { 1722 if (mCameraDevice == null) { 1723 mCameraDevice = CameraHolder.instance().open(mCameraId); 1724 mInitialParams = mCameraDevice.getParameters(); 1725 } 1726 } 1727 1728 private void showCameraErrorAndFinish() { 1729 Resources ress = getResources(); 1730 Util.showFatalErrorAndFinish(Camera.this, 1731 ress.getString(R.string.camera_error_title), 1732 ress.getString(R.string.cannot_connect_camera)); 1733 } 1734 1735 private boolean restartPreview() { 1736 try { 1737 startPreview(); 1738 } catch (CameraHardwareException e) { 1739 showCameraErrorAndFinish(); 1740 return false; 1741 } 1742 return true; 1743 } 1744 1745 private void setPreviewDisplay(SurfaceHolder holder) { 1746 try { 1747 mCameraDevice.setPreviewDisplay(holder); 1748 } catch (Throwable ex) { 1749 closeCamera(); 1750 throw new RuntimeException("setPreviewDisplay failed", ex); 1751 } 1752 } 1753 1754 private void startPreview() throws CameraHardwareException { 1755 if (mPausing || isFinishing()) return; 1756 1757 ensureCameraDevice(); 1758 mCameraDevice.setErrorCallback(mErrorCallback); 1759 1760 // If we're previewing already, stop the preview first (this will blank 1761 // the screen). 1762 if (mPreviewing) stopPreview(); 1763 1764 setPreviewDisplay(mSurfaceHolder); 1765 mDisplayRotation = Util.getDisplayRotation(this); 1766 Util.setCameraDisplayOrientation(mDisplayRotation, mCameraId, mCameraDevice); 1767 setCameraParameters(UPDATE_PARAM_ALL); 1768 1769 1770 try { 1771 Log.v(TAG, "startPreview"); 1772 mCameraDevice.startPreview(); 1773 } catch (Throwable ex) { 1774 closeCamera(); 1775 throw new RuntimeException("startPreview failed", ex); 1776 } 1777 mPreviewing = true; 1778 mZoomState = ZOOM_STOPPED; 1779 mStatus = IDLE; 1780 } 1781 1782 private void stopPreview() { 1783 if (mCameraDevice != null && mPreviewing) { 1784 Log.v(TAG, "stopPreview"); 1785 mCameraDevice.stopPreview(); 1786 } 1787 mPreviewing = false; 1788 // If auto focus was in progress, it would have been canceled. 1789 clearFocusState(); 1790 } 1791 1792 private static boolean isSupported(String value, List<String> supported) { 1793 return supported == null ? false : supported.indexOf(value) >= 0; 1794 } 1795 1796 private void updateCameraParametersInitialize() { 1797 // Reset preview frame rate to the maximum because it may be lowered by 1798 // video camera application. 1799 List<Integer> frameRates = mParameters.getSupportedPreviewFrameRates(); 1800 if (frameRates != null) { 1801 Integer max = Collections.max(frameRates); 1802 mParameters.setPreviewFrameRate(max); 1803 } 1804 1805 } 1806 1807 private void updateCameraParametersZoom() { 1808 // Set zoom. 1809 if (mParameters.isZoomSupported()) { 1810 mParameters.setZoom(mZoomValue); 1811 } 1812 } 1813 1814 private void updateCameraParametersPreference() { 1815 // Set picture size. 1816 String pictureSize = mPreferences.getString( 1817 CameraSettings.KEY_PICTURE_SIZE, null); 1818 if (pictureSize == null) { 1819 CameraSettings.initialCameraPictureSize(this, mParameters); 1820 } else { 1821 List<Size> supported = mParameters.getSupportedPictureSizes(); 1822 CameraSettings.setCameraPictureSize( 1823 pictureSize, supported, mParameters); 1824 } 1825 1826 // Set the preview frame aspect ratio according to the picture size. 1827 Size size = mParameters.getPictureSize(); 1828 PreviewFrameLayout frameLayout = 1829 (PreviewFrameLayout) findViewById(R.id.frame_layout); 1830 frameLayout.setAspectRatio((double) size.width / size.height); 1831 1832 // Set a preview size that is closest to the viewfinder height and has 1833 // the right aspect ratio. 1834 List<Size> sizes = mParameters.getSupportedPreviewSizes(); 1835 Size optimalSize = Util.getOptimalPreviewSize(this, 1836 sizes, (double) size.width / size.height); 1837 Size original = mParameters.getPreviewSize(); 1838 if (!original.equals(optimalSize)) { 1839 mParameters.setPreviewSize(optimalSize.width, optimalSize.height); 1840 1841 // Zoom related settings will be changed for different preview 1842 // sizes, so set and read the parameters to get lastest values 1843 mCameraDevice.setParameters(mParameters); 1844 mParameters = mCameraDevice.getParameters(); 1845 } 1846 Log.v(TAG, "Preview size is " + optimalSize.width + "x" + optimalSize.height); 1847 1848 // Since change scene mode may change supported values, 1849 // Set scene mode first, 1850 mSceneMode = mPreferences.getString( 1851 CameraSettings.KEY_SCENE_MODE, 1852 getString(R.string.pref_camera_scenemode_default)); 1853 if (isSupported(mSceneMode, mParameters.getSupportedSceneModes())) { 1854 if (!mParameters.getSceneMode().equals(mSceneMode)) { 1855 mParameters.setSceneMode(mSceneMode); 1856 mCameraDevice.setParameters(mParameters); 1857 1858 // Setting scene mode will change the settings of flash mode, 1859 // white balance, and focus mode. Here we read back the 1860 // parameters, so we can know those settings. 1861 mParameters = mCameraDevice.getParameters(); 1862 } 1863 } else { 1864 mSceneMode = mParameters.getSceneMode(); 1865 if (mSceneMode == null) { 1866 mSceneMode = Parameters.SCENE_MODE_AUTO; 1867 } 1868 } 1869 1870 // Set JPEG quality. 1871 String jpegQuality = mPreferences.getString( 1872 CameraSettings.KEY_JPEG_QUALITY, 1873 getString(R.string.pref_camera_jpegquality_default)); 1874 mParameters.setJpegQuality(JpegEncodingQualityMappings.getQualityNumber(jpegQuality)); 1875 1876 // For the following settings, we need to check if the settings are 1877 // still supported by latest driver, if not, ignore the settings. 1878 1879 // Set color effect parameter. 1880 String colorEffect = mPreferences.getString( 1881 CameraSettings.KEY_COLOR_EFFECT, 1882 getString(R.string.pref_camera_coloreffect_default)); 1883 if (isSupported(colorEffect, mParameters.getSupportedColorEffects())) { 1884 mParameters.setColorEffect(colorEffect); 1885 } 1886 1887 // Set exposure compensation 1888 String exposure = mPreferences.getString( 1889 CameraSettings.KEY_EXPOSURE, 1890 getString(R.string.pref_exposure_default)); 1891 try { 1892 int value = Integer.parseInt(exposure); 1893 int max = mParameters.getMaxExposureCompensation(); 1894 int min = mParameters.getMinExposureCompensation(); 1895 if (value >= min && value <= max) { 1896 mParameters.setExposureCompensation(value); 1897 } else { 1898 Log.w(TAG, "invalid exposure range: " + exposure); 1899 } 1900 } catch (NumberFormatException e) { 1901 Log.w(TAG, "invalid exposure: " + exposure); 1902 } 1903 1904 if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) { 1905 // Set flash mode. 1906 String flashMode = mPreferences.getString( 1907 CameraSettings.KEY_FLASH_MODE, 1908 getString(R.string.pref_camera_flashmode_default)); 1909 List<String> supportedFlash = mParameters.getSupportedFlashModes(); 1910 if (isSupported(flashMode, supportedFlash)) { 1911 mParameters.setFlashMode(flashMode); 1912 } else { 1913 flashMode = mParameters.getFlashMode(); 1914 if (flashMode == null) { 1915 flashMode = getString( 1916 R.string.pref_camera_flashmode_no_flash); 1917 } 1918 } 1919 1920 // Set white balance parameter. 1921 String whiteBalance = mPreferences.getString( 1922 CameraSettings.KEY_WHITE_BALANCE, 1923 getString(R.string.pref_camera_whitebalance_default)); 1924 if (isSupported(whiteBalance, 1925 mParameters.getSupportedWhiteBalance())) { 1926 mParameters.setWhiteBalance(whiteBalance); 1927 } else { 1928 whiteBalance = mParameters.getWhiteBalance(); 1929 if (whiteBalance == null) { 1930 whiteBalance = Parameters.WHITE_BALANCE_AUTO; 1931 } 1932 } 1933 1934 // Set focus mode. 1935 mFocusMode = mPreferences.getString( 1936 CameraSettings.KEY_FOCUS_MODE, 1937 getString(R.string.pref_camera_focusmode_default)); 1938 if (isSupported(mFocusMode, mParameters.getSupportedFocusModes())) { 1939 mParameters.setFocusMode(mFocusMode); 1940 } else { 1941 mFocusMode = mParameters.getFocusMode(); 1942 if (mFocusMode == null) { 1943 mFocusMode = Parameters.FOCUS_MODE_AUTO; 1944 } 1945 } 1946 } else { 1947 mFocusMode = mParameters.getFocusMode(); 1948 } 1949 } 1950 1951 // We separate the parameters into several subsets, so we can update only 1952 // the subsets actually need updating. The PREFERENCE set needs extra 1953 // locking because the preference can be changed from GLThread as well. 1954 private void setCameraParameters(int updateSet) { 1955 mParameters = mCameraDevice.getParameters(); 1956 1957 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) { 1958 updateCameraParametersInitialize(); 1959 } 1960 1961 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) { 1962 updateCameraParametersZoom(); 1963 } 1964 1965 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) { 1966 updateCameraParametersPreference(); 1967 } 1968 1969 mCameraDevice.setParameters(mParameters); 1970 } 1971 1972 // If the Camera is idle, update the parameters immediately, otherwise 1973 // accumulate them in mUpdateSet and update later. 1974 private void setCameraParametersWhenIdle(int additionalUpdateSet) { 1975 mUpdateSet |= additionalUpdateSet; 1976 if (mCameraDevice == null) { 1977 // We will update all the parameters when we open the device, so 1978 // we don't need to do anything now. 1979 mUpdateSet = 0; 1980 return; 1981 } else if (isCameraIdle()) { 1982 setCameraParameters(mUpdateSet); 1983 updateSceneModeUI(); 1984 mUpdateSet = 0; 1985 } else { 1986 if (!mHandler.hasMessages(SET_CAMERA_PARAMETERS_WHEN_IDLE)) { 1987 mHandler.sendEmptyMessageDelayed( 1988 SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000); 1989 } 1990 } 1991 } 1992 1993 private void gotoGallery() { 1994 MenuHelper.gotoCameraImageGallery(this); 1995 } 1996 1997 private void viewImage(RotateImageView view) { 1998 if(!view.isUriValid()) { 1999 Log.e(TAG, "Uri invalid. uri=" + view.getUri()); 2000 return; 2001 } 2002 2003 try { 2004 startActivity(new Intent( 2005 Util.REVIEW_ACTION, view.getUri())); 2006 } catch (ActivityNotFoundException ex) { 2007 try { 2008 startActivity(new Intent( 2009 Intent.ACTION_VIEW, view.getUri())); 2010 } catch (ActivityNotFoundException e) { 2011 Log.e(TAG, "review image fail. uri=" + view.getUri(), e); 2012 } 2013 } 2014 } 2015 2016 private void startReceivingLocationUpdates() { 2017 if (mLocationManager != null) { 2018 try { 2019 mLocationManager.requestLocationUpdates( 2020 LocationManager.NETWORK_PROVIDER, 2021 1000, 2022 0F, 2023 mLocationListeners[1]); 2024 } catch (java.lang.SecurityException ex) { 2025 Log.i(TAG, "fail to request location update, ignore", ex); 2026 } catch (IllegalArgumentException ex) { 2027 Log.d(TAG, "provider does not exist " + ex.getMessage()); 2028 } 2029 try { 2030 mLocationManager.requestLocationUpdates( 2031 LocationManager.GPS_PROVIDER, 2032 1000, 2033 0F, 2034 mLocationListeners[0]); 2035 showGpsOnScreenIndicator(false); 2036 } catch (java.lang.SecurityException ex) { 2037 Log.i(TAG, "fail to request location update, ignore", ex); 2038 } catch (IllegalArgumentException ex) { 2039 Log.d(TAG, "provider does not exist " + ex.getMessage()); 2040 } 2041 Log.d(TAG, "startReceivingLocationUpdates"); 2042 } 2043 } 2044 2045 private void stopReceivingLocationUpdates() { 2046 if (mLocationManager != null) { 2047 for (int i = 0; i < mLocationListeners.length; i++) { 2048 try { 2049 mLocationManager.removeUpdates(mLocationListeners[i]); 2050 } catch (Exception ex) { 2051 Log.i(TAG, "fail to remove location listners, ignore", ex); 2052 } 2053 } 2054 Log.d(TAG, "stopReceivingLocationUpdates"); 2055 } 2056 hideGpsOnScreenIndicator(); 2057 } 2058 2059 private Location getCurrentLocation() { 2060 // go in best to worst order 2061 for (int i = 0; i < mLocationListeners.length; i++) { 2062 Location l = mLocationListeners[i].current(); 2063 if (l != null) return l; 2064 } 2065 Log.d(TAG, "No location received yet."); 2066 return null; 2067 } 2068 2069 private boolean isCameraIdle() { 2070 return mStatus == IDLE && mFocusState == FOCUS_NOT_STARTED; 2071 } 2072 2073 private boolean isImageCaptureIntent() { 2074 String action = getIntent().getAction(); 2075 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)); 2076 } 2077 2078 private void setupCaptureParams() { 2079 Bundle myExtras = getIntent().getExtras(); 2080 if (myExtras != null) { 2081 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT); 2082 mCropValue = myExtras.getString("crop"); 2083 } 2084 } 2085 2086 private void showPostCaptureAlert() { 2087 if (mIsImageCaptureIntent) { 2088 if (mIndicatorWheel == null) { 2089 mShutterButton.setVisibility(View.INVISIBLE); 2090 } else { 2091 mShutterButton.setEnabled(false); 2092 } 2093 int[] pickIds = {R.id.btn_retake, R.id.btn_done}; 2094 for (int id : pickIds) { 2095 View button = findViewById(id); 2096 ((View) button.getParent()).setVisibility(View.VISIBLE); 2097 } 2098 2099 // Remove the text of the cancel button 2100 View view = findViewById(R.id.btn_cancel); 2101 if (view instanceof Button) ((Button) view).setText(""); 2102 } 2103 } 2104 2105 private void hidePostCaptureAlert() { 2106 if (mIsImageCaptureIntent) { 2107 if (mIndicatorWheel == null) { 2108 mShutterButton.setVisibility(View.VISIBLE); 2109 } else { 2110 mShutterButton.setEnabled(true); 2111 } 2112 int[] pickIds = {R.id.btn_retake, R.id.btn_done}; 2113 for (int id : pickIds) { 2114 View button = findViewById(id); 2115 ((View) button.getParent()).setVisibility(View.GONE); 2116 } 2117 enableCameraControls(true); 2118 2119 // Restore the text of the cancel button 2120 View view = findViewById(R.id.btn_cancel); 2121 if (view instanceof Button) { 2122 ((Button) view).setText(R.string.review_cancel); 2123 } 2124 } 2125 } 2126 2127 @Override 2128 public boolean onPrepareOptionsMenu(Menu menu) { 2129 super.onPrepareOptionsMenu(menu); 2130 // Only show the menu when camera is idle. 2131 for (int i = 0; i < menu.size(); i++) { 2132 menu.getItem(i).setVisible(isCameraIdle()); 2133 } 2134 2135 return true; 2136 } 2137 2138 @Override 2139 public boolean onCreateOptionsMenu(Menu menu) { 2140 super.onCreateOptionsMenu(menu); 2141 2142 if (mIsImageCaptureIntent) { 2143 // No options menu for attach mode. 2144 return false; 2145 } else { 2146 addBaseMenuItems(menu); 2147 } 2148 return true; 2149 } 2150 2151 private void addBaseMenuItems(Menu menu) { 2152 MenuHelper.addSwitchModeMenuItem(menu, true, new Runnable() { 2153 public void run() { 2154 switchToVideoMode(); 2155 } 2156 }); 2157 MenuItem gallery = menu.add(Menu.NONE, Menu.NONE, 2158 MenuHelper.POSITION_GOTO_GALLERY, 2159 R.string.camera_gallery_photos_text) 2160 .setOnMenuItemClickListener(new OnMenuItemClickListener() { 2161 public boolean onMenuItemClick(MenuItem item) { 2162 gotoGallery(); 2163 return true; 2164 } 2165 }); 2166 gallery.setIcon(android.R.drawable.ic_menu_gallery); 2167 mGalleryItems.add(gallery); 2168 2169 if (mNumberOfCameras > 1) { 2170 menu.add(Menu.NONE, Menu.NONE, 2171 MenuHelper.POSITION_SWITCH_CAMERA_ID, 2172 R.string.switch_camera_id) 2173 .setOnMenuItemClickListener(new OnMenuItemClickListener() { 2174 public boolean onMenuItemClick(MenuItem item) { 2175 CameraSettings.writePreferredCameraId(mPreferences, 2176 ((mCameraId == mFrontCameraId) 2177 ? mBackCameraId : mFrontCameraId)); 2178 onSharedPreferenceChanged(); 2179 return true; 2180 } 2181 }).setIcon(android.R.drawable.ic_menu_camera); 2182 } 2183 } 2184 2185 private void switchCameraId(int cameraId) { 2186 if (mPausing || !isCameraIdle()) return; 2187 mCameraId = cameraId; 2188 2189 stopPreview(); 2190 closeCamera(); 2191 2192 // Remove the messages in the event queue. 2193 mHandler.removeMessages(RESTART_PREVIEW); 2194 2195 // Reset variables 2196 mJpegPictureCallbackTime = 0; 2197 mZoomValue = 0; 2198 2199 // Reload the preferences. 2200 mPreferences.setLocalId(this, mCameraId); 2201 CameraSettings.upgradeLocalPreferences(mPreferences.getLocal()); 2202 2203 // Restart the preview. 2204 resetExposureCompensation(); 2205 if (!restartPreview()) return; 2206 2207 initializeZoom(); 2208 2209 // Reload the UI. 2210 initializeHeadUpDisplay(); 2211 initializeIndicatorWheel(); 2212 } 2213 2214 private boolean switchToVideoMode() { 2215 if (isFinishing() || !isCameraIdle()) return false; 2216 MenuHelper.gotoVideoMode(Camera.this); 2217 mHandler.removeMessages(FIRST_TIME_INIT); 2218 finish(); 2219 return true; 2220 } 2221 2222 public boolean onSwitchChanged(Switcher source, boolean onOff) { 2223 if (onOff == SWITCH_VIDEO) { 2224 return switchToVideoMode(); 2225 } else { 2226 return true; 2227 } 2228 } 2229 2230 private void onSharedPreferenceChanged() { 2231 // ignore the events after "onPause()" 2232 if (mPausing) return; 2233 2234 boolean recordLocation; 2235 2236 recordLocation = RecordLocationPreference.get( 2237 mPreferences, getContentResolver()); 2238 2239 if (mRecordLocation != recordLocation) { 2240 mRecordLocation = recordLocation; 2241 if (mRecordLocation) { 2242 startReceivingLocationUpdates(); 2243 } else { 2244 stopReceivingLocationUpdates(); 2245 } 2246 } 2247 int cameraId = CameraSettings.readPreferredCameraId(mPreferences); 2248 if (mCameraId != cameraId) { 2249 switchCameraId(cameraId); 2250 } else { 2251 setCameraParametersWhenIdle(UPDATE_PARAM_PREFERENCE); 2252 } 2253 } 2254 2255 @Override 2256 public void onUserInteraction() { 2257 super.onUserInteraction(); 2258 keepScreenOnAwhile(); 2259 } 2260 2261 private void resetScreenOn() { 2262 mHandler.removeMessages(CLEAR_SCREEN_DELAY); 2263 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 2264 } 2265 2266 private void keepScreenOnAwhile() { 2267 mHandler.removeMessages(CLEAR_SCREEN_DELAY); 2268 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 2269 mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY); 2270 } 2271 2272 private class MyHeadUpDisplayListener implements HeadUpDisplay.Listener { 2273 2274 public void onSharedPreferenceChanged() { 2275 Camera.this.onSharedPreferenceChanged(); 2276 } 2277 2278 public void onRestorePreferencesClicked() { 2279 Camera.this.onRestorePreferencesClicked(); 2280 } 2281 2282 public void onPopupWindowVisibilityChanged(int visibility) { 2283 } 2284 } 2285 2286 protected void onRestorePreferencesClicked() { 2287 if (mPausing) return; 2288 Runnable runnable = new Runnable() { 2289 public void run() { 2290 restorePreferences(); 2291 } 2292 }; 2293 MenuHelper.confirmAction(this, 2294 getString(R.string.confirm_restore_title), 2295 getString(R.string.confirm_restore_message), 2296 runnable); 2297 } 2298 2299 private void restorePreferences() { 2300 // Reset the zoom. Zoom value is not stored in preference. 2301 if (mParameters.isZoomSupported()) { 2302 mZoomValue = 0; 2303 setCameraParametersWhenIdle(UPDATE_PARAM_ZOOM); 2304 if (mZoomPicker != null) mZoomPicker.setZoomIndex(0); 2305 } 2306 2307 if (mHeadUpDisplay != null) { 2308 mHeadUpDisplay.restorePreferences(mParameters); 2309 } 2310 2311 if (mIndicatorWheel != null) { 2312 mIndicatorWheel.dismissSettingPopup(); 2313 CameraSettings.restorePreferences(Camera.this, mPreferences, 2314 mParameters); 2315 initializeIndicatorWheel(); 2316 onSharedPreferenceChanged(); 2317 } 2318 } 2319 2320 protected void onOverriddenPreferencesClicked() { 2321 if (mPausing) return; 2322 if (mNotSelectableToast == null) { 2323 String str = getResources().getString(R.string.not_selectable_in_scene_mode); 2324 mNotSelectableToast = Toast.makeText(Camera.this, str, Toast.LENGTH_SHORT); 2325 } 2326 mNotSelectableToast.cancel(); 2327 mNotSelectableToast.show(); 2328 } 2329 2330 private class MyIndicatorWheelListener implements IndicatorWheel.Listener { 2331 public void onSharedPreferenceChanged() { 2332 Camera.this.onSharedPreferenceChanged(); 2333 } 2334 2335 public void onRestorePreferencesClicked() { 2336 Camera.this.onRestorePreferencesClicked(); 2337 } 2338 2339 public void onOverriddenPreferencesClicked() { 2340 Camera.this.onOverriddenPreferencesClicked(); 2341 } 2342 } 2343 2344 private class MyCameraPickerListener implements CameraPicker.Listener { 2345 public void onSharedPreferenceChanged() { 2346 Camera.this.onSharedPreferenceChanged(); 2347 } 2348 } 2349} 2350