OSDN Git Service

Save video using MediaSaveService.
authorAngus Kong <shkong@google.com>
Wed, 17 Apr 2013 22:37:07 +0000 (15:37 -0700)
committerAngus Kong <shkong@google.com>
Fri, 19 Apr 2013 19:46:08 +0000 (12:46 -0700)
bug:8511719
Change-Id: I3fc5054bf7661953181b27e66cc70ba95082d36c

src/com/android/camera/MediaSaveService.java
src/com/android/camera/VideoModule.java

index b1f47df..e37b45c 100644 (file)
@@ -18,16 +18,20 @@ package com.android.camera;
 
 import android.app.Service;
 import android.content.ContentResolver;
+import android.content.ContentValues;
 import android.content.Intent;
 import android.location.Location;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Binder;
 import android.os.IBinder;
+import android.provider.MediaStore.Video;
 import android.util.Log;
 
 import com.android.gallery3d.exif.ExifInterface;
 
+import java.io.File;
+
 /*
  * Service for saving images in the background thread.
  */
@@ -85,7 +89,8 @@ public class MediaSaveService extends Service {
             Log.e(TAG, "Cannot add image when the queue is full");
             return;
         }
-        SaveTask t = new SaveTask(data, title, date, (loc == null) ? null : new Location(loc),
+        ImageSaveTask t = new ImageSaveTask(data, title, date,
+                (loc == null) ? null : new Location(loc),
                 width, height, orientation, exif, resolver, l);
 
         mTaskNumber++;
@@ -95,6 +100,13 @@ public class MediaSaveService extends Service {
         t.execute();
     }
 
+    public void addVideo(String path, long duration, ContentValues values,
+            OnMediaSavedListener l, ContentResolver resolver) {
+        // We don't set a queue limit for video saving because the file
+        // is already in the storage. Only updating the database.
+        new VideoSaveTask(path, duration, values, l, resolver).execute();
+    }
+
     public void setListener(Listener l) {
         mListener = l;
         if (l == null) return;
@@ -109,7 +121,7 @@ public class MediaSaveService extends Service {
         if (mListener != null) mListener.onQueueStatus(false);
     }
 
-    private class SaveTask extends AsyncTask <Void, Void, Uri> {
+    private class ImageSaveTask extends AsyncTask <Void, Void, Uri> {
         private byte[] data;
         private String title;
         private long date;
@@ -120,9 +132,9 @@ public class MediaSaveService extends Service {
         private ContentResolver resolver;
         private OnMediaSavedListener listener;
 
-        public SaveTask(byte[] data, String title, long date, Location loc,
-                int width, int height, int orientation, ExifInterface exif,
-                ContentResolver resolver, OnMediaSavedListener listener) {
+        public ImageSaveTask(byte[] data, String title, long date, Location loc,
+                             int width, int height, int orientation, ExifInterface exif,
+                             ContentResolver resolver, OnMediaSavedListener listener) {
             this.data = data;
             this.title = title;
             this.date = date;
@@ -148,9 +160,66 @@ public class MediaSaveService extends Service {
 
         @Override
         protected void onPostExecute(Uri uri) {
-            listener.onMediaSaved(uri);
+            if (listener != null) listener.onMediaSaved(uri);
             mTaskNumber--;
             if (mTaskNumber == SAVE_TASK_LIMIT - 1) onQueueAvailable();
         }
     }
+
+    private class VideoSaveTask extends AsyncTask <Void, Void, Uri> {
+        private String path;
+        private long duration;
+        private ContentValues values;
+        private OnMediaSavedListener listener;
+        private ContentResolver resolver;
+
+        public VideoSaveTask(String path, long duration, ContentValues values,
+                OnMediaSavedListener l, ContentResolver r) {
+            this.path = path;
+            this.duration = duration;
+            this.values = new ContentValues(values);
+            this.listener = l;
+            this.resolver = r;
+        }
+
+        @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 = resolver.insert(videoTable, values);
+
+                // Rename the video file to the final name. This avoids other
+                // apps reading incomplete data.  We need to do it after we are
+                // certain that the previous insert to MediaProvider is completed.
+                String finalName = values.getAsString(
+                        Video.Media.DATA);
+                if (new File(path).renameTo(new File(finalName))) {
+                    path = finalName;
+                }
+
+                resolver.update(uri, values, null, null);
+            } catch (Exception e) {
+                // We failed to insert into the database. This can happen if
+                // the SD card is unmounted.
+                Log.e(TAG, "failed to add video to media store", e);
+                uri = null;
+            } finally {
+                Log.v(TAG, "Current video URI: " + uri);
+            }
+            return uri;
+        }
+
+        @Override
+        protected void onPostExecute(Uri uri) {
+            if (listener != null) listener.onMediaSaved(uri);
+        }
+    }
 }
index d4be225..9549c73 100644 (file)
@@ -174,8 +174,6 @@ public class VideoModule implements CameraModule,
 
     private LocationManager mLocationManager;
 
-    private VideoNamer mVideoNamer;
-
     private int mPendingSwitchCameraId;
 
     private final Handler mHandler = new MainHandler();
@@ -188,7 +186,20 @@ public class VideoModule implements CameraModule,
     private boolean mRestoreFlash;  // This is used to check if we need to restore the flash
                                     // status when going back from gallery.
 
-    private MediaSaveService.OnMediaSavedListener mOnMediaSavedListener =
+    private final MediaSaveService.OnMediaSavedListener mOnVideoSavedListener =
+            new MediaSaveService.OnMediaSavedListener() {
+                @Override
+                public void onMediaSaved(Uri uri) {
+                    if (uri != null) {
+                        mActivity.addSecureAlbumItemIfNeeded(true, uri);
+                        mActivity.sendBroadcast(
+                                new Intent(Util.ACTION_NEW_VIDEO, uri));
+                        Util.broadcastNewPicture(mActivity, uri);
+                    }
+                }
+            };
+
+    private final MediaSaveService.OnMediaSavedListener mOnPhotoSavedListener =
             new MediaSaveService.OnMediaSavedListener() {
                 @Override
                 public void onMediaSaved(Uri uri) {
@@ -761,7 +772,6 @@ public class VideoModule implements CameraModule,
         // Dismiss open menu if exists.
         PopupManager.getInstance(mActivity).notifyShowPopup(null);
 
-        mVideoNamer = new VideoNamer();
         UsageStatistics.onContentViewChanged(
                 UsageStatistics.COMPONENT_CAMERA, "VideoModule");
     }
@@ -950,7 +960,6 @@ public class VideoModule implements CameraModule,
             // that will close down the effects are well, thus making this if
             // condition invalid.
             closeVideoFileDescriptor();
-            clearVideoNamer();
         }
 
         releasePreviewResources();
@@ -1349,69 +1358,25 @@ public class VideoModule implements CameraModule,
             mCurrentVideoValues.put(Video.Media.LATITUDE, loc.getLatitude());
             mCurrentVideoValues.put(Video.Media.LONGITUDE, loc.getLongitude());
         }
-        mVideoNamer.prepareUri(mContentResolver, mCurrentVideoValues);
         mVideoFilename = tmpPath;
         Log.v(TAG, "New video filename: " + mVideoFilename);
     }
 
-    private boolean addVideoToMediaStore() {
-        boolean fail = false;
+    private void saveVideo() {
         if (mVideoFileDescriptor == null) {
-            mCurrentVideoValues.put(Video.Media.SIZE,
-                    new File(mCurrentVideoFilename).length());
             long duration = SystemClock.uptimeMillis() - mRecordingStartTime;
             if (duration > 0) {
                 if (mCaptureTimeLapse) {
                     duration = getTimeLapseVideoLength(duration);
                 }
-                mCurrentVideoValues.put(Video.Media.DURATION, duration);
             } else {
                 Log.w(TAG, "Video duration <= 0 : " + duration);
             }
-            try {
-                mCurrentVideoUri = mVideoNamer.getUri();
-                mActivity.addSecureAlbumItemIfNeeded(true, mCurrentVideoUri);
-
-                // Rename the video file to the final name. This avoids other
-                // apps reading incomplete data.  We need to do it after the
-                // above mVideoNamer.getUri() call, so we are certain that the
-                // previous insert to MediaProvider is completed.
-                String finalName = mCurrentVideoValues.getAsString(
-                        Video.Media.DATA);
-                if (new File(mCurrentVideoFilename).renameTo(new File(finalName))) {
-                    mCurrentVideoFilename = finalName;
-                }
-
-                mContentResolver.update(mCurrentVideoUri, mCurrentVideoValues
-                        , null, null);
-                mActivity.sendBroadcast(new Intent(Util.ACTION_NEW_VIDEO,
-                        mCurrentVideoUri));
-            } catch (Exception e) {
-                // We failed to insert into the database. This can happen if
-                // the SD card is unmounted.
-                Log.e(TAG, "failed to add video to media store", e);
-                mCurrentVideoUri = null;
-                mCurrentVideoFilename = null;
-                fail = true;
-            } finally {
-                Log.v(TAG, "Current video URI: " + mCurrentVideoUri);
-            }
+            mActivity.getMediaSaveService().addVideo(mCurrentVideoFilename,
+                    duration, mCurrentVideoValues,
+                    mOnVideoSavedListener, mContentResolver);
         }
         mCurrentVideoValues = null;
-        return fail;
-    }
-
-    private void deleteCurrentVideo() {
-        // Remove the video and the uri if the uri is not passed in by intent.
-        if (mCurrentVideoFilename != null) {
-            deleteVideoFile(mCurrentVideoFilename);
-            mCurrentVideoFilename = null;
-            if (mCurrentVideoUri != null) {
-                mContentResolver.delete(mCurrentVideoUri, null, null);
-                mCurrentVideoUri = null;
-            }
-        }
-        mActivity.updateStorageSpaceAndHint();
     }
 
     private void deleteVideoFile(String fileName) {
@@ -1599,7 +1564,7 @@ public class VideoModule implements CameraModule,
             try {
                 if (effectsActive()) {
                     // This is asynchronous, so we can't add to media store now because thumbnail
-                    // may not be ready. In such case addVideoToMediaStore is called later
+                    // 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();
@@ -1647,7 +1612,7 @@ public class VideoModule implements CameraModule,
             mUI.setOrientationIndicator(0, true);
             keepScreenOnAwhile();
             if (shouldAddToMediaStoreNow) {
-                if (addVideoToMediaStore()) fail = true;
+                saveVideo();
             }
         }
         // always release media recorder if no effects running
@@ -1934,7 +1899,8 @@ public class VideoModule implements CameraModule,
             checkQualityAndStartPreview();
         } else if (effectMsg == EffectsRecorder.EFFECT_MSG_RECORDING_DONE) {
             // This follows the codepath from onStopVideoRecording.
-            if (mEffectsDisplayResult && !addVideoToMediaStore()) {
+            if (mEffectsDisplayResult) {
+                saveVideo();
                 if (mIsVideoCaptureIntent) {
                     if (mQuickCapture) {
                         doReturnToCaller(true);
@@ -1948,7 +1914,6 @@ public class VideoModule implements CameraModule,
             // had to wait till the effects recording is complete to do this.
             if (mPaused) {
                 closeVideoFileDescriptor();
-                clearVideoNamer();
             }
         } else if (effectMsg == EffectsRecorder.EFFECT_MSG_PREVIEW_RUNNING) {
             // Enable the shutter button once the preview is complete.
@@ -2232,7 +2197,7 @@ public class VideoModule implements CameraModule,
         Size s = mParameters.getPictureSize();
         mActivity.getMediaSaveService().addImage(
                 data, title, dateTaken, loc, s.width, s.height, orientation,
-                exif, mOnMediaSavedListener, mContentResolver);
+                exif, mOnPhotoSavedListener, mContentResolver);
     }
 
     private boolean resetEffect() {
@@ -2282,90 +2247,6 @@ public class VideoModule implements CameraModule,
         editor.apply();
     }
 
-    private void clearVideoNamer() {
-        if (mVideoNamer != null) {
-            mVideoNamer.finish();
-            mVideoNamer = null;
-        }
-    }
-
-    private static class VideoNamer extends Thread {
-        private boolean mRequestPending;
-        private ContentResolver mResolver;
-        private ContentValues mValues;
-        private boolean mStop;
-        private Uri mUri;
-
-        // Runs in main thread
-        public VideoNamer() {
-            start();
-        }
-
-        // Runs in main thread
-        public synchronized void prepareUri(
-                ContentResolver resolver, ContentValues values) {
-            mRequestPending = true;
-            mResolver = resolver;
-            mValues = new ContentValues(values);
-            notifyAll();
-        }
-
-        // Runs in main thread
-        public synchronized Uri getUri() {
-            // wait until the request is done.
-            while (mRequestPending) {
-                try {
-                    wait();
-                } catch (InterruptedException ex) {
-                    // ignore.
-                }
-            }
-            Uri uri = mUri;
-            mUri = null;
-            return uri;
-        }
-
-        // Runs in namer thread
-        @Override
-        public synchronized void run() {
-            while (true) {
-                if (mStop) break;
-                if (!mRequestPending) {
-                    try {
-                        wait();
-                    } catch (InterruptedException ex) {
-                        // ignore.
-                    }
-                    continue;
-                }
-                cleanOldUri();
-                generateUri();
-                mRequestPending = false;
-                notifyAll();
-            }
-            cleanOldUri();
-        }
-
-        // Runs in main thread
-        public synchronized void finish() {
-            mStop = true;
-            notifyAll();
-        }
-
-        // Runs in namer thread
-        private void generateUri() {
-            Uri videoTable = Uri.parse("content://media/external/video/media");
-            mUri = mResolver.insert(videoTable, mValues);
-        }
-
-        // Runs in namer thread
-        private void cleanOldUri() {
-            if (mUri == null) return;
-            mResolver.delete(mUri, null, null);
-            mUri = null;
-        }
-    }
-
     @Override
     public boolean updateStorageHintOnResume() {
         return true;