• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
onQueueStatus(boolean full)48         public void onQueueStatus(boolean full);
49     }
50 
51     interface OnMediaSavedListener {
onMediaSaved(Uri uri)52         public void onMediaSaved(Uri uri);
53     }
54 
55     class LocalBinder extends Binder {
getService()56         public MediaSaveService getService() {
57             return MediaSaveService.this;
58         }
59     }
60 
61     @Override
onBind(Intent intent)62     public IBinder onBind(Intent intent) {
63         return mBinder;
64     }
65 
66     @Override
onStartCommand(Intent intent, int flag, int startId)67     public int onStartCommand(Intent intent, int flag, int startId) {
68         return START_STICKY;
69     }
70 
71     @Override
onDestroy()72     public void onDestroy() {
73     }
74 
75     @Override
onCreate()76     public void onCreate() {
77         mTaskNumber = 0;
78     }
79 
isQueueFull()80     public boolean isQueueFull() {
81         return (mTaskNumber >= SAVE_TASK_LIMIT);
82     }
83 
84     // Runs in main thread
addImage(final byte[] data, String title, long date, Location loc, int width, int height, int orientation, ExifInterface exif, OnMediaSavedListener l, ContentResolver resolver)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 
addVideo(String path, long duration, ContentValues values, OnMediaSavedListener l, ContentResolver resolver)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 
setListener(Listener l)110     public void setListener(Listener l) {
111         mListener = l;
112         if (l == null) return;
113         l.onQueueStatus(isQueueFull());
114     }
115 
onQueueFull()116     private void onQueueFull() {
117         if (mListener != null) mListener.onQueueStatus(true);
118     }
119 
onQueueAvailable()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 
ImageSaveTask(byte[] data, String title, long date, Location loc, int width, int height, int orientation, ExifInterface exif, ContentResolver resolver, OnMediaSavedListener listener)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
onPreExecute()151         protected void onPreExecute() {
152             // do nothing.
153         }
154 
155         @Override
doInBackground(Void... v)156         protected Uri doInBackground(Void... v) {
157             return Storage.addImage(
158                     resolver, title, date, loc, orientation, exif, data, width, height);
159         }
160 
161         @Override
onPostExecute(Uri uri)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 
VideoSaveTask(String path, long duration, ContentValues values, OnMediaSavedListener l, ContentResolver r)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
onPreExecute()186         protected void onPreExecute() {
187             // do nothing.
188         }
189 
190         @Override
doInBackground(Void... v)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
onPostExecute(Uri uri)221         protected void onPostExecute(Uri uri) {
222             if (listener != null) listener.onMediaSaved(uri);
223         }
224     }
225 }
226