diff options
Diffstat (limited to 'src/com/android/camera/VideoCamera.java')
-rw-r--r-- | src/com/android/camera/VideoCamera.java | 1040 |
1 files changed, 1040 insertions, 0 deletions
diff --git a/src/com/android/camera/VideoCamera.java b/src/com/android/camera/VideoCamera.java new file mode 100644 index 0000000..37e1dcb --- /dev/null +++ b/src/com/android/camera/VideoCamera.java @@ -0,0 +1,1040 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.graphics.Bitmap; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.location.LocationManager; +import android.media.MediaMetadataRetriever; +import android.media.MediaRecorder; +import android.net.Uri; +import android.os.Bundle; +import android.os.Environment; +import android.os.Handler; +import android.os.Message; +import android.os.StatFs; +import android.os.SystemClock; +import android.preference.PreferenceManager; +import android.provider.MediaStore; +import android.provider.MediaStore.Video; +import android.text.format.DateFormat; +import android.util.Log; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuItem; +import android.view.SurfaceHolder; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.view.MenuItem.OnMenuItemClickListener; +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +public class VideoCamera extends Activity implements View.OnClickListener, + ShutterButton.OnShutterButtonListener, SurfaceHolder.Callback, MediaRecorder.OnErrorListener { + + private static final String TAG = "videocamera"; + + private static final boolean DEBUG = true; + private static final boolean DEBUG_SUPPRESS_AUDIO_RECORDING = DEBUG && false; + private static final boolean DEBUG_DO_NOT_REUSE_MEDIA_RECORDER = DEBUG && true; + private static final boolean DEBUG_LOG_APP_LIFECYCLE = DEBUG && false; + + private static final int CLEAR_SCREEN_DELAY = 4; + private static final int UPDATE_RECORD_TIME = 5; + + private static final int SCREEN_DELAY = 2 * 60 * 1000; + + private static final long NO_STORAGE_ERROR = -1L; + private static final long CANNOT_STAT_ERROR = -2L; + private static final long LOW_STORAGE_THRESHOLD = 512L * 1024L; + + public static final int MENU_SETTINGS = 6; + public static final int MENU_GALLERY_PHOTOS = 7; + public static final int MENU_GALLERY_VIDEOS = 8; + public static final int MENU_SAVE_GALLERY_PHOTO = 34; + public static final int MENU_SAVE_PLAY_VIDEO = 35; + public static final int MENU_SAVE_SELECT_VIDEO = 36; + public static final int MENU_SAVE_NEW_VIDEO = 37; + + SharedPreferences mPreferences; + + private static final float VIDEO_ASPECT_RATIO = 176.0f / 144.0f; + VideoPreview mVideoPreview; + SurfaceHolder mSurfaceHolder = null; + ImageView mBlackout = null; + ImageView mVideoFrame; + Bitmap mVideoFrameBitmap; + + private MediaRecorder mMediaRecorder; + private boolean mMediaRecorderRecording = false; + private long mRecordingStartTime; + // The video file that the hardware camera is about to record into + // (or is recording into.) + private String mCameraVideoFilename; + private FileDescriptor mCameraVideoFileDescriptor; + + // The video file that has already been recorded, and that is being + // examined by the user. + private String mCurrentVideoFilename; + private Uri mCurrentVideoUri; + private ContentValues mCurrentVideoValues; + + boolean mPausing = false; + + static ContentResolver mContentResolver; + boolean mDidRegister = false; + + int mCurrentZoomIndex = 0; + + private ShutterButton mShutterButton; + private TextView mRecordingTimeView; + private boolean mHasSdCard; + + ArrayList<MenuItem> mGalleryItems = new ArrayList<MenuItem>(); + + View mPostPictureAlert; + LocationManager mLocationManager = null; + + private Handler mHandler = new MainHandler(); + + /** This Handler is used to post message back onto the main thread of the application */ + private class MainHandler extends Handler { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + + case CLEAR_SCREEN_DELAY: { + clearScreenOnFlag(); + break; + } + + case UPDATE_RECORD_TIME: { + if (mMediaRecorderRecording) { + long now = SystemClock.uptimeMillis(); + long delta = now - mRecordingStartTime; + long seconds = delta / 1000; + long minutes = seconds / 60; + long hours = minutes / 60; + long remainderMinutes = minutes - (hours * 60); + long remainderSeconds = seconds - (minutes * 60); + + String secondsString = Long.toString(remainderSeconds); + if (secondsString.length() < 2) { + secondsString = "0" + secondsString; + } + String minutesString = Long.toString(remainderMinutes); + if (minutesString.length() < 2) { + minutesString = "0" + minutesString; + } + String text = minutesString + ":" + secondsString; + if (hours > 0) { + String hoursString = Long.toString(hours); + if (hoursString.length() < 2) { + hoursString = "0" + hoursString; + } + text = hoursString + ":" + text; + } + mRecordingTimeView.setText(text); + // Work around a limitation of the T-Mobile G1: The T-Mobile + // hardware blitter can't pixel-accurately scale and clip at the same time, + // and the SurfaceFlinger doesn't attempt to work around this limitation. + // In order to avoid visual corruption we must manually refresh the entire + // surface view when changing any overlapping view's contents. + mVideoPreview.invalidate(); + mHandler.sendEmptyMessageDelayed(UPDATE_RECORD_TIME, 1000); + } + break; + } + + default: + Log.v(TAG, "Unhandled message: " + msg.what); + break; + } + } + }; + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action.equals(Intent.ACTION_MEDIA_EJECT)) { + mHasSdCard = false; + stopVideoRecording(); + initializeVideo(); + } else if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) { + // SD card available + // TODO put up a "please wait" message + // TODO also listen for the media scanner finished message + updateStorageHint(); + mHasSdCard = true; + initializeVideo(); + } else if (action.equals(Intent.ACTION_MEDIA_UNMOUNTED)) { + // SD card unavailable + updateStorageHint(); + mHasSdCard = false; + releaseMediaRecorder(); + } else if (action.equals(Intent.ACTION_MEDIA_SCANNER_STARTED)) { + Toast.makeText(VideoCamera.this, getResources().getString(R.string.wait), 5000); + } else if (action.equals(Intent.ACTION_MEDIA_SCANNER_FINISHED)) { + updateStorageHint(); + } + } + }; + + static private String createName(long dateTaken) { + return DateFormat.format("yyyy-MM-dd kk.mm.ss", dateTaken).toString(); + } + + /** Called with the activity is first created. */ + @Override + public void onCreate(Bundle icicle) { + if (DEBUG_LOG_APP_LIFECYCLE) { + Log.v(TAG, "onCreate " + this.hashCode()); + } + super.onCreate(icicle); + + mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); + + mPreferences = PreferenceManager.getDefaultSharedPreferences(this); + mContentResolver = getContentResolver(); + + //setDefaultKeyMode(DEFAULT_KEYS_SHORTCUT); + requestWindowFeature(Window.FEATURE_PROGRESS); + setContentView(R.layout.video_camera); + + mVideoPreview = (VideoPreview) findViewById(R.id.camera_preview); + mVideoPreview.setAspectRatio(VIDEO_ASPECT_RATIO); + + // don't set mSurfaceHolder here. We have it set ONLY within + // surfaceCreated / surfaceDestroyed, other parts of the code + // assume that when it is set, the surface is also set. + SurfaceHolder holder = mVideoPreview.getHolder(); + holder.addCallback(this); + holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); + + mBlackout = (ImageView) findViewById(R.id.blackout); + mBlackout.setBackgroundDrawable(new ColorDrawable(0xFF000000)); + + mPostPictureAlert = findViewById(R.id.post_picture_panel); + + int[] ids = new int[]{R.id.play, R.id.share, R.id.discard, + R.id.cancel, R.id.attach}; + for (int id : ids) { + findViewById(id).setOnClickListener(this); + } + + mShutterButton = (ShutterButton) findViewById(R.id.shutter_button); + mShutterButton.setOnShutterButtonListener(this); + mRecordingTimeView = (TextView) findViewById(R.id.recording_time); + mVideoFrame = (ImageView) findViewById(R.id.video_frame); + } + + @Override + public void onStart() { + if (DEBUG_LOG_APP_LIFECYCLE) { + Log.v(TAG, "onStart " + this.hashCode()); + } + super.onStart(); + + Thread t = new Thread(new Runnable() { + public void run() { + final boolean storageOK = getAvailableStorage() >= LOW_STORAGE_THRESHOLD; + + if (!storageOK) { + mHandler.post(new Runnable() { + public void run() { + updateStorageHint(); + } + }); + } + } + }); + t.start(); + } + + public void onClick(View v) { + switch (v.getId()) { + + case R.id.gallery: + MenuHelper.gotoCameraVideoGallery(this); + break; + + case R.id.attach: + doReturnToCaller(true); + break; + + case R.id.cancel: + doReturnToCaller(false); + break; + + case R.id.discard: { + discardCurrentVideoAndStartPreview(); + break; + } + + case R.id.share: { + Intent intent = new Intent(); + intent.setAction(Intent.ACTION_SEND); + intent.setType("video/3gpp"); + intent.putExtra(Intent.EXTRA_STREAM, mCurrentVideoUri); + try { + startActivity(Intent.createChooser(intent, getText(R.string.sendVideo))); + } catch (android.content.ActivityNotFoundException ex) { + Toast.makeText(VideoCamera.this, R.string.no_way_to_share_video, Toast.LENGTH_SHORT).show(); + } + + break; + } + + case R.id.play: { + doPlayCurrentVideo(); + break; + } + } + } + + public void onShutterButtonFocus(ShutterButton button, boolean pressed) { + switch (button.getId()) { + case R.id.shutter_button: + if (pressed) { + if (mMediaRecorderRecording) { + stopVideoRecordingAndDisplayDialog(); + } else if (mVideoFrame.getVisibility() == View.VISIBLE) { + doStartCaptureMode(); + } else { + startVideoRecording(); + } + } + break; + } + } + + public void onShutterButtonClick(ShutterButton button) { + // Do nothing (everything happens in onShutterButtonFocus). + } + + private void doStartCaptureMode() { + if (isVideoCaptureIntent()) { + discardCurrentVideoAndStartPreview(); + } else { + hideVideoFrameAndStartPreview(); + } + } + + private void doPlayCurrentVideo() { + Log.e(TAG, "Playing current video: " + mCurrentVideoUri); + Intent intent = new Intent(Intent.ACTION_VIEW, mCurrentVideoUri); + try { + startActivity(intent); + } catch (android.content.ActivityNotFoundException ex) { + Log.e(TAG, "Couldn't view video " + mCurrentVideoUri, ex); + } + } + + private void discardCurrentVideoAndStartPreview() { + deleteCurrentVideo(); + hideVideoFrameAndStartPreview(); + } + + private OnScreenHint mStorageHint; + + private void updateStorageHint() { + long remaining = getAvailableStorage(); + String errorMessage = null; + if (remaining == NO_STORAGE_ERROR) { + errorMessage = getString(R.string.no_storage); + } else if (remaining < LOW_STORAGE_THRESHOLD) { + errorMessage = getString(R.string.spaceIsLow_content); + if (mStorageHint != null) { + mStorageHint.cancel(); + mStorageHint = null; + } + } + if (errorMessage != null) { + if (mStorageHint == null) { + mStorageHint = OnScreenHint.makeText(this, errorMessage); + } else { + mStorageHint.setText(errorMessage); + } + mStorageHint.show(); + } else if (mStorageHint != null) { + mStorageHint.cancel(); + mStorageHint = null; + } + } + + @Override + public void onResume() { + if (DEBUG_LOG_APP_LIFECYCLE) { + Log.v(TAG, "onResume " + this.hashCode()); + } + super.onResume(); + + setScreenTimeoutLong(); + + mPausing = false; + + // install an intent filter to receive SD card related events. + IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_MOUNTED); + intentFilter.addAction(Intent.ACTION_MEDIA_EJECT); + intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED); + intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED); + intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED); + intentFilter.addDataScheme("file"); + registerReceiver(mReceiver, intentFilter); + mDidRegister = true; + mHasSdCard = ImageManager.hasStorage(); + + mBlackout.setVisibility(View.INVISIBLE); + if (mVideoFrameBitmap == null) { + initializeVideo(); + } else { + showPostRecordingAlert(); + } + } + + @Override + public void onStop() { + if (DEBUG_LOG_APP_LIFECYCLE) { + Log.v(TAG, "onStop " + this.hashCode()); + } + stopVideoRecording(); + setScreenTimeoutSystemDefault(); + super.onStop(); + } + + @Override + protected void onPause() { + if (DEBUG_LOG_APP_LIFECYCLE) { + Log.v(TAG, "onPause " + this.hashCode()); + } + super.onPause(); + + stopVideoRecording(); + hidePostPictureAlert(); + + mPausing = true; + + if (mDidRegister) { + unregisterReceiver(mReceiver); + mDidRegister = false; + } + mBlackout.setVisibility(View.VISIBLE); + setScreenTimeoutSystemDefault(); + + if (mStorageHint != null) { + mStorageHint.cancel(); + mStorageHint = null; + } + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + setScreenTimeoutLong(); + + switch (keyCode) { + case KeyEvent.KEYCODE_BACK: + if (mMediaRecorderRecording) { + Log.v(TAG, "onKeyBack"); + stopVideoRecordingAndDisplayDialog(); + return true; + } else if(isPostRecordingAlertVisible()) { + hideVideoFrameAndStartPreview(); + return true; + } + break; + case KeyEvent.KEYCODE_CAMERA: + if (event.getRepeatCount() == 0) { + // If we get a dpad center event without any focused view, move the + // focus to the shutter button and press it. + if (mShutterButton.isInTouchMode()) { + mShutterButton.requestFocusFromTouch(); + } else { + mShutterButton.requestFocus(); + } + mShutterButton.setPressed(true); + return true; + } + return true; + case KeyEvent.KEYCODE_DPAD_CENTER: + if (event.getRepeatCount() == 0) { + // If we get a dpad center event without any focused view, move the + // focus to the shutter button and press it. + if (mShutterButton.isInTouchMode()) { + mShutterButton.requestFocusFromTouch(); + } else { + mShutterButton.requestFocus(); + } + mShutterButton.setPressed(true); + } + break; + case KeyEvent.KEYCODE_MENU: + if (mMediaRecorderRecording) { + stopVideoRecordingAndDisplayDialog(); + return true; + } + break; + } + + return super.onKeyDown(keyCode, event); + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + switch(keyCode) { + case KeyEvent.KEYCODE_CAMERA: + mShutterButton.setPressed(false); + return true; + } + return super.onKeyUp(keyCode, event); + } + + public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { + stopVideoRecording(); + initializeVideo(); + } + + public void surfaceCreated(SurfaceHolder holder) { + mSurfaceHolder = holder; + } + + public void surfaceDestroyed(SurfaceHolder holder) { + mSurfaceHolder = null; + } + + void gotoGallery() { + MenuHelper.gotoCameraVideoGallery(this); + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + super.onPrepareOptionsMenu(menu); + + for (int i = 1; i <= MenuHelper.MENU_ITEM_MAX; i++) { + if (i != MenuHelper.GENERIC_ITEM) { + menu.setGroupVisible(i, false); + } + } + + menu.setGroupVisible(MenuHelper.VIDEO_MODE_ITEM, true); + return true; + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + + if (isVideoCaptureIntent()) { + // No options menu for attach mode. + return false; + } else { + addBaseMenuItems(menu); + MenuHelper.addImageMenuItems( + menu, + MenuHelper.INCLUDE_ALL & ~MenuHelper.INCLUDE_ROTATE_MENU, + false, + VideoCamera.this, + mHandler, + + // Handler for deletion + new Runnable() { + public void run() { + // What do we do here? + // mContentResolver.delete(uri, null, null); + } + }, + new MenuHelper.MenuInvoker() { + public void run(final MenuHelper.MenuCallback cb) { + } + }); + + MenuItem gallery = menu.add(MenuHelper.IMAGE_SAVING_ITEM, MENU_SAVE_GALLERY_PHOTO, 0, + R.string.camera_gallery_photos_text).setOnMenuItemClickListener( + new MenuItem.OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + gotoGallery(); + return true; + } + }); + gallery.setIcon(android.R.drawable.ic_menu_gallery); + } + return true; + } + + private boolean isVideoCaptureIntent() { + String action = getIntent().getAction(); + return (MediaStore.ACTION_VIDEO_CAPTURE.equals(action)); + } + + private void doReturnToCaller(boolean success) { + Intent resultIntent = new Intent(); + int resultCode; + if (success) { + resultCode = RESULT_OK; + resultIntent.setData(mCurrentVideoUri); + } else { + resultCode = RESULT_CANCELED; + } + setResult(resultCode, resultIntent); + finish(); + } + + /** + * Returns + * @return number of bytes available, or an ERROR code. + */ + private static long getAvailableStorage() { + try { + if (!ImageManager.hasStorage()) { + return NO_STORAGE_ERROR; + } else { + String storageDirectory = Environment.getExternalStorageDirectory().toString(); + StatFs stat = new StatFs(storageDirectory); + return ((long)stat.getAvailableBlocks() * (long)stat.getBlockSize()); + } + } catch (Exception ex) { + // if we can't stat the filesystem then we don't know how many + // free bytes exist. It might be zero but just leave it + // blank since we really don't know. + return CANNOT_STAT_ERROR; + } + } + + private void cleanupEmptyFile() { + if (mCameraVideoFilename != null) { + File f = new File(mCameraVideoFilename); + if (f.length() == 0 && f.delete()) { + Log.v(TAG, "Empty video file deleted: " + mCameraVideoFilename); + mCameraVideoFilename = null; + } + } + } + + // Returns false if initializeVideo fails + private boolean initializeVideo() { + Log.v(TAG, "initializeVideo"); + boolean isCaptureIntent = isVideoCaptureIntent(); + Intent intent = getIntent(); + Bundle myExtras = intent.getExtras(); + + if (isCaptureIntent && myExtras != null) { + Uri saveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT); + if (saveUri != null) { + try { + mCameraVideoFileDescriptor = mContentResolver. + openFileDescriptor(saveUri, "rw").getFileDescriptor(); + mCurrentVideoUri = saveUri; + } + catch (java.io.FileNotFoundException ex) { + // invalid uri + Log.e(TAG, ex.toString()); + } + } + } + releaseMediaRecorder(); + + if (mSurfaceHolder == null) { + Log.v(TAG, "SurfaceHolder is null"); + return false; + } + + mMediaRecorder = new MediaRecorder(); + + if (DEBUG_SUPPRESS_AUDIO_RECORDING) { + Log.v(TAG, "DEBUG_SUPPRESS_AUDIO_RECORDING is true."); + } else { + mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); + } + mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); + mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); + + if (!mHasSdCard) { + mMediaRecorder.setOutputFile("/dev/null"); + } else { + // We try Uri in intent first. If it doesn't work, use our own instead. + if (mCameraVideoFileDescriptor != null) { + mMediaRecorder.setOutputFile(mCameraVideoFileDescriptor); + } else { + createVideoPath(); + mMediaRecorder.setOutputFile(mCameraVideoFilename); + } + } + + boolean videoQualityHigh = getBooleanPreference(CameraSettings.KEY_VIDEO_QUALITY, + CameraSettings.DEFAULT_VIDEO_QUALITY_VALUE); + + if (intent.hasExtra(MediaStore.EXTRA_VIDEO_QUALITY)) { + int extraVideoQuality = intent.getIntExtra(MediaStore.EXTRA_VIDEO_QUALITY, 0); + videoQualityHigh = (extraVideoQuality > 0); + } + + // Use the same frame rate for both, since internally + // if the frame rate is too large, it can cause camera to become + // unstable. We need to fix the MediaRecorder to disable the support + // of setting frame rate for now. + mMediaRecorder.setVideoFrameRate(20); + if (videoQualityHigh) { + mMediaRecorder.setVideoSize(352,288); + } else { + mMediaRecorder.setVideoSize(176,144); + } + mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H263); + if (!DEBUG_SUPPRESS_AUDIO_RECORDING) { + mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); + } + mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface()); + try { + mMediaRecorder.prepare(); + } catch (IOException exception) { + Log.e(TAG, "prepare failed for " + mCameraVideoFilename); + releaseMediaRecorder(); + // TODO: add more exception handling logic here + return false; + } + mMediaRecorderRecording = false; + return true; + } + + private void releaseMediaRecorder() { + Log.v(TAG, "Releasing media recorder."); + if (mMediaRecorder != null) { + cleanupEmptyFile(); + mMediaRecorder.reset(); + mMediaRecorder.release(); + mMediaRecorder = null; + } + } + + private void restartPreview() { + if (DEBUG_DO_NOT_REUSE_MEDIA_RECORDER) { + Log.v(TAG, "DEBUG_DO_NOT_REUSE_MEDIA_RECORDER recreating mMediaRecorder."); + initializeVideo(); + } else { + try { + mMediaRecorder.prepare(); + } catch (IOException exception) { + Log.e(TAG, "prepare failed for " + mCameraVideoFilename); + releaseMediaRecorder(); + // TODO: add more exception handling logic here + } + } + } + + private int getIntPreference(String key, int defaultValue) { + String s = mPreferences.getString(key, ""); + int result = defaultValue; + try { + result = Integer.parseInt(s); + } catch (NumberFormatException e) { + // Ignore, result is already the default value. + } + return result; + } + + private boolean getBooleanPreference(String key, boolean defaultValue) { + return getIntPreference(key, defaultValue ? 1 : 0) != 0; + } + + private void createVideoPath() { + long dateTaken = System.currentTimeMillis(); + String title = createName(dateTaken); + String displayName = title + ".3gp"; // Used when emailing. + String cameraDirPath = ImageManager.CAMERA_IMAGE_BUCKET_NAME; + File cameraDir = new File(cameraDirPath); + cameraDir.mkdirs(); + SimpleDateFormat dateFormat = new SimpleDateFormat( + getString(R.string.video_file_name_format)); + Date date = new Date(dateTaken); + String filepart = dateFormat.format(date); + String filename = cameraDirPath + "/" + filepart + ".3gp"; + ContentValues values = new ContentValues(7); + values.put(Video.Media.TITLE, title); + values.put(Video.Media.DISPLAY_NAME, displayName); + values.put(Video.Media.DESCRIPTION, ""); + values.put(Video.Media.DATE_TAKEN, dateTaken); + values.put(Video.Media.MIME_TYPE, "video/3gpp"); + values.put(Video.Media.DATA, filename); + mCameraVideoFilename = filename; + Log.v(TAG, "Current camera video filename: " + mCameraVideoFilename); + mCurrentVideoValues = values; + } + + private void registerVideo() { + if (mCameraVideoFileDescriptor == null) { + Uri videoTable = Uri.parse("content://media/external/video/media"); + mCurrentVideoUri = mContentResolver.insert(videoTable, + mCurrentVideoValues); + Log.v(TAG, "Current video URI: " + mCurrentVideoUri); + } + mCurrentVideoValues = null; + } + + private void deleteCurrentVideo() { + if (mCurrentVideoFilename != null) { + deleteVideoFile(mCurrentVideoFilename); + mCurrentVideoFilename = null; + } + if (mCurrentVideoUri != null) { + mContentResolver.delete(mCurrentVideoUri, null, null); + mCurrentVideoUri = null; + } + } + + private void deleteVideoFile(String fileName) { + Log.v(TAG, "Deleting video " + fileName); + File f = new File(fileName); + if (! f.delete()) { + Log.v(TAG, "Could not delete " + fileName); + } + } + + private void addBaseMenuItems(Menu menu) { + MenuHelper.addSwitchModeMenuItem(menu, this, false); + { + MenuItem gallery = menu.add(MenuHelper.IMAGE_MODE_ITEM, MENU_GALLERY_PHOTOS, 0, R.string.camera_gallery_photos_text).setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + gotoGallery(); + return true; + } + }); + gallery.setIcon(android.R.drawable.ic_menu_gallery); + mGalleryItems.add(gallery); + } + { + MenuItem gallery = menu.add(MenuHelper.VIDEO_MODE_ITEM, MENU_GALLERY_VIDEOS, 0, R.string.camera_gallery_photos_text).setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + gotoGallery(); + return true; + } + }); + gallery.setIcon(android.R.drawable.ic_menu_gallery); + mGalleryItems.add(gallery); + } + + MenuItem item = menu.add(MenuHelper.GENERIC_ITEM, MENU_SETTINGS, 0, R.string.settings).setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + Intent intent = new Intent(); + intent.setClass(VideoCamera.this, CameraSettings.class); + startActivity(intent); + return true; + } + }); + item.setIcon(android.R.drawable.ic_menu_preferences); + } + + // from MediaRecorder.OnErrorListener + public void onError(MediaRecorder mr, int what, int extra) { + if (what == MediaRecorder.MEDIA_RECORDER_ERROR_UNKNOWN) { + // We may have run out of space on the sdcard. + stopVideoRecording(); + updateStorageHint(); + } + } + + private void startVideoRecording() { + Log.v(TAG, "startVideoRecording"); + if (!mMediaRecorderRecording) { + + if (mStorageHint != null) { + Log.v(TAG, "Storage issue, ignore the start request"); + return; + } + + // Check mMediaRecorder to see whether it is initialized or not. + if (mMediaRecorder == null && initializeVideo() == false ) { + Log.e(TAG, "Initialize video (MediaRecorder) failed."); + return; + } + + try { + mMediaRecorder.setOnErrorListener(this); + mMediaRecorder.start(); // Recording is now started + } catch (RuntimeException e) { + Log.e(TAG, "Could not start media recorder. ", e); + return; + } + mMediaRecorderRecording = true; + mRecordingStartTime = SystemClock.uptimeMillis(); + updateRecordingIndicator(true); + mRecordingTimeView.setText(""); + mRecordingTimeView.setVisibility(View.VISIBLE); + mHandler.sendEmptyMessage(UPDATE_RECORD_TIME); + setScreenTimeoutInfinite(); + } + } + + private void updateRecordingIndicator(boolean showRecording) { + int drawableId = showRecording ? R.drawable.ic_camera_bar_indicator_record + : R.drawable.ic_camera_indicator_video; + Drawable drawable = getResources().getDrawable(drawableId); + mShutterButton.setImageDrawable(drawable); + } + + private void stopVideoRecordingAndDisplayDialog() { + Log.v(TAG, "stopVideoRecordingAndDisplayDialog"); + if (mMediaRecorderRecording) { + stopVideoRecording(); + acquireAndShowVideoFrame(); + showPostRecordingAlert(); + } + } + + private void showPostRecordingAlert() { + int[] pickIds = {R.id.attach, R.id.cancel}; + int[] normalIds = {R.id.gallery, R.id.share, R.id.discard}; + int[] alwaysOnIds = {R.id.play}; + int[] hideIds = pickIds; + int[] connectIds = normalIds; + if (isVideoCaptureIntent()) { + hideIds = normalIds; + connectIds = pickIds; + } + for(int id : hideIds) { + mPostPictureAlert.findViewById(id).setVisibility(View.GONE); + } + connectAndFadeIn(connectIds); + connectAndFadeIn(alwaysOnIds); + mPostPictureAlert.setVisibility(View.VISIBLE); + } + + private void connectAndFadeIn(int[] connectIds) { + for(int id : connectIds) { + View view = mPostPictureAlert.findViewById(id); + view.setOnClickListener(this); + Animation animation = new AlphaAnimation(0F, 1F); + animation.setDuration(500); + view.setAnimation(animation); + } + } + + private void hidePostPictureAlert() { + mPostPictureAlert.setVisibility(View.INVISIBLE); + } + + private boolean isPostRecordingAlertVisible() { + return mPostPictureAlert.getVisibility() == View.VISIBLE; + } + + private void stopVideoRecording() { + Log.v(TAG, "stopVideoRecording"); + boolean needToRegisterRecording = false; + if (mMediaRecorderRecording || mMediaRecorder != null) { + if (mMediaRecorderRecording && mMediaRecorder != null) { + try { + mMediaRecorder.setOnErrorListener(null); + mMediaRecorder.stop(); + } catch (RuntimeException e) { + Log.e(TAG, "stop fail: " + e.getMessage()); + } + mCurrentVideoFilename = mCameraVideoFilename; + Log.v(TAG, "Setting current video filename: " + mCurrentVideoFilename); + needToRegisterRecording = true; + mMediaRecorderRecording = false; + } + releaseMediaRecorder(); + updateRecordingIndicator(false); + mRecordingTimeView.setVisibility(View.GONE); + setScreenTimeoutLong(); + } + if (needToRegisterRecording && mHasSdCard) registerVideo(); + + mCameraVideoFilename = null; + mCameraVideoFileDescriptor = null; + } + + private void setScreenTimeoutSystemDefault() { + mHandler.removeMessages(CLEAR_SCREEN_DELAY); + clearScreenOnFlag(); + } + + private void setScreenTimeoutLong() { + mHandler.removeMessages(CLEAR_SCREEN_DELAY); + setScreenOnFlag(); + mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY); + } + + private void setScreenTimeoutInfinite() { + mHandler.removeMessages(CLEAR_SCREEN_DELAY); + setScreenOnFlag(); + } + + private void clearScreenOnFlag() { + Window w = getWindow(); + final int keepScreenOnFlag = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; + if ((w.getAttributes().flags & keepScreenOnFlag) != 0) { + w.clearFlags(keepScreenOnFlag); + } + } + + private void setScreenOnFlag() { + Window w = getWindow(); + final int keepScreenOnFlag = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; + if ((w.getAttributes().flags & keepScreenOnFlag) == 0) { + w.addFlags(keepScreenOnFlag); + } + } + + private void hideVideoFrameAndStartPreview() { + hidePostPictureAlert(); + hideVideoFrame(); + restartPreview(); + } + + private void acquireAndShowVideoFrame() { + recycleVideoFrameBitmap(); + mVideoFrameBitmap = ImageManager.createVideoThumbnail(mCurrentVideoFilename); + mVideoFrame.setImageBitmap(mVideoFrameBitmap); + mVideoFrame.setVisibility(View.VISIBLE); + } + + private void hideVideoFrame() { + recycleVideoFrameBitmap(); + mVideoFrame.setVisibility(View.GONE); + } + + private void recycleVideoFrameBitmap() { + if (mVideoFrameBitmap != null) { + mVideoFrame.setImageDrawable(null); + mVideoFrameBitmap.recycle(); + mVideoFrameBitmap = null; + } + } +} + |