diff options
Diffstat (limited to 'src/com/android/camera/CaptureModule.java')
-rw-r--r-- | src/com/android/camera/CaptureModule.java | 328 |
1 files changed, 297 insertions, 31 deletions
diff --git a/src/com/android/camera/CaptureModule.java b/src/com/android/camera/CaptureModule.java index e870161f3..e5ceb4e11 100644 --- a/src/com/android/camera/CaptureModule.java +++ b/src/com/android/camera/CaptureModule.java @@ -19,19 +19,23 @@ package com.android.camera; +import android.app.Activity; import android.app.ActivityManager; import android.app.AlertDialog; +import android.content.ActivityNotFoundException; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.res.Configuration; +import android.graphics.Bitmap; import android.graphics.ImageFormat; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; +import android.hardware.Camera; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; @@ -59,12 +63,14 @@ import android.media.MediaRecorder; import android.media.EncoderCapabilities; import android.media.EncoderCapabilities.VideoEncoderCap; import android.net.Uri; +import android.os.Bundle; import android.os.Debug; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Bundle; import android.os.Message; +import android.os.ParcelFileDescriptor; import android.os.SystemClock; import android.provider.MediaStore; import android.util.Log; @@ -104,13 +110,17 @@ import org.codeaurora.snapcam.R; import org.codeaurora.snapcam.filter.ClearSightImageProcessor; import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.nio.ByteBuffer; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.LinkedList; import java.util.List; +import java.util.Set; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.lang.reflect.Method; @@ -126,6 +136,10 @@ public class CaptureModule implements CameraModule, PhotoController, public static final int BAYER_ID = 0; public static int MONO_ID = -1; public static int FRONT_ID = -1; + public static final int INTENT_MODE_NORMAL = 0; + public static final int INTENT_MODE_CAPTURE = 1; + public static final int INTENT_MODE_VIDEO = 2; + public static final int INTENT_MODE_CAPTURE_SECURE = 3; private static final int BACK_MODE = 0; private static final int FRONT_MODE = 1; private static final int CANCEL_TOUCH_FOCUS_DELAY = 3000; @@ -134,6 +148,8 @@ public class CaptureModule implements CameraModule, PhotoController, private static final int MAX_NUM_CAM = 3; private static final MeteringRectangle[] ZERO_WEIGHT_3A_REGION = new MeteringRectangle[]{ new MeteringRectangle(0, 0, 0, 0, 0)}; + private static final String EXTRA_QUICK_CAPTURE = + "android.intent.extra.quickCapture"; /** * Camera state: Showing camera preview. */ @@ -244,6 +260,15 @@ public class CaptureModule implements CameraModule, PhotoController, boolean mUnsupportedResolution = false; private static final int SDCARD_SIZE_LIMIT = 4000 * 1024 * 1024; + private static final String sTempCropFilename = "crop-temp"; + private static final int REQUEST_CROP = 1000; + private int mIntentMode = INTENT_MODE_NORMAL; + private String mCropValue; + private Uri mCurrentVideoUri; + private ParcelFileDescriptor mVideoFileDescriptor; + private Uri mSaveUri; + private boolean mQuickCapture; + private byte[] mJpegImageData; /** * A {@link CameraCaptureSession } for camera preview. @@ -380,6 +405,7 @@ public class CaptureModule implements CameraModule, PhotoController, public void onMediaSaved(Uri uri) { if (uri != null) { mActivity.notifyNewMedia(uri); + mCurrentVideoUri = uri; } } }; @@ -1088,6 +1114,7 @@ public class CaptureModule implements CameraModule, PhotoController, mFrameProcessor = new FrameProcessor(mActivity, this); mContentResolver = mActivity.getContentResolver(); + initModeByIntent(); mUI = new CaptureUI(activity, this, parent); mUI.initializeControlByIntent(); @@ -1095,6 +1122,49 @@ public class CaptureModule implements CameraModule, PhotoController, mLocationManager = new LocationManager(mActivity, this); } + private void initModeByIntent() { + String action = mActivity.getIntent().getAction(); + if (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)) { + mIntentMode = INTENT_MODE_CAPTURE; + } else if (CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action)) { + mIntentMode = INTENT_MODE_CAPTURE_SECURE; + } else if (MediaStore.ACTION_VIDEO_CAPTURE.equals(action)) { + mIntentMode = INTENT_MODE_VIDEO; + } + mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false); + Bundle myExtras = mActivity.getIntent().getExtras(); + if (myExtras != null) { + mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT); + mCropValue = myExtras.getString("crop"); + } + } + + public boolean isQuickCapture() { + return mQuickCapture; + } + + public void setJpegImageData(byte[] data) { + mJpegImageData = data; + } + + public void showCapturedReview(byte[] jpegData, int orientation, boolean mirror) { + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + mUI.showCapturedImageForReview(jpegData, orientation, mirror); + } + }); + } + + + public int getCurrentIntentMode() { + return mIntentMode; + } + + public void cancelCapture() { + mActivity.finish(); + } + /** * Initiate a still image capture. */ @@ -1245,6 +1315,7 @@ public class CaptureModule implements CameraModule, PhotoController, private void captureStillPicture(final int id) { Log.d(TAG, "captureStillPicture " + id); + mJpegImageData = null; mIsRefocus = false; CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() { @@ -1549,14 +1620,24 @@ public class CaptureModule implements CameraModule, PhotoController, ExifInterface exif = Exif.getExif(bytes); int orientation = Exif.getOrientation(exif); - mActivity.getMediaSaveService().addImage(bytes, title, date, - null, image.getWidth(), image.getHeight(), orientation, null, - mOnMediaSavedListener, mContentResolver, "jpeg"); - - if(mLongshotActive) { - mLastJpegData = bytes; + if (getCameraMode() != CaptureModule.INTENT_MODE_NORMAL) { + mJpegImageData = bytes; + if (!mQuickCapture) { + showCapturedReview(bytes, orientation, + mPostProcessor.isSelfieMirrorOn()); + } else { + onCaptureDone(); + } } else { - mActivity.updateThumbnail(bytes); + mActivity.getMediaSaveService().addImage(bytes, title, date, + null, image.getWidth(), image.getHeight(), orientation, null, + mOnMediaSavedListener, mContentResolver, "jpeg"); + + if(mLongshotActive) { + mLastJpegData = bytes; + } else { + mActivity.updateThumbnail(bytes); + } } image.close(); } @@ -1955,6 +2036,8 @@ public class CaptureModule implements CameraModule, PhotoController, stopBackgroundThread(); mLastJpegData = null; setProModeVisible(); + mJpegImageData = null; + closeVideoFileDescriptor(); } @Override @@ -2173,7 +2256,13 @@ public class CaptureModule implements CameraModule, PhotoController, String scene = mSettingsManager.getValue(SettingsManager.KEY_SCENE_MODE); if(isPanoSetting(scene)) { - mActivity.onModuleSelected(ModuleSwitcher.PANOCAPTURE_MODULE_INDEX); + if (mIntentMode != CaptureModule.INTENT_MODE_NORMAL) { + mSettingsManager.setValue( + SettingsManager.KEY_SCENE_MODE, ""+SettingsManager.SCENE_MODE_AUTO_INT); + showToast("Pano Capture is not supported in this mode"); + } else { + mActivity.onModuleSelected(ModuleSwitcher.PANOCAPTURE_MODULE_INDEX); + } } } @@ -2304,7 +2393,99 @@ public class CaptureModule implements CameraModule, PhotoController, @Override public void onCaptureDone() { + if (mPaused) { + return; + } + byte[] data = mJpegImageData; + + if (mCropValue == null) { + // First handle the no crop case -- just return the value. If the + // caller specifies a "save uri" then write the data to its + // stream. Otherwise, pass back a scaled down version of the bitmap + // directly in the extras. + if (mSaveUri != null) { + OutputStream outputStream = null; + try { + outputStream = mContentResolver.openOutputStream(mSaveUri); + outputStream.write(data); + outputStream.close(); + + mActivity.setResultEx(Activity.RESULT_OK); + mActivity.finish(); + } catch (IOException ex) { + // ignore exception + } finally { + CameraUtil.closeSilently(outputStream); + } + } else { + ExifInterface exif = Exif.getExif(data); + int orientation = Exif.getOrientation(exif); + Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024); + bitmap = CameraUtil.rotate(bitmap, orientation); + mActivity.setResultEx(Activity.RESULT_OK, + new Intent("inline-data").putExtra("data", bitmap)); + mActivity.finish(); + } + } else { + // Save the image to a temp file and invoke the cropper + Uri tempUri = null; + FileOutputStream tempStream = null; + try { + File path = mActivity.getFileStreamPath(sTempCropFilename); + path.delete(); + tempStream = mActivity.openFileOutput(sTempCropFilename, 0); + tempStream.write(data); + tempStream.close(); + tempUri = Uri.fromFile(path); + } catch (FileNotFoundException ex) { + mActivity.setResultEx(Activity.RESULT_CANCELED); + mActivity.finish(); + return; + } catch (IOException ex) { + mActivity.setResultEx(Activity.RESULT_CANCELED); + mActivity.finish(); + return; + } finally { + CameraUtil.closeSilently(tempStream); + } + + Bundle newExtras = new Bundle(); + if (mCropValue.equals("circle")) { + newExtras.putString("circleCrop", "true"); + } + if (mSaveUri != null) { + newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri); + } else { + newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true); + } + if (mActivity.isSecureCamera()) { + newExtras.putBoolean(CameraUtil.KEY_SHOW_WHEN_LOCKED, true); + } + + // TODO: Share this constant. + final String CROP_ACTION = "com.android.camera.action.CROP"; + Intent cropIntent = new Intent(CROP_ACTION); + + cropIntent.setData(tempUri); + cropIntent.putExtras(newExtras); + + mActivity.startActivityForResult(cropIntent, REQUEST_CROP); + } + } + + public void onRecordingDone(boolean valid) { + Intent resultIntent = new Intent(); + int resultCode; + if (valid) { + resultCode = Activity.RESULT_OK; + resultIntent.setData(mCurrentVideoUri); + resultIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + } else { + resultCode = Activity.RESULT_CANCELED; + } + mActivity.setResultEx(resultCode, resultIntent); + mActivity.finish(); } @Override @@ -3012,6 +3193,14 @@ public class CaptureModule implements CameraModule, PhotoController, mUI.enableShutter(true); mIsRecordingVideo = false; + if (mIntentMode == INTENT_MODE_VIDEO) { + if (isQuickCapture()) { + onRecordingDone(true); + } else { + Bitmap thumbnail = getVideoThumbnail(); + mUI.showRecordVideoForReview(thumbnail); + } + } if(mFrameProcessor != null) { mFrameProcessor.onOpen(getFrameProcFilterId(), mPreviewSize); @@ -3073,28 +3262,30 @@ public class CaptureModule implements CameraModule, PhotoController, } private void saveVideo() { - File origFile = new File(mVideoFilename); - if (!origFile.exists() || origFile.length() <= 0) { - Log.e(TAG, "Invalid file"); - mCurrentVideoValues = null; - return; - } + if (mVideoFileDescriptor == null) { + File origFile = new File(mVideoFilename); + if (!origFile.exists() || origFile.length() <= 0) { + Log.e(TAG, "Invalid file"); + mCurrentVideoValues = null; + return; + } - long duration = 0L; - MediaMetadataRetriever retriever = new MediaMetadataRetriever(); + long duration = 0L; + MediaMetadataRetriever retriever = new MediaMetadataRetriever(); - try { - retriever.setDataSource(mVideoFilename); - duration = Long.valueOf(retriever.extractMetadata( - MediaMetadataRetriever.METADATA_KEY_DURATION)); - } catch (IllegalArgumentException e) { - Log.e(TAG, "cannot access the file"); - } - retriever.release(); + try { + retriever.setDataSource(mVideoFilename); + duration = Long.valueOf(retriever.extractMetadata( + MediaMetadataRetriever.METADATA_KEY_DURATION)); + } catch (IllegalArgumentException e) { + Log.e(TAG, "cannot access the file"); + } + retriever.release(); - mActivity.getMediaSaveService().addVideo(mVideoFilename, - duration, mCurrentVideoValues, - mOnVideoSavedListener, mContentResolver); + mActivity.getMediaSaveService().addVideo(mVideoFilename, + duration, mCurrentVideoValues, + mOnVideoSavedListener, mContentResolver); + } mCurrentVideoValues = null; } @@ -3102,11 +3293,20 @@ public class CaptureModule implements CameraModule, PhotoController, Log.d(TAG, "setUpMediaRecorder"); String videoSize = mSettingsManager.getValue(SettingsManager.KEY_VIDEO_QUALITY); int size = CameraSettings.VIDEO_QUALITY_TABLE.get(videoSize); + Intent intent = mActivity.getIntent(); + if (intent.hasExtra(MediaStore.EXTRA_VIDEO_QUALITY)) { + int extraVideoQuality = + intent.getIntExtra(MediaStore.EXTRA_VIDEO_QUALITY, 0); + if (extraVideoQuality > 0) { + size = CamcorderProfile.QUALITY_HIGH; + } else { // 0 is mms. + size = CamcorderProfile.QUALITY_LOW; + } + } if (mCaptureTimeLapse) { size = CameraSettings.getTimeLapseQualityFor(size); } - Intent intent = mActivity.getIntent(); Bundle myExtras = intent.getExtras(); if (mMediaRecorder == null) mMediaRecorder = new MediaRecorder(); @@ -3135,9 +3335,26 @@ public class CaptureModule implements CameraModule, PhotoController, mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); mMediaRecorder.setOutputFormat(mProfile.fileFormat); - String fileName = generateVideoFilename(mProfile.fileFormat); - Log.v(TAG, "New video filename: " + fileName); - mMediaRecorder.setOutputFile(fileName); + closeVideoFileDescriptor(); + if (mIntentMode == CaptureModule.INTENT_MODE_VIDEO && myExtras != null) { + Uri saveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT); + if (saveUri != null) { + try { + mCurrentVideoUri = saveUri; + mVideoFileDescriptor = + mContentResolver.openFileDescriptor(saveUri, "rw"); + mCurrentVideoUri = saveUri; + } catch (java.io.FileNotFoundException ex) { + // invalid uri + Log.e(TAG, ex.toString()); + } + } + mMediaRecorder.setOutputFile(mVideoFileDescriptor.getFileDescriptor()); + } else { + String fileName = generateVideoFilename(mProfile.fileFormat); + Log.v(TAG, "New video filename: " + fileName); + mMediaRecorder.setOutputFile(fileName); + } mMediaRecorder.setVideoFrameRate(mProfile.videoFrameRate); mMediaRecorder.setVideoEncodingBitRate(mProfile.videoBitRate); if(mFrameProcessor.isFrameFilterEnabled()) { @@ -4168,6 +4385,55 @@ public class CaptureModule implements CameraModule, PhotoController, .KEY_CAMERA_SAVEPATH).equals("1")); } + public void startPlayVideoActivity() { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setDataAndType(mCurrentVideoUri, + CameraUtil.convertOutputFormatToMimeType(mProfile.fileFormat)); + try { + mActivity + .startActivityForResult(intent, CameraActivity.REQ_CODE_DONT_SWITCH_TO_PREVIEW); + } catch (ActivityNotFoundException ex) { + Log.e(TAG, "Couldn't view video " + mCurrentVideoUri, ex); + } + } + + private void closeVideoFileDescriptor() { + if (mVideoFileDescriptor != null) { + try { + mVideoFileDescriptor.close(); + } catch (IOException e) { + Log.e(TAG, "Fail to close fd", e); + } + mVideoFileDescriptor = null; + } + } + + private Bitmap getVideoThumbnail() { + Bitmap bitmap = null; + if (mVideoFileDescriptor != null) { + bitmap = Thumbnail.createVideoThumbnailBitmap(mVideoFileDescriptor.getFileDescriptor(), + mVideoPreviewSize.getWidth()); + } else if (mCurrentVideoUri != null) { + try { + mVideoFileDescriptor = mContentResolver.openFileDescriptor(mCurrentVideoUri, "r"); + bitmap = Thumbnail.createVideoThumbnailBitmap( + mVideoFileDescriptor.getFileDescriptor(), mVideoPreviewSize.getWidth()); + } catch (java.io.FileNotFoundException ex) { + // invalid uri + Log.e(TAG, ex.toString()); + } + } + + if (bitmap != null) { + // MetadataRetriever already rotates the thumbnail. We should rotate + // it to match the UI orientation (and mirror if it is front-facing camera). + Camera.CameraInfo[] info = CameraHolder.instance().getCameraInfo(); + boolean mirror = mPostProcessor.isSelfieMirrorOn(); + bitmap = CameraUtil.rotateAndMirror(bitmap, 0, mirror); + } + return bitmap; + } + private void deleteVideoFile(String fileName) { Log.v(TAG, "Deleting video " + fileName); File f = new File(fileName); |