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