diff options
-rw-r--r-- | src/com/android/camera/CameraActivity.java | 4 | ||||
-rw-r--r-- | src/com/android/camera/MediaSaveService.java | 9 | ||||
-rw-r--r-- | src/com/android/camera/PhotoModule.java | 5 | ||||
-rw-r--r-- | src/com/android/camera/PhotoUI.java | 20 | ||||
-rw-r--r-- | src/com/android/camera/VideoModule.java | 504 | ||||
-rw-r--r-- | src/com/android/camera/VideoUI.java | 18 | ||||
-rw-r--r-- | src/com/android/camera/ui/FilmStripView.java | 105 |
7 files changed, 157 insertions, 508 deletions
diff --git a/src/com/android/camera/CameraActivity.java b/src/com/android/camera/CameraActivity.java index 1baa865fd..411b29ea7 100644 --- a/src/com/android/camera/CameraActivity.java +++ b/src/com/android/camera/CameraActivity.java @@ -127,6 +127,10 @@ public class CameraActivity extends Activity private ActionBar mActionBar; private Menu mActionBarMenu; + public void gotoGallery() { + mFilmStripView.getController().goToNextItem(); + } + private class MyOrientationEventListener extends OrientationEventListener { public MyOrientationEventListener(Context context) { diff --git a/src/com/android/camera/MediaSaveService.java b/src/com/android/camera/MediaSaveService.java index 713821d50..cec8b4329 100644 --- a/src/com/android/camera/MediaSaveService.java +++ b/src/com/android/camera/MediaSaveService.java @@ -37,6 +37,8 @@ import java.io.File; * Service for saving images in the background thread. */ public class MediaSaveService extends Service { + public static final String VIDEO_BASE_URI = "content://media/external/video/media"; + // The memory limit for unsaved image is 20MB. private static final int SAVE_TASK_MEMORY_LIMIT = 20 * 1024 * 1024; private static final String TAG = "CAM_" + MediaSaveService.class.getSimpleName(); @@ -207,17 +209,12 @@ public class MediaSaveService extends Service { } @Override - protected void onPreExecute() { - // do nothing. - } - - @Override protected Uri doInBackground(Void... v) { values.put(Video.Media.SIZE, new File(path).length()); values.put(Video.Media.DURATION, duration); Uri uri = null; try { - Uri videoTable = Uri.parse("content://media/external/video/media"); + Uri videoTable = Uri.parse(VIDEO_BASE_URI); uri = resolver.insert(videoTable, values); // Rename the video file to the final name. This avoids other diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java index 6098d01f3..bb486cb62 100644 --- a/src/com/android/camera/PhotoModule.java +++ b/src/com/android/camera/PhotoModule.java @@ -681,9 +681,6 @@ public class PhotoModule } if (mIsImageCaptureIntent) { stopPreview(); - } else { - // Animate capture with real jpeg data instead of a preview frame. - mUI.animateCapture(jpegData); } if (mSceneMode == CameraUtil.SCENE_MODE_HDR) { mUI.showSwitcher(); @@ -754,6 +751,8 @@ public class PhotoModule jpegData, title, date, mLocation, width, height, orientation, exif, mOnMediaSavedListener, mContentResolver); } + // Animate capture with real jpeg data instead of a preview frame. + mUI.animateCapture(jpegData, orientation); } else { mJpegImageData = jpegData; if (!mQuickCapture) { diff --git a/src/com/android/camera/PhotoUI.java b/src/com/android/camera/PhotoUI.java index 43862d376..7adb27f90 100644 --- a/src/com/android/camera/PhotoUI.java +++ b/src/com/android/camera/PhotoUI.java @@ -147,15 +147,24 @@ public class PhotoUI implements PieListener, private class DecodeTask extends AsyncTask<Integer, Void, Bitmap> { private final byte [] mData; + private int mOrientation; - public DecodeTask(byte[] data) { + public DecodeTask(byte[] data, int orientation) { mData = data; + mOrientation = orientation; } @Override protected Bitmap doInBackground(Integer... params) { // Decode image in background. - return CameraUtil.downSample(mData, DOWN_SAMPLE_FACTOR); + Bitmap bitmap = CameraUtil.downSample(mData, DOWN_SAMPLE_FACTOR); + if (mOrientation != 0) { + Matrix m = new Matrix(); + m.postRotate(mOrientation); + return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m, + false); + } + return bitmap; } @Override @@ -299,9 +308,9 @@ public class PhotoUI implements PieListener, updateOnScreenIndicators(params, prefGroup, prefs); } - public void animateCapture(final byte[] jpegData) { + public void animateCapture(final byte[] jpegData, int orientation) { // Decode jpeg byte array and then animate the jpeg - DecodeTask task = new DecodeTask(jpegData); + DecodeTask task = new DecodeTask(jpegData, orientation); task.execute(); } @@ -321,8 +330,7 @@ public class PhotoUI implements PieListener, mPreviewThumb.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - // TODO: go to filmstrip - // mActivity.gotoGallery(); + mActivity.gotoGallery(); } }); mMenuButton = mRootView.findViewById(R.id.menu); diff --git a/src/com/android/camera/VideoModule.java b/src/com/android/camera/VideoModule.java index e1b921d4c..bd2658e1b 100644 --- a/src/com/android/camera/VideoModule.java +++ b/src/com/android/camera/VideoModule.java @@ -77,14 +77,10 @@ public class VideoModule implements CameraModule, CameraPreference.OnPreferenceChangedListener, ShutterButton.OnShutterButtonListener, MediaRecorder.OnErrorListener, - MediaRecorder.OnInfoListener, - EffectsRecorder.EffectsListener { + MediaRecorder.OnInfoListener { private static final String TAG = "CAM_VideoModule"; - // We number the request code from 1000 to avoid collision with Gallery. - private static final int REQUEST_EFFECT_BACKDROPPER = 1000; - private static final int CHECK_DISPLAY_ROTATION = 3; private static final int CLEAR_SCREEN_DELAY = 4; private static final int UPDATE_RECORD_TIME = 5; @@ -114,8 +110,6 @@ public class VideoModule implements CameraModule, private boolean mIsInReviewMode; private boolean mSnapshotInProgress = false; - private static final String EFFECT_BG_FROM_GALLERY = "gallery"; - private final CameraErrorCallback mErrorCallback = new CameraErrorCallback(); private ComboPreferences mPreferences; @@ -128,14 +122,6 @@ public class VideoModule implements CameraModule, private boolean mQuickCapture; private MediaRecorder mMediaRecorder; - private EffectsRecorder mEffectsRecorder; - private boolean mEffectsDisplayResult; - - private int mEffectType = EffectsRecorder.EFFECT_NONE; - private Object mEffectParameter = null; - private String mEffectUriFromGallery = null; - private String mPrefVideoEffectDefault; - private boolean mResetEffect = true; private boolean mSwitchingCamera; private boolean mMediaRecorderRecording = false; @@ -349,8 +335,6 @@ public class VideoModule implements CameraModule, mPreferences.setLocalId(mActivity, mCameraId); CameraSettings.upgradeLocalPreferences(mPreferences.getLocal()); - mPrefVideoEffectDefault = mActivity.getString(R.string.pref_video_effect_default); - resetEffect(); mOrientationManager = new OrientationManager(mActivity); /* @@ -398,33 +382,19 @@ public class VideoModule implements CameraModule, mPendingSwitchCameraId = -1; mUI.updateOnScreenIndicators(mParameters, mPreferences); - // Disable the shutter button if effects are ON since it might take - // a little more time for the effects preview to be ready. We do not - // want to allow recording before that happens. The shutter button - // will be enabled when we get the message from effectsrecorder that - // the preview is running. This becomes critical when the camera is - // swapped. - if (effectsActive()) { - mUI.enableShutter(false); - } } // SingleTapListener // Preview area is touched. Take a picture. @Override public void onSingleTapUp(View view, int x, int y) { - if (mMediaRecorderRecording && effectsActive()) { - new RotateTextToast(mActivity, R.string.disable_video_snapshot_hint, - mOrientation).show(); - return; - } takeASnapshot(); } private void takeASnapshot() { // Only take snapshots if video snapshot is supported by device if (CameraUtil.isVideoSnapshotSupported(mParameters) && !mIsVideoCaptureIntent) { - if (!mMediaRecorderRecording || mPaused || mSnapshotInProgress || effectsActive()) { + if (!mMediaRecorderRecording || mPaused || mSnapshotInProgress) { return; } MediaSaveService s = mActivity.getMediaSaveService(); @@ -463,11 +433,6 @@ public class VideoModule implements CameraModule, private void initializeVideoControl() { loadCameraPreferences(); mUI.initializePopup(mPreferenceGroup); - if (effectsActive()) { - mUI.overrideSettings( - CameraSettings.KEY_VIDEO_QUALITY, - Integer.toString(CamcorderProfile.QUALITY_480P)); - } } @Override @@ -480,14 +445,6 @@ public class VideoModule implements CameraModule, if (mOrientation != newOrientation) { mOrientation = newOrientation; - // The input of effects recorder is affected by - // android.hardware.Camera.setDisplayOrientation. Its value only - // compensates the camera orientation (no Display.getRotation). - // So the orientation hint here should only consider sensor - // orientation. - if (effectsActive()) { - mEffectsRecorder.setOrientationHint(mOrientation); - } } // Show the toast after getting the first orientation changed. @@ -531,15 +488,12 @@ public class VideoModule implements CameraModule, } private void onStopVideoRecording() { - mEffectsDisplayResult = true; boolean recordFail = stopVideoRecording(); if (mIsVideoCaptureIntent) { - if (!effectsActive()) { - if (mQuickCapture) { - doReturnToCaller(!recordFail); - } else if (!recordFail) { - showCaptureResult(); - } + if (mQuickCapture) { + doReturnToCaller(!recordFail); + } else if (!recordFail) { + showCaptureResult(); } } else if (!recordFail){ // Start capture animation. @@ -551,10 +505,7 @@ public class VideoModule implements CameraModule, // will not be continuous for a short period of time. mUI.animateFlash(); - Bitmap bitmap = getVideoThumbnail(); - if (bitmap != null) { - mUI.animateCapture(bitmap); - } + mUI.animateCapture(); } } } @@ -631,18 +582,6 @@ public class VideoModule implements CameraModule, mMaxVideoDurationInMs = CameraSettings.getMaxVideoDuration(mActivity); } - // Set effect - mEffectType = CameraSettings.readEffectType(mPreferences); - if (mEffectType != EffectsRecorder.EFFECT_NONE) { - mEffectParameter = CameraSettings.readEffectParameter(mPreferences); - // Set quality to be no higher than 480p. - CamcorderProfile profile = CamcorderProfile.get(mCameraId, quality); - if (profile.videoFrameHeight > 480) { - quality = CamcorderProfile.QUALITY_480P; - } - } else { - mEffectParameter = null; - } // Read time lapse recording interval. if (ApiHelper.HAS_TIME_LAPSE_RECORDING) { String frameIntervalStr = mPreferences.getString( @@ -658,18 +597,11 @@ public class VideoModule implements CameraModule, mPreferenceRead = true; } - private void writeDefaultEffectToPrefs() { - ComboPreferences.Editor editor = mPreferences.edit(); - editor.putString(CameraSettings.KEY_VIDEO_EFFECT, - mActivity.getString(R.string.pref_video_effect_default)); - editor.apply(); - } - @TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB) private void getDesiredPreviewSize() { mParameters = mCameraDevice.getParameters(); if (ApiHelper.HAS_GET_SUPPORTED_VIDEO_SIZE) { - if (mParameters.getSupportedVideoSizes() == null || effectsActive()) { + if (mParameters.getSupportedVideoSizes() == null) { mDesiredPreviewWidth = mProfile.videoFrameWidth; mDesiredPreviewHeight = mProfile.videoFrameHeight; } else { // Driver supports separates outputs for preview and video. @@ -729,7 +661,6 @@ public class VideoModule implements CameraModule, showVideoSnapshotUI(false); if (!mPreviewing) { - resetEffect(); openCamera(); if (mOpenCameraFail) { CameraUtil.showErrorAndFinish(mActivity, @@ -808,10 +739,6 @@ public class VideoModule implements CameraModule, mCameraDevice.setErrorCallback(mErrorCallback); if (mPreviewing == true) { stopPreview(); - if (effectsActive() && mEffectsRecorder != null) { - mEffectsRecorder.release(); - mEffectsRecorder = null; - } } setDisplayOrientation(); @@ -819,17 +746,10 @@ public class VideoModule implements CameraModule, setCameraParameters(); try { - if (!effectsActive()) { - mCameraDevice.setPreviewTexture(surfaceTexture); - mCameraDevice.startPreview(); - mPreviewing = true; - onPreviewStarted(); - } else { - initializeEffectsPreview(); - mEffectsRecorder.startPreview(); - mPreviewing = true; - onPreviewStarted(); - } + mCameraDevice.setPreviewTexture(surfaceTexture); + mCameraDevice.startPreview(); + mPreviewing = true; + onPreviewStarted(); } catch (Throwable ex) { closeCamera(); throw new RuntimeException("startPreview failed", ex); @@ -853,49 +773,12 @@ public class VideoModule implements CameraModule, mPreviewing = false; } - // Closing the effects out. Will shut down the effects graph. - private void closeEffects() { - Log.v(TAG, "Closing effects"); - mEffectType = EffectsRecorder.EFFECT_NONE; - if (mEffectsRecorder == null) { - Log.d(TAG, "Effects are already closed. Nothing to do"); - return; - } - // This call can handle the case where the camera is already released - // after the recording has been stopped. - mEffectsRecorder.release(); - mEffectsRecorder = null; - } - - // By default, we want to close the effects as well with the camera. private void closeCamera() { - closeCamera(true); - } - - // In certain cases, when the effects are active, we may want to shutdown - // only the camera related parts, and handle closing the effects in the - // effectsUpdate callback. - // For example, in onPause, we want to make the camera available to - // outside world immediately, however, want to wait till the effects - // callback to shut down the effects. In such a case, we just disconnect - // the effects from the camera by calling disconnectCamera. That way - // the effects can handle that when shutting down. - // - // @param closeEffectsAlso - indicates whether we want to close the - // effects also along with the camera. - private void closeCamera(boolean closeEffectsAlso) { Log.v(TAG, "closeCamera"); if (mCameraDevice == null) { Log.d(TAG, "already stopped."); return; } - - if (mEffectsRecorder != null) { - // Disconnect the camera from effects so that camera is ready to - // be released to the outside world. - mEffectsRecorder.disconnectCamera(); - } - if (closeEffectsAlso) closeEffects(); mCameraDevice.setZoomChangeListener(null); mCameraDevice.setErrorCallback(null); CameraHolder.instance().release(); @@ -919,25 +802,12 @@ public class VideoModule implements CameraModule, onStopVideoRecording(); } else { closeCamera(); - if (!effectsActive()) releaseMediaRecorder(); - } - if (effectsActive()) { - // If the effects are active, make sure we tell the graph that the - // surfacetexture is not valid anymore. Disconnect the graph from - // the display. This should be done before releasing the surface - // texture. - mEffectsRecorder.disconnectDisplay(); - } else { - // Close the file descriptor and clear the video namer only if the - // effects are not active. If effects are active, we need to wait - // till we get the callback from the Effects that the graph is done - // recording. That also needs a change in the stopVideoRecording() - // call to not call closeCamera if the effects are active, because - // that will close down the effects are well, thus making this if - // condition invalid. - closeVideoFileDescriptor(); + releaseMediaRecorder(); } + closeVideoFileDescriptor(); + + releasePreviewResources(); if (mReceiver != null) { @@ -1191,98 +1061,6 @@ public class VideoModule implements CameraModule, } } - private void initializeEffectsPreview() { - Log.v(TAG, "initializeEffectsPreview"); - // If the mCameraDevice is null, then this activity is going to finish - if (mCameraDevice == null) return; - - CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId]; - - mEffectsDisplayResult = false; - mEffectsRecorder = new EffectsRecorder(mActivity); - - // TODO: Confirm none of the following need to go to initializeEffectsRecording() - // and none of these change even when the preview is not refreshed. - mEffectsRecorder.setCameraDisplayOrientation(mCameraDisplayOrientation); - mEffectsRecorder.setCamera(mCameraDevice); - mEffectsRecorder.setCameraFacing(info.facing); - mEffectsRecorder.setProfile(mProfile); - mEffectsRecorder.setEffectsListener(this); - mEffectsRecorder.setOnInfoListener(this); - mEffectsRecorder.setOnErrorListener(this); - - // The input of effects recorder is affected by - // android.hardware.Camera.setDisplayOrientation. Its value only - // compensates the camera orientation (no Display.getRotation). So the - // orientation hint here should only consider sensor orientation. - int orientation = 0; - if (mOrientation != OrientationEventListener.ORIENTATION_UNKNOWN) { - orientation = mOrientation; - } - mEffectsRecorder.setOrientationHint(orientation); - - mEffectsRecorder.setPreviewSurfaceTexture(mUI.getSurfaceTexture(), - mUI.getPreviewWidth(), mUI.getPreviewHeight()); - - if (mEffectType == EffectsRecorder.EFFECT_BACKDROPPER && - ((String) mEffectParameter).equals(EFFECT_BG_FROM_GALLERY)) { - mEffectsRecorder.setEffect(mEffectType, mEffectUriFromGallery); - } else { - mEffectsRecorder.setEffect(mEffectType, mEffectParameter); - } - } - - private void initializeEffectsRecording() { - Log.v(TAG, "initializeEffectsRecording"); - - Intent intent = mActivity.getIntent(); - Bundle myExtras = intent.getExtras(); - - long requestedSizeLimit = 0; - closeVideoFileDescriptor(); - if (mIsVideoCaptureIntent && myExtras != null) { - Uri saveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT); - if (saveUri != null) { - try { - mVideoFileDescriptor = - mContentResolver.openFileDescriptor(saveUri, "rw"); - mCurrentVideoUri = saveUri; - } catch (java.io.FileNotFoundException ex) { - // invalid uri - Log.e(TAG, ex.toString()); - } - } - requestedSizeLimit = myExtras.getLong(MediaStore.EXTRA_SIZE_LIMIT); - } - - mEffectsRecorder.setProfile(mProfile); - // important to set the capture rate to zero if not timelapsed, since the - // effectsrecorder object does not get created again for each recording - // session - if (mCaptureTimeLapse) { - mEffectsRecorder.setCaptureRate((1000 / (double) mTimeBetweenTimeLapseFrameCaptureMs)); - } else { - mEffectsRecorder.setCaptureRate(0); - } - - // Set output file - if (mVideoFileDescriptor != null) { - mEffectsRecorder.setOutputFile(mVideoFileDescriptor.getFileDescriptor()); - } else { - generateVideoFilename(mProfile.fileFormat); - mEffectsRecorder.setOutputFile(mVideoFilename); - } - - // Set maximum file size. - long maxFileSize = mActivity.getStorageSpace() - Storage.LOW_STORAGE_THRESHOLD; - if (requestedSizeLimit > 0 && requestedSizeLimit < maxFileSize) { - maxFileSize = requestedSizeLimit; - } - mEffectsRecorder.setMaxFileSize(maxFileSize); - mEffectsRecorder.setMaxDuration(mMaxVideoDurationInMs); - } - - private void releaseMediaRecorder() { Log.v(TAG, "Releasing media recorder."); if (mMediaRecorder != null) { @@ -1294,17 +1072,6 @@ public class VideoModule implements CameraModule, mVideoFilename = null; } - private void releaseEffectsRecorder() { - Log.v(TAG, "Releasing effects recorder."); - if (mEffectsRecorder != null) { - cleanupEmptyFile(); - mEffectsRecorder.release(); - mEffectsRecorder = null; - } - mEffectType = EffectsRecorder.EFFECT_NONE; - mVideoFilename = null; - } - private void generateVideoFilename(int outputFileFormat) { long dateTaken = System.currentTimeMillis(); String title = createName(dateTaken); @@ -1429,40 +1196,23 @@ public class VideoModule implements CameraModule, //?? //if (!mCameraDevice.waitDone()) return; mCurrentVideoUri = null; - if (effectsActive()) { - initializeEffectsRecording(); - if (mEffectsRecorder == null) { - Log.e(TAG, "Fail to initialize effect recorder"); - return; - } - } else { - initializeRecorder(); - if (mMediaRecorder == null) { - Log.e(TAG, "Fail to initialize media recorder"); - return; - } + + initializeRecorder(); + if (mMediaRecorder == null) { + Log.e(TAG, "Fail to initialize media recorder"); + return; } pauseAudioPlayback(); - if (effectsActive()) { - try { - mEffectsRecorder.startRecording(); - } catch (RuntimeException e) { - Log.e(TAG, "Could not start effects recorder. ", e); - releaseEffectsRecorder(); - return; - } - } else { - try { - mMediaRecorder.start(); // Recording is now started - } catch (RuntimeException e) { - Log.e(TAG, "Could not start media recorder. ", e); - releaseMediaRecorder(); - // If start fails, frameworks will not lock the camera for us. - mCameraDevice.lock(); - return; - } + try { + mMediaRecorder.start(); // Recording is now started + } catch (RuntimeException e) { + Log.e(TAG, "Could not start media recorder. ", e); + releaseMediaRecorder(); + // If start fails, frameworks will not lock the camera for us. + mCameraDevice.lock(); + return; } // Make sure the video recording has started before announcing @@ -1509,6 +1259,7 @@ public class VideoModule implements CameraModule, 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). @@ -1542,18 +1293,10 @@ public class VideoModule implements CameraModule, boolean shouldAddToMediaStoreNow = false; try { - if (effectsActive()) { - // This is asynchronous, so we can't add to media store now because thumbnail - // may not be ready. In such case saveVideo() is called later - // through a callback from the MediaEncoderFilter to EffectsRecorder, - // and then to the VideoModule. - mEffectsRecorder.stopRecording(); - } else { - mMediaRecorder.setOnErrorListener(null); - mMediaRecorder.setOnInfoListener(null); - mMediaRecorder.stop(); - shouldAddToMediaStoreNow = true; - } + mMediaRecorder.setOnErrorListener(null); + mMediaRecorder.setOnInfoListener(null); + mMediaRecorder.stop(); + shouldAddToMediaStoreNow = true; mCurrentVideoFilename = mVideoFilename; Log.v(TAG, "stopVideoRecording: Setting current video filename: " + mCurrentVideoFilename); @@ -1570,17 +1313,8 @@ public class VideoModule implements CameraModule, // If the activity is paused, this means activity is interrupted // during recording. Release the camera as soon as possible because // face unlock or other applications may need to use the camera. - // However, if the effects are active, then we can only release the - // camera and cannot release the effects recorder since that will - // stop the graph. It is possible to separate out the Camera release - // part and the effects release part. However, the effects recorder - // does hold on to the camera, hence, it needs to be "disconnected" - // from the camera in the closeCamera call. if (mPaused) { - // Closing only the camera part if effects active. Effects will - // be closed in the callback from effects. - boolean closeEffects = !effectsActive(); - closeCamera(closeEffects); + closeCamera(); } mUI.showRecordingUI(false, mParameters.isZoomSupported()); @@ -1600,17 +1334,15 @@ public class VideoModule implements CameraModule, } } } - // always release media recorder if no effects running - if (!effectsActive()) { - releaseMediaRecorder(); - if (!mPaused) { - mCameraDevice.lock(); - if (!ApiHelper.HAS_SURFACE_TEXTURE_RECORDING) { - stopPreview(); - mUI.hideSurfaceView(); - // Switch back to use SurfaceTexture for preview. - startPreview(); - } + // release media recorder + releaseMediaRecorder(); + if (!mPaused) { + mCameraDevice.lock(); + if (!ApiHelper.HAS_SURFACE_TEXTURE_RECORDING) { + stopPreview(); + mUI.hideSurfaceView(); + // Switch back to use SurfaceTexture for preview. + startPreview(); } } // Update the parameters here because the parameters might have been altered @@ -1836,84 +1568,7 @@ public class VideoModule implements CameraModule, @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { - switch (requestCode) { - case REQUEST_EFFECT_BACKDROPPER: - if (resultCode == Activity.RESULT_OK) { - // onActivityResult() runs before onResume(), so this parameter will be - // seen by startPreview from onResume() - mEffectUriFromGallery = data.getData().toString(); - Log.v(TAG, "Received URI from gallery: " + mEffectUriFromGallery); - mResetEffect = false; - } else { - mEffectUriFromGallery = null; - Log.w(TAG, "No URI from gallery"); - mResetEffect = true; - } - break; - } - } - - @Override - public void onEffectsUpdate(int effectId, int effectMsg) { - Log.v(TAG, "onEffectsUpdate. Effect Message = " + effectMsg); - if (effectMsg == EffectsRecorder.EFFECT_MSG_EFFECTS_STOPPED) { - // Effects have shut down. Hide learning message if any, - // and restart regular preview. - checkQualityAndStartPreview(); - } else if (effectMsg == EffectsRecorder.EFFECT_MSG_RECORDING_DONE) { - // This follows the codepath from onStopVideoRecording. - if (mEffectsDisplayResult) { - saveVideo(); - if (mIsVideoCaptureIntent) { - if (mQuickCapture) { - doReturnToCaller(true); - } else { - showCaptureResult(); - } - } - } - mEffectsDisplayResult = false; - // In onPause, these were not called if the effects were active. We - // had to wait till the effects recording is complete to do this. - if (mPaused) { - closeVideoFileDescriptor(); - } - } else if (effectMsg == EffectsRecorder.EFFECT_MSG_PREVIEW_RUNNING) { - // Enable the shutter button once the preview is complete. - mUI.enableShutter(true); - } - // In onPause, this was not called if the effects were active. We had to - // wait till the effects completed to do this. - if (mPaused) { - Log.v(TAG, "OnEffectsUpdate: closing effects if activity paused"); - closeEffects(); - } - } - - public void onCancelBgTraining(View v) { - // Write default effect out to shared prefs - writeDefaultEffectToPrefs(); - // Tell VideoCamer to re-init based on new shared pref values. - onSharedPreferenceChanged(); - } - - @Override - public synchronized void onEffectsError(Exception exception, String fileName) { - // TODO: Eventually we may want to show the user an error dialog, and then restart the - // camera and encoder gracefully. For now, we just delete the file and bail out. - if (fileName != null && new File(fileName).exists()) { - deleteVideoFile(fileName); - } - try { - if (Class.forName("android.filterpacks.videosink.MediaRecorderStopException") - .isInstance(exception)) { - Log.w(TAG, "Problem recoding video file. Removing incomplete file."); - return; - } - } catch (ClassNotFoundException ex) { - Log.w(TAG, ex); - } - throw new RuntimeException("Error during recording!", exception); + // Do nothing. } @Override @@ -1931,10 +1586,6 @@ public class VideoModule implements CameraModule, public void onRestorePreferencesClicked() { } - private boolean effectsActive() { - return (mEffectType != EffectsRecorder.EFFECT_NONE); - } - @Override public void onSharedPreferenceChanged() { // ignore the events after "onPause()" or preview has not started yet @@ -1948,21 +1599,14 @@ public class VideoModule implements CameraModule, mPreferences, mContentResolver); mLocationManager.recordLocation(recordLocation); - // Check if the current effects selection has changed - if (updateEffectSelection()) return; - readVideoPreferences(); mUI.showTimeLapseUI(mCaptureTimeLapse); // We need to restart the preview if preview size is changed. Size size = mParameters.getPreviewSize(); if (size.width != mDesiredPreviewWidth || size.height != mDesiredPreviewHeight) { - if (!effectsActive()) { - stopPreview(); - } else { - mEffectsRecorder.release(); - mEffectsRecorder = null; - } + + stopPreview(); resizeForPreviewAspectRatio(); startPreview(); // Parameters will be set in startPreview(). } else { @@ -2019,46 +1663,6 @@ public class VideoModule implements CameraModule, public void onCaptureTextureCopied() { } - private boolean updateEffectSelection() { - int previousEffectType = mEffectType; - Object previousEffectParameter = mEffectParameter; - mEffectType = CameraSettings.readEffectType(mPreferences); - mEffectParameter = CameraSettings.readEffectParameter(mPreferences); - - if (mEffectType == previousEffectType) { - if (mEffectType == EffectsRecorder.EFFECT_NONE) return false; - if (mEffectParameter.equals(previousEffectParameter)) return false; - } - Log.v(TAG, "New effect selection: " + mPreferences.getString( - CameraSettings.KEY_VIDEO_EFFECT, "none")); - - if (mEffectType == EffectsRecorder.EFFECT_NONE) { - // Stop effects and return to normal preview - mEffectsRecorder.stopPreview(); - mPreviewing = false; - return true; - } - if (mEffectType == EffectsRecorder.EFFECT_BACKDROPPER && - ((String) mEffectParameter).equals(EFFECT_BG_FROM_GALLERY)) { - // Request video from gallery to use for background - Intent i = new Intent(Intent.ACTION_PICK); - i.setDataAndType(Video.Media.EXTERNAL_CONTENT_URI, - "video/*"); - i.putExtra(Intent.EXTRA_LOCAL_ONLY, true); - mActivity.startActivityForResult(i, REQUEST_EFFECT_BACKDROPPER); - return true; - } - if (previousEffectType == EffectsRecorder.EFFECT_NONE) { - // Stop regular preview and start effects. - stopPreview(); - checkQualityAndStartPreview(); - } else { - // Switch currently running effect - mEffectsRecorder.setEffect(mEffectType, mEffectParameter); - } - return true; - } - // Verifies that the current preview view size is correct before starting // preview. If not, resets the surface texture and resizes the view. private void checkQualityAndStartPreview() { @@ -2090,6 +1694,7 @@ public class VideoModule implements CameraModule, if (CameraUtil.isVideoSnapshotSupported(mParameters) && !mIsVideoCaptureIntent) { if (enabled) { mUI.animateFlash(); + mUI.animateCapture(); } else { mUI.showPreviewBorder(enabled); } @@ -2147,19 +1752,6 @@ public class VideoModule implements CameraModule, exif, mOnPhotoSavedListener, mContentResolver); } - private boolean resetEffect() { - if (mResetEffect) { - String value = mPreferences.getString(CameraSettings.KEY_VIDEO_EFFECT, - mPrefVideoEffectDefault); - if (!mPrefVideoEffectDefault.equals(value)) { - writeDefaultEffectToPrefs(); - return true; - } - } - mResetEffect = true; - return false; - } - private String convertOutputFormatToMimeType(int outputFileFormat) { if (outputFileFormat == MediaRecorder.OutputFormat.MPEG_4) { return "video/mp4"; diff --git a/src/com/android/camera/VideoUI.java b/src/com/android/camera/VideoUI.java index 684179698..74d4cea9b 100644 --- a/src/com/android/camera/VideoUI.java +++ b/src/com/android/camera/VideoUI.java @@ -305,9 +305,25 @@ public class VideoUI implements PieRenderer.PieListener, /** * Starts a capture animation + */ + public void animateCapture() { + Bitmap bitmap = null; + if (mTextureView != null) { + bitmap = mTextureView.getBitmap((int) mSurfaceTextureUncroppedWidth / 2, + (int) mSurfaceTextureUncroppedHeight / 2); + } + animateCapture(bitmap); + } + + /** + * Starts a capture animation * @param bitmap the captured image that we shrink and slide in the animation */ public void animateCapture(Bitmap bitmap) { + if (bitmap == null) { + Log.e(TAG, "No valid bitmap for capture animation."); + return; + } ((ImageView) mPreviewThumb).setImageBitmap(bitmap); mAnimationManager.startCaptureAnimation(mPreviewThumb); } @@ -418,7 +434,7 @@ public class VideoUI implements PieRenderer.PieListener, mPreviewThumb.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - // TODO: Go to filmstrip view + mActivity.gotoGallery(); } }); } diff --git a/src/com/android/camera/ui/FilmStripView.java b/src/com/android/camera/ui/FilmStripView.java index 4d37f04c4..85ebd6cc5 100644 --- a/src/com/android/camera/ui/FilmStripView.java +++ b/src/com/android/camera/ui/FilmStripView.java @@ -28,6 +28,7 @@ import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; import android.widget.FrameLayout; import android.widget.Scroller; @@ -43,7 +44,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { private static final String TAG = "CAM_FilmStripView"; private static final int BUFFER_SIZE = 5; - private static final int DURATION_GEOMETRY_ADJUST = 200; + private static final int GEOMETRY_ADJUST_TIME_MS = 400; private static final int SNAP_IN_CENTER_TIME_MS = 600; private static final float FILM_STRIP_SCALE = 0.6f; private static final float FULL_SCREEN_SCALE = 1f; @@ -341,17 +342,19 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { public void fling(float velocity); - public void scrollTo(int position, int duration, boolean interruptible); + public void scrollToPosition(int position, int duration, boolean interruptible); + + public boolean goToNextItem(); public boolean stopScrolling(); public boolean isScrolling(); - public void gotoCameraFullScreen(); + public void goToCameraFullScreen(); - public void gotoFilmStrip(); + public void goToFilmStrip(); - public void gotoFullScreen(); + public void goToFullScreen(); } /** @@ -861,13 +864,13 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { if (mCenterX != currentViewCenter) { int snapInTime = (int) (SNAP_IN_CENTER_TIME_MS * Math.abs(mCenterX - currentViewCenter) / mDrawArea.width()); - mController.scrollTo(currentViewCenter, + mController.scrollToPosition(currentViewCenter, snapInTime, false); } if (getCurrentViewType() == ImageData.TYPE_STICKY_VIEW && !mController.isScalling() && mScale != FULL_SCREEN_SCALE) { - mController.gotoFullScreen(); + mController.goToFullScreen(); } } @@ -1031,7 +1034,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { private void slideViewBack(View v) { v.animate().translationX(0) .alpha(1f) - .setDuration(DURATION_GEOMETRY_ADJUST) + .setDuration(GEOMETRY_ADJUST_TIME_MS) .setInterpolator(mViewAnimInterpolator) .start(); } @@ -1143,7 +1146,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { .alpha(0f) .translationYBy(transY) .setInterpolator(mViewAnimInterpolator) - .setDuration(DURATION_GEOMETRY_ADJUST) + .setDuration(GEOMETRY_ADJUST_TIME_MS) .withEndAction(new Runnable() { @Override public void run() { @@ -1236,7 +1239,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { .alpha(1f) .translationY(0f) .setInterpolator(mViewAnimInterpolator) - .setDuration(DURATION_GEOMETRY_ADJUST) + .setDuration(GEOMETRY_ADJUST_TIME_MS) .start(); invalidate(); } @@ -1457,25 +1460,39 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener { - private ValueAnimator mScaleAnimator; + private final ValueAnimator mScaleAnimator; private boolean mHasNewScale; private float mNewScale; - private Scroller mScroller; + private final Scroller mScroller; private boolean mHasNewPosition; - private DecelerateInterpolator mDecelerateInterpolator; + private final DecelerateInterpolator mDecelerateInterpolator; + private final TimeInterpolator mDecelerateAccelerateInterpolator = + new TimeInterpolator() { + private final TimeInterpolator interpolator = + new AccelerateInterpolator(); + + @Override + public float getInterpolation(float v) { + float v2 = v * 2f; + return ((v2 < 1f) ? + mDecelerateInterpolator.getInterpolation(v2) / 2f : + interpolator.getInterpolation(v2 - 1f) / 2f + 0.5f); + } + }; private boolean mCanStopScroll; MyController(Context context) { mScroller = new Scroller(context); mHasNewPosition = false; + mCanStopScroll = true; + mHasNewScale = false; + mScaleAnimator = new ValueAnimator(); mScaleAnimator.addUpdateListener(MyController.this); mScaleAnimator.addListener(MyController.this); - mDecelerateInterpolator = new DecelerateInterpolator(); - mCanStopScroll = true; - mHasNewScale = false; + mDecelerateInterpolator = new DecelerateInterpolator(1.5f); } @Override @@ -1485,7 +1502,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { @Override public boolean isScalling() { - return mScaleAnimator.isRunning(); + return (mScaleAnimator.isRunning()); } boolean hasNewGeometry() { @@ -1553,7 +1570,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { if (inFullScreen() && getCurrentViewType() == ImageData.TYPE_STICKY_VIEW && scaledVelocityX < 0) { // Swipe left in camera preview. - gotoFilmStrip(); + goToFilmStrip(); } int w = getWidth(); @@ -1585,7 +1602,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { } @Override - public void scrollTo(int position, int duration, boolean interruptible) { + public void scrollToPosition(int position, int duration, boolean interruptible) { if (!stopScrolling()) { return; } @@ -1593,9 +1610,25 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { stopScrolling(); mScroller.startScroll(mCenterX, 0, position - mCenterX, 0, duration); + mHasNewPosition = true; invalidate(); } + @Override + public boolean goToNextItem() { + ViewItem nextItem = mViewItem[mCurrentItem + 1]; + if (nextItem == null) { + return false; + } + stopScale(); + scrollToPosition(nextItem.getCenterX(), GEOMETRY_ADJUST_TIME_MS * 2, false); + mScaleAnimator.setFloatValues(FULL_SCREEN_SCALE, FILM_STRIP_SCALE, FULL_SCREEN_SCALE); + mScaleAnimator.setInterpolator(mDecelerateAccelerateInterpolator); + mScaleAnimator.setDuration(GEOMETRY_ADJUST_TIME_MS * 2); + mScaleAnimator.start(); + return true; + } + private void scaleTo(float scale, int duration) { if (mViewItem[mCurrentItem] == null) { return; @@ -1605,13 +1638,12 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { mScaleAnimator.setFloatValues(mScale, scale); mScaleAnimator.setInterpolator(mDecelerateInterpolator); mScaleAnimator.start(); - mHasNewScale = true; layoutChildren(); } @Override - public void gotoFilmStrip() { - scaleTo(FILM_STRIP_SCALE, DURATION_GEOMETRY_ADJUST); + public void goToFilmStrip() { + scaleTo(FILM_STRIP_SCALE, GEOMETRY_ADJUST_TIME_MS); if (mListener != null) { mListener.onSwitchMode(false); mBottomControls.setVisibility(View.VISIBLE); @@ -1619,10 +1651,10 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { } @Override - public void gotoFullScreen() { + public void goToFullScreen() { if (mViewItem[mCurrentItem] != null) { - mController.scrollTo(mViewItem[mCurrentItem].getCenterX(), - DURATION_GEOMETRY_ADJUST, false); + mController.scrollToPosition(mViewItem[mCurrentItem].getCenterX(), + GEOMETRY_ADJUST_TIME_MS, false); } enterFullScreen(); } @@ -1639,21 +1671,21 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { if (inFullScreen()) { return; } - scaleTo(1f, DURATION_GEOMETRY_ADJUST); + scaleTo(1f, GEOMETRY_ADJUST_TIME_MS); } @Override - public void gotoCameraFullScreen() { + public void goToCameraFullScreen() { if (mDataAdapter.getImageData(0).getViewType() != ImageData.TYPE_STICKY_VIEW) { return; } - gotoFullScreen(); - scrollTo( + goToFullScreen(); + scrollToPosition( estimateMinX(mViewItem[mCurrentItem].getID(), mViewItem[mCurrentItem].getLeftPosition(), getWidth()), - DURATION_GEOMETRY_ADJUST, false); + GEOMETRY_ADJUST_TIME_MS, false); } @Override @@ -1668,6 +1700,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { @Override public void onAnimationStart(Animator anim) { + mHasNewScale = true; } @Override @@ -1701,7 +1734,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { if (inFilmStrip()) { ViewItem centerItem = mViewItem[mCurrentItem]; if (centerItem != null && centerItem.areaContains(x, y)) { - mController.gotoFullScreen(); + mController.goToFullScreen(); return true; } } else if (inFullScreen()) { @@ -1752,7 +1785,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { mViewItem[i].getView().animate() .translationY(0f) .alpha(1f) - .setDuration(DURATION_GEOMETRY_ADJUST) + .setDuration(GEOMETRY_ADJUST_TIME_MS) .start(); } } @@ -1772,7 +1805,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { if (deltaX > 0 && inFullScreen() && getCurrentViewType() == ImageData.TYPE_STICKY_VIEW) { - mController.gotoFilmStrip(); + mController.goToFilmStrip(); } mController.scroll(deltaX); } else { @@ -1805,7 +1838,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { } } else if (inFullScreen()) { if (deltaX > 0 && inCameraFullscreen()) { - mController.gotoFilmStrip(); + mController.goToFilmStrip(); } // Multiplied by 1.2 to make it more easy to swipe. mController.scroll((int) (deltaX * 1.2)); @@ -1860,9 +1893,9 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { @Override public void onScaleEnd() { if (mScaleTrend >= 1f) { - mController.gotoFullScreen(); + mController.goToFullScreen(); } else { - mController.gotoFilmStrip(); + mController.goToFilmStrip(); } mScaleTrend = 1f; } |