diff options
Diffstat (limited to 'src/com/android/camera/Camera.java')
-rw-r--r-- | src/com/android/camera/Camera.java | 361 |
1 files changed, 232 insertions, 129 deletions
diff --git a/src/com/android/camera/Camera.java b/src/com/android/camera/Camera.java index c9c7394..ebffe30 100644 --- a/src/com/android/camera/Camera.java +++ b/src/com/android/camera/Camera.java @@ -16,6 +16,15 @@ package com.android.camera; +import com.android.camera.gallery.IImage; +import com.android.camera.gallery.IImageList; +import com.android.camera.ui.BasicSettingPicker; +import com.android.camera.ui.CameraHeadUpDisplay; +import com.android.camera.ui.GLRootView; +import com.android.camera.ui.HeadUpDisplay; +import com.android.camera.ui.ControlPanel; +import com.android.camera.ui.ZoomControllerListener; + import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; @@ -24,10 +33,10 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.content.res.Configuration; import android.content.res.Resources; +import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.hardware.Camera.CameraInfo; @@ -43,18 +52,17 @@ import android.media.ToneGenerator; import android.net.Uri; import android.os.Build; import android.os.Bundle; -import android.os.Debug; import android.os.Environment; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.MessageQueue; -import android.os.SystemClock; import android.provider.MediaStore; import android.provider.Settings; +import android.provider.MediaStore.Images.ImageColumns; +import android.provider.MediaStore.Images.Media; import android.util.AttributeSet; import android.util.Log; -import android.view.Display; import android.view.GestureDetector; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -66,18 +74,16 @@ import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; +import android.view.ViewStub; import android.view.Window; import android.view.WindowManager; import android.view.MenuItem.OnMenuItemClickListener; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.Button; +import android.widget.CursorAdapter; import android.widget.FrameLayout; -import android.widget.ImageView; - -import com.android.camera.gallery.IImage; -import com.android.camera.gallery.IImageList; -import com.android.camera.ui.CameraHeadUpDisplay; -import com.android.camera.ui.GLRootView; -import com.android.camera.ui.HeadUpDisplay; -import com.android.camera.ui.ZoomControllerListener; +import android.widget.ListView; import java.io.File; import java.io.FileNotFoundException; @@ -130,6 +136,7 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, private int mZoomValue; // The current zoom value. private int mZoomMax; private int mTargetZoomValue; + private ZoomPicker mZoomPicker; private Parameters mParameters; private Parameters mInitialParams; @@ -163,10 +170,15 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, private GLRootView mGLRootView; - // mPostCaptureAlert, mLastPictureButton, mThumbController - // are non-null only if isImageCaptureIntent() is true. - private ImageView mLastPictureButton; - private ThumbnailController mThumbController; + // The layouts of small devices have a thumbnail button, which shows the last + // captured picture. + private RotateImageView mThumbnailButton; + // The layouts of xlarge devices have a list of thumbnails, which show the + // last captured pictures. + private ListView mThumbnailList; + private OnItemClickListener mThumbnailItemClickListener = + new ThumbnailItemClickListener(); + private ThumbnailAdapter mThumbnailAdapter; // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true. private String mCropValue; @@ -174,6 +186,15 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, private ImageCapture mImageCapture = null; + /** + * An unpublished intent flag requesting to return as soon as capturing + * is completed. + * + * TODO: consider publishing by moving into MediaStore. + */ + private final static String EXTRA_QUICK_CAPTURE = + "android.intent.extra.quickCapture"; + private boolean mPreviewing; private boolean mPausing; private boolean mFirstTimeInitialized; @@ -231,11 +252,15 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, private final Handler mHandler = new MainHandler(); private CameraHeadUpDisplay mHeadUpDisplay; + private ControlPanel mControlPanel; + private PreferenceGroup mPreferenceGroup; // multiple cameras support private int mNumberOfCameras; private int mCameraId; + private boolean mQuickCapture; + /** * This Handler is used to post message back onto the main thread of the * application @@ -323,14 +348,8 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, mContentResolver = getContentResolver(); if (!mIsImageCaptureIntent) { findViewById(R.id.camera_switch).setOnClickListener(this); - mLastPictureButton = - (ImageView) findViewById(R.id.review_thumbnail); - mLastPictureButton.setOnClickListener(this); - mThumbController = new ThumbnailController( - getResources(), mLastPictureButton, mContentResolver); - mThumbController.loadData(ImageManager.getLastImageThumbPath()); - // Update last image thumbnail. - updateThumbnailButton(); + initThumbnailButton(); + initThumbnailList(); } // Initialize shutter button. @@ -363,12 +382,95 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, }); } + private void initThumbnailButton() { + mThumbnailButton = + (RotateImageView) findViewById(R.id.review_thumbnail); + if (mThumbnailButton != null) { + mThumbnailButton.setOnClickListener(this); + mThumbnailButton.loadData(ImageManager.getLastImageThumbPath()); + updateThumbnailButton(); + } + } + private void updateThumbnailButton() { + if (mThumbnailButton == null) return; // Update last image if URI is invalid and the storage is ready. - if (!mThumbController.isUriValid() && mPicturesRemaining >= 0) { - updateLastImage(); + if (!mThumbnailButton.isUriValid() && mPicturesRemaining >= 0) { + IImageList list = ImageManager.makeImageList( + mContentResolver, + dataLocation(), + ImageManager.INCLUDE_IMAGES, + ImageManager.SORT_ASCENDING, + ImageManager.CAMERA_IMAGE_BUCKET_ID); + int count = list.getCount(); + if (count > 0) { + IImage image = list.getImageAt(count - 1); + Uri uri = image.fullSizeImageUri(); + mThumbnailButton.setData(uri, image.miniThumbBitmap()); + } else { + mThumbnailButton.setData(null, null); + } + list.close(); } - mThumbController.updateDisplayIfNeeded(); + } + + private void setLastPictureThumb(byte[] data, int degree, Uri uri) { + if (mThumbnailButton == null) return; + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inSampleSize = 16; + Bitmap lastPictureThumb = + BitmapFactory.decodeByteArray(data, 0, data.length, options); + lastPictureThumb = Util.rotate(lastPictureThumb, degree); + mThumbnailButton.setData(uri, lastPictureThumb); + } + + private void initThumbnailList() { + mThumbnailList = (ListView) findViewById(R.id.thumbnail_list); + if (mThumbnailList == null) return; + + int width = mThumbnailList.getWidth(); + int height = mThumbnailList.getHeight(); + + // Add gallery button to header view. + if (mThumbnailList.getHeaderViewsCount() == 0) { + LayoutInflater inflater = getLayoutInflater(); + Button b = new Button(this); + ListView.LayoutParams params = new ListView.LayoutParams(width, width); + b.setId(R.id.btn_gallery); + b.setLayoutParams(params); + b.setOnClickListener(this); + b.setBackgroundResource(R.drawable.ic_menu_gallery); + mThumbnailList.addHeaderView(b); + } + + // Set data adapter. + int thumbnailCount = (height + mThumbnailList.getDividerHeight()) + / (width + mThumbnailList.getDividerHeight()) - 1; + Cursor cursor = getThumbnailsCursor(thumbnailCount); + mThumbnailAdapter = new ThumbnailAdapter( + getApplicationContext(), R.layout.thumbnail_item, cursor, true); + mThumbnailList.setAdapter(mThumbnailAdapter); + mThumbnailList.setOnItemClickListener(mThumbnailItemClickListener); + } + + private void updateThumbnailList() { + if (mThumbnailList == null) return; + mThumbnailAdapter.getCursor().requery(); + mThumbnailAdapter.notifyDataSetChanged(); + } + + private Cursor getThumbnailsCursor(int thumbnailCount) { + Log.v(TAG, "thumbnailCount=" + thumbnailCount); + String[] projections = { MediaStore.Images.Thumbnails._ID }; + Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI + .buildUpon() + .appendQueryParameter("limit", String.valueOf(thumbnailCount)) + .build(); + // TODO: managedQuery is deprecated. + return managedQuery(uri, projections, + Media.MIME_TYPE + " = 'image/jpeg' AND " + Media.BUCKET_ID + " = ?", + new String[] {ImageManager.CAMERA_IMAGE_BUCKET_ID}, + ImageColumns._ID + " DESC"); } // If the activity is paused and resumed, this method will be called in @@ -402,6 +504,15 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, mZoomMax = mParameters.getMaxZoom(); mSmoothZoomSupported = mParameters.isSmoothZoomSupported(); mGestureDetector = new GestureDetector(this, new ZoomGestureListener()); + if (mZoomPicker != null) { + mZoomPicker.setZoomRatios(getZoomRatios()); + mZoomPicker.setOnZoomChangeListener( + new ZoomPicker.OnZoomChangedListener() { + public void onZoomChanged(int index) { + onZoomValueChanged(index); + } + }); + } mCameraDevice.setZoomChangeListener(mZoomListener); } @@ -447,6 +558,13 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, return false; } + int x = Math.round(e.getX()); + int y = Math.round(e.getY()); + if (x < mSurfaceView.getLeft() || x > mSurfaceView.getRight() + || y < mSurfaceView.getTop() || y > mSurfaceView.getBottom()) { + return false; + } + if (mZoomValue < mZoomMax) { // Zoom in to the maximum. mZoomValue = mZoomMax; @@ -456,7 +574,11 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, setCameraParametersWhenIdle(UPDATE_PARAM_ZOOM); - mHeadUpDisplay.setZoomIndex(mZoomValue); + if (mZoomPicker != null) { + mZoomPicker.setZoomIndex(mZoomValue); + } else { + mHeadUpDisplay.setZoomIndex(mZoomValue); + } return true; } } @@ -486,6 +608,7 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, checkStorage(); if (!mIsImageCaptureIntent) { updateThumbnailButton(); + updateThumbnailList(); } } } @@ -747,10 +870,14 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, "com.android.camera.NEW_PICTURE", mLastContentUri)); setLastPictureThumb(data, degree, mImageCapture.getLastCaptureUri()); - mThumbController.updateDisplayIfNeeded(); + updateThumbnailList(); } else { mCaptureOnlyData = data; - showPostCaptureAlert(); + if (!mQuickCapture) { + showPostCaptureAlert(); + } else { + doAttach(); + } } } @@ -863,15 +990,6 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, return true; } - private void setLastPictureThumb(byte[] data, int degree, Uri uri) { - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inSampleSize = 16; - Bitmap lastPictureThumb = - BitmapFactory.decodeByteArray(data, 0, data.length, options); - lastPictureThumb = Util.rotate(lastPictureThumb, degree); - mThumbController.setData(uri, lastPictureThumb); - } - private String createName(long dateTaken) { Date date = new Date(dateTaken); SimpleDateFormat dateFormat = new SimpleDateFormat( @@ -894,6 +1012,7 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, CameraSettings.upgradeLocalPreferences(mPreferences.getLocal()); mNumberOfCameras = CameraHolder.instance().getNumberOfCameras(); + mQuickCapture = getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false); // we need to reset exposure for the preview resetExposureCompensation(); @@ -946,6 +1065,10 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, mSwitcher.addTouchView(findViewById(R.id.camera_switch_set)); } + // Show zoom picker. + ViewStub zoomStub = (ViewStub) findViewById(R.id.zoom_stub); + if (zoomStub != null) mZoomPicker = (ZoomPicker) zoomStub.inflate(); + // Make sure preview is started. try { startPreviewThread.join(); @@ -956,6 +1079,8 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, } catch (InterruptedException ex) { // ignore } + + initializeControlPanel(); } private void changeHeadUpDisplayState() { @@ -992,13 +1117,31 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, } } + private void initializeControlPanel() { + String[] keys = new String[]{CameraSettings.KEY_FLASH_MODE, + CameraSettings.KEY_WHITE_BALANCE, + CameraSettings.KEY_RECORD_LOCATION, + CameraSettings.KEY_CAMERA_ID}; + mControlPanel = (ControlPanel) findViewById(R.id.control_panel); + if (mControlPanel != null) { + CameraSettings settings = new CameraSettings(this, mInitialParams, + mCameraId, CameraHolder.instance().getCameraInfo()); + mPreferenceGroup = settings.getPreferenceGroup(R.xml.camera_preferences); + mControlPanel.initialize(this, mPreferenceGroup, keys); + mControlPanel.setListener(new MyControlPanelListener()); + } + } + private void initializeHeadUpDisplay() { CameraSettings settings = new CameraSettings(this, mInitialParams, - CameraHolder.instance().getCameraInfo()); + mCameraId, CameraHolder.instance().getCameraInfo()); + // If we have zoom picker, do not show zoom control on head-up display. + float[] zoomRatios = null; + if (mZoomPicker == null) zoomRatios = getZoomRatios(); mHeadUpDisplay.initialize(this, settings.getPreferenceGroup(R.xml.camera_preferences), - getZoomRatios(), mOrientationCompensation); - if (mParameters.isZoomSupported()) { + zoomRatios, mOrientationCompensation); + if (mZoomPicker == null && mParameters.isZoomSupported()) { mHeadUpDisplay.setZoomListener(new ZoomControllerListener() { public void onZoomChanged( int index, float ratio, boolean isMoving) { @@ -1011,7 +1154,7 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, private void attachHeadUpDisplay() { mHeadUpDisplay.setOrientation(mOrientationCompensation); - if (mParameters.isZoomSupported()) { + if (mZoomPicker == null && mParameters.isZoomSupported()) { mHeadUpDisplay.setZoomIndex(mZoomValue); } FrameLayout frame = (FrameLayout) findViewById(R.id.frame); @@ -1058,8 +1201,10 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, } private void setOrientationIndicator(int degree) { - ((RotateImageView) findViewById( - R.id.review_thumbnail)).setDegree(degree); + RotateImageView thumbnail = (RotateImageView) findViewById( + R.id.review_thumbnail); + if (thumbnail != null) thumbnail.setDegree(degree); + ((RotateImageView) findViewById( R.id.camera_switch_icon)).setDegree(degree); ((RotateImageView) findViewById( @@ -1096,7 +1241,7 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, break; case R.id.review_thumbnail: if (isCameraIdle()) { - viewLastImage(); + viewImage(mThumbnailButton); } break; case R.id.btn_done: @@ -1104,9 +1249,19 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, break; case R.id.btn_cancel: doCancel(); + break; + case R.id.btn_gallery: + gotoGallery(); + break; } } + private class ThumbnailItemClickListener implements OnItemClickListener { + public void onItemClick(AdapterView<?> p, View v, int pos, long id) { + viewImage((RotateImageView)v); + } + } + private Bitmap createCaptureBitmap(byte[] data) { // This is really stupid...we just want to read the orientation in // the jpeg header. @@ -1337,12 +1492,15 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, closeCamera(); resetScreenOn(); changeHeadUpDisplayState(); + if (mControlPanel != null) mControlPanel.hideSettingPicker(); if (mFirstTimeInitialized) { mOrientationListener.disable(); if (!mIsImageCaptureIntent) { - mThumbController.storeData( - ImageManager.getLastImageThumbPath()); + if (mThumbnailButton != null) { + mThumbnailButton.storeData( + ImageManager.getLastImageThumbPath()); + } } hidePostCaptureAlert(); } @@ -1478,6 +1636,7 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, // the shutter button gets the focus, doFocus() will be // called again but it is fine. if (mHeadUpDisplay.collapse()) return true; + if (mControlPanel != null) mControlPanel.hideSettingPicker(); doFocus(true); if (mShutterButton.isInTouchMode()) { mShutterButton.requestFocusFromTouch(); @@ -1506,6 +1665,7 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, private void doSnap() { if (mHeadUpDisplay.collapse()) return; + if (mControlPanel != null) mControlPanel.hideSettingPicker(); Log.v(TAG, "doSnap: mFocusState=" + mFocusState); // If the user has half-pressed the shutter and focus is completed, we @@ -1530,6 +1690,7 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, private void doFocus(boolean pressed) { // Do the focus if the mode is not infinity. if (mHeadUpDisplay.collapse()) return; + if (mControlPanel != null) mControlPanel.hideSettingPicker(); if (!(mFocusMode.equals(Parameters.FOCUS_MODE_INFINITY) || mFocusMode.equals(Parameters.FOCUS_MODE_FIXED) || mFocusMode.equals(Parameters.FOCUS_MODE_EDOF))) { @@ -1609,24 +1770,6 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, } } - private void updateLastImage() { - IImageList list = ImageManager.makeImageList( - mContentResolver, - dataLocation(), - ImageManager.INCLUDE_IMAGES, - ImageManager.SORT_ASCENDING, - ImageManager.CAMERA_IMAGE_BUCKET_ID); - int count = list.getCount(); - if (count > 0) { - IImage image = list.getImageAt(count - 1); - Uri uri = image.fullSizeImageUri(); - mThumbController.setData(uri, image.miniThumbBitmap()); - } else { - mThumbController.setData(null, null); - } - list.close(); - } - private void showCameraErrorAndFinish() { Resources ress = getResources(); Util.showFatalErrorAndFinish(Camera.this, @@ -1690,53 +1833,6 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, clearFocusState(); } - private Size getOptimalPreviewSize(List<Size> sizes, double targetRatio) { - final double ASPECT_TOLERANCE = 0.05; - if (sizes == null) return null; - - Size optimalSize = null; - double minDiff = Double.MAX_VALUE; - - // Because of bugs of overlay and layout, we sometimes will try to - // layout the viewfinder in the portrait orientation and thus get the - // wrong size of mSurfaceView. When we change the preview size, the - // new overlay will be created before the old one closed, which causes - // an exception. For now, just get the screen size - - Display display = getWindowManager().getDefaultDisplay(); - int targetHeight = Math.min(display.getHeight(), display.getWidth()); - - if (targetHeight <= 0) { - // We don't know the size of SurefaceView, use screen height - WindowManager windowManager = (WindowManager) - getSystemService(Context.WINDOW_SERVICE); - targetHeight = windowManager.getDefaultDisplay().getHeight(); - } - - // Try to find an size match aspect ratio and size - for (Size size : sizes) { - double ratio = (double) size.width / size.height; - if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue; - if (Math.abs(size.height - targetHeight) < minDiff) { - optimalSize = size; - minDiff = Math.abs(size.height - targetHeight); - } - } - - // Cannot find the one match the aspect ratio, ignore the requirement - if (optimalSize == null) { - Log.v(TAG, "No preview size match the aspect ratio"); - minDiff = Double.MAX_VALUE; - for (Size size : sizes) { - if (Math.abs(size.height - targetHeight) < minDiff) { - optimalSize = size; - minDiff = Math.abs(size.height - targetHeight); - } - } - } - return optimalSize; - } - private static boolean isSupported(String value, List<String> supported) { return supported == null ? false : supported.indexOf(value) >= 0; } @@ -1780,7 +1876,7 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, // Set a preview size that is closest to the viewfinder height and has // the right aspect ratio. List<Size> sizes = mParameters.getSupportedPreviewSizes(); - Size optimalSize = getOptimalPreviewSize( + Size optimalSize = Util.getOptimalPreviewSize(this, sizes, (double) size.width / size.height); if (optimalSize != null) { Size original = mParameters.getPreviewSize(); @@ -1944,21 +2040,22 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, MenuHelper.gotoCameraImageGallery(this); } - private void viewLastImage() { - if (mThumbController.isUriValid()) { - Intent intent = new Intent(Util.REVIEW_ACTION, mThumbController.getUri()); + private void viewImage(RotateImageView view) { + if(!view.isUriValid()) { + Log.e(TAG, "Uri invalid. uri=" + view.getUri()); + return; + } + + try { + startActivity(new Intent( + Util.REVIEW_ACTION, view.getUri())); + } catch (ActivityNotFoundException ex) { try { - startActivity(intent); - } catch (ActivityNotFoundException ex) { - try { - intent = new Intent(Intent.ACTION_VIEW, mThumbController.getUri()); - startActivity(intent); - } catch (ActivityNotFoundException e) { - Log.e(TAG, "review image fail", e); - } + startActivity(new Intent( + Intent.ACTION_VIEW, view.getUri())); + } catch (ActivityNotFoundException e) { + Log.e(TAG, "review image fail. uri=" + view.getUri(), e); } - } else { - Log.e(TAG, "Can't view last image."); } } @@ -2156,7 +2253,7 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, } } - private void onSharedPreferenceChanged() { + private void onSharedPreferencesChanged() { // ignore the events after "onPause()" if (mPausing) return; @@ -2201,7 +2298,7 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, private class MyHeadUpDisplayListener implements HeadUpDisplay.Listener { public void onSharedPreferencesChanged() { - Camera.this.onSharedPreferenceChanged(); + Camera.this.onSharedPreferencesChanged(); } public void onRestorePreferencesClicked() { @@ -2224,6 +2321,12 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, getString(R.string.confirm_restore_message), runnable); } + + private class MyControlPanelListener implements ControlPanel.Listener { + public void onSharedPreferencesChanged() { + Camera.this.onSharedPreferencesChanged(); + } + } } class FocusRectangle extends View { |