OSDN Git Service

Save video using MediaSaveService.
[android-x86/packages-apps-Gallery2.git] / src / com / android / camera / MediaSaveService.java
1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.android.camera;
18
19 import android.app.Service;
20 import android.content.ContentResolver;
21 import android.content.ContentValues;
22 import android.content.Intent;
23 import android.location.Location;
24 import android.net.Uri;
25 import android.os.AsyncTask;
26 import android.os.Binder;
27 import android.os.IBinder;
28 import android.provider.MediaStore.Video;
29 import android.util.Log;
30
31 import com.android.gallery3d.exif.ExifInterface;
32
33 import java.io.File;
34
35 /*
36  * Service for saving images in the background thread.
37  */
38 public class MediaSaveService extends Service {
39     private static final int SAVE_TASK_LIMIT = 3;
40     private static final String TAG = MediaSaveService.class.getSimpleName();
41
42     private final IBinder mBinder = new LocalBinder();
43     private int mTaskNumber;
44     private Listener mListener;
45
46     interface Listener {
47
48         public void onQueueStatus(boolean full);
49     }
50
51     interface OnMediaSavedListener {
52         public void onMediaSaved(Uri uri);
53     }
54
55     class LocalBinder extends Binder {
56         public MediaSaveService getService() {
57             return MediaSaveService.this;
58         }
59     }
60
61     @Override
62     public IBinder onBind(Intent intent) {
63         return mBinder;
64     }
65
66     @Override
67     public int onStartCommand(Intent intent, int flag, int startId) {
68         return START_STICKY;
69     }
70
71     @Override
72     public void onDestroy() {
73     }
74
75     @Override
76     public void onCreate() {
77         mTaskNumber = 0;
78     }
79
80     public boolean isQueueFull() {
81         return (mTaskNumber >= SAVE_TASK_LIMIT);
82     }
83
84     // Runs in main thread
85     public void addImage(final byte[] data, String title, long date, Location loc,
86             int width, int height, int orientation, ExifInterface exif,
87             OnMediaSavedListener l, ContentResolver resolver) {
88         if (isQueueFull()) {
89             Log.e(TAG, "Cannot add image when the queue is full");
90             return;
91         }
92         ImageSaveTask t = new ImageSaveTask(data, title, date,
93                 (loc == null) ? null : new Location(loc),
94                 width, height, orientation, exif, resolver, l);
95
96         mTaskNumber++;
97         if (isQueueFull()) {
98             onQueueFull();
99         }
100         t.execute();
101     }
102
103     public void addVideo(String path, long duration, ContentValues values,
104             OnMediaSavedListener l, ContentResolver resolver) {
105         // We don't set a queue limit for video saving because the file
106         // is already in the storage. Only updating the database.
107         new VideoSaveTask(path, duration, values, l, resolver).execute();
108     }
109
110     public void setListener(Listener l) {
111         mListener = l;
112         if (l == null) return;
113         l.onQueueStatus(isQueueFull());
114     }
115
116     private void onQueueFull() {
117         if (mListener != null) mListener.onQueueStatus(true);
118     }
119
120     private void onQueueAvailable() {
121         if (mListener != null) mListener.onQueueStatus(false);
122     }
123
124     private class ImageSaveTask extends AsyncTask <Void, Void, Uri> {
125         private byte[] data;
126         private String title;
127         private long date;
128         private Location loc;
129         private int width, height;
130         private int orientation;
131         private ExifInterface exif;
132         private ContentResolver resolver;
133         private OnMediaSavedListener listener;
134
135         public ImageSaveTask(byte[] data, String title, long date, Location loc,
136                              int width, int height, int orientation, ExifInterface exif,
137                              ContentResolver resolver, OnMediaSavedListener listener) {
138             this.data = data;
139             this.title = title;
140             this.date = date;
141             this.loc = loc;
142             this.width = width;
143             this.height = height;
144             this.orientation = orientation;
145             this.exif = exif;
146             this.resolver = resolver;
147             this.listener = listener;
148         }
149
150         @Override
151         protected void onPreExecute() {
152             // do nothing.
153         }
154
155         @Override
156         protected Uri doInBackground(Void... v) {
157             return Storage.addImage(
158                     resolver, title, date, loc, orientation, exif, data, width, height);
159         }
160
161         @Override
162         protected void onPostExecute(Uri uri) {
163             if (listener != null) listener.onMediaSaved(uri);
164             mTaskNumber--;
165             if (mTaskNumber == SAVE_TASK_LIMIT - 1) onQueueAvailable();
166         }
167     }
168
169     private class VideoSaveTask extends AsyncTask <Void, Void, Uri> {
170         private String path;
171         private long duration;
172         private ContentValues values;
173         private OnMediaSavedListener listener;
174         private ContentResolver resolver;
175
176         public VideoSaveTask(String path, long duration, ContentValues values,
177                 OnMediaSavedListener l, ContentResolver r) {
178             this.path = path;
179             this.duration = duration;
180             this.values = new ContentValues(values);
181             this.listener = l;
182             this.resolver = r;
183         }
184
185         @Override
186         protected void onPreExecute() {
187             // do nothing.
188         }
189
190         @Override
191         protected Uri doInBackground(Void... v) {
192             values.put(Video.Media.SIZE, new File(path).length());
193             values.put(Video.Media.DURATION, duration);
194             Uri uri = null;
195             try {
196                 Uri videoTable = Uri.parse("content://media/external/video/media");
197                 uri = resolver.insert(videoTable, values);
198
199                 // Rename the video file to the final name. This avoids other
200                 // apps reading incomplete data.  We need to do it after we are
201                 // certain that the previous insert to MediaProvider is completed.
202                 String finalName = values.getAsString(
203                         Video.Media.DATA);
204                 if (new File(path).renameTo(new File(finalName))) {
205                     path = finalName;
206                 }
207
208                 resolver.update(uri, values, null, null);
209             } catch (Exception e) {
210                 // We failed to insert into the database. This can happen if
211                 // the SD card is unmounted.
212                 Log.e(TAG, "failed to add video to media store", e);
213                 uri = null;
214             } finally {
215                 Log.v(TAG, "Current video URI: " + uri);
216             }
217             return uri;
218         }
219
220         @Override
221         protected void onPostExecute(Uri uri) {
222             if (listener != null) listener.onMediaSaved(uri);
223         }
224     }
225 }