summaryrefslogtreecommitdiffstats
path: root/src/com/android/camera/MediaSaveService.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/camera/MediaSaveService.java')
-rw-r--r--src/com/android/camera/MediaSaveService.java233
1 files changed, 233 insertions, 0 deletions
diff --git a/src/com/android/camera/MediaSaveService.java b/src/com/android/camera/MediaSaveService.java
new file mode 100644
index 000000000..40675b8c0
--- /dev/null
+++ b/src/com/android/camera/MediaSaveService.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2013 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 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.
+ */
+public class MediaSaveService extends Service {
+ // 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();
+
+ private final IBinder mBinder = new LocalBinder();
+ private Listener mListener;
+ // Memory used by the total queued save request, in bytes.
+ private long mMemoryUse;
+
+ interface Listener {
+ public void onQueueStatus(boolean full);
+ }
+
+ interface OnMediaSavedListener {
+ public void onMediaSaved(Uri uri);
+ }
+
+ class LocalBinder extends Binder {
+ public MediaSaveService getService() {
+ return MediaSaveService.this;
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flag, int startId) {
+ return START_STICKY;
+ }
+
+ @Override
+ public void onDestroy() {
+ }
+
+ @Override
+ public void onCreate() {
+ mMemoryUse = 0;
+ }
+
+ public boolean isQueueFull() {
+ return (mMemoryUse >= SAVE_TASK_MEMORY_LIMIT);
+ }
+
+ public void addImage(final byte[] data, String title, long date, Location loc,
+ int width, int height, int orientation, ExifInterface exif,
+ OnMediaSavedListener l, ContentResolver resolver) {
+ if (isQueueFull()) {
+ Log.e(TAG, "Cannot add image when the queue is full");
+ return;
+ }
+ ImageSaveTask t = new ImageSaveTask(data, title, date,
+ (loc == null) ? null : new Location(loc),
+ width, height, orientation, exif, resolver, l);
+
+ mMemoryUse += data.length;
+ if (isQueueFull()) {
+ onQueueFull();
+ }
+ t.execute();
+ }
+
+ public void addImage(final byte[] data, String title, Location loc,
+ int width, int height, int orientation, ExifInterface exif,
+ OnMediaSavedListener l, ContentResolver resolver) {
+ addImage(data, title, System.currentTimeMillis(), loc, width, height,
+ orientation, exif, l, resolver);
+ }
+
+ 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;
+ l.onQueueStatus(isQueueFull());
+ }
+
+ private void onQueueFull() {
+ if (mListener != null) mListener.onQueueStatus(true);
+ }
+
+ private void onQueueAvailable() {
+ if (mListener != null) mListener.onQueueStatus(false);
+ }
+
+ private class ImageSaveTask extends AsyncTask <Void, Void, Uri> {
+ private byte[] data;
+ private String title;
+ private long date;
+ private Location loc;
+ private int width, height;
+ private int orientation;
+ private ExifInterface exif;
+ private ContentResolver resolver;
+ private 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;
+ this.loc = loc;
+ this.width = width;
+ this.height = height;
+ this.orientation = orientation;
+ this.exif = exif;
+ this.resolver = resolver;
+ this.listener = listener;
+ }
+
+ @Override
+ protected void onPreExecute() {
+ // do nothing.
+ }
+
+ @Override
+ protected Uri doInBackground(Void... v) {
+ return Storage.addImage(
+ resolver, title, date, loc, orientation, exif, data, width, height);
+ }
+
+ @Override
+ protected void onPostExecute(Uri uri) {
+ if (listener != null) listener.onMediaSaved(uri);
+ boolean previouslyFull = isQueueFull();
+ mMemoryUse -= data.length;
+ if (isQueueFull() != previouslyFull) 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);
+ }
+ }
+}