MediaSaveService.java revision c40c411683da5db1e393e2172a451c3f9c511811
1ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong/* 2ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong * Copyright (C) 2013 The Android Open Source Project 3ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong * 4ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong * Licensed under the Apache License, Version 2.0 (the "License"); 5ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong * you may not use this file except in compliance with the License. 6ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong * You may obtain a copy of the License at 7ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong * 8ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong * http://www.apache.org/licenses/LICENSE-2.0 9ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong * 10ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong * Unless required by applicable law or agreed to in writing, software 11ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong * distributed under the License is distributed on an "AS IS" BASIS, 12ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong * See the License for the specific language governing permissions and 14ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong * limitations under the License. 15ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong */ 16ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong 17ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kongpackage com.android.camera; 18ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong 19ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kongimport android.app.Service; 20ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kongimport android.content.ContentResolver; 2183a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kongimport android.content.ContentValues; 22ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kongimport android.content.Intent; 23ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kongimport android.location.Location; 24ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kongimport android.net.Uri; 25ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kongimport android.os.AsyncTask; 26ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kongimport android.os.Binder; 27ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kongimport android.os.IBinder; 2883a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kongimport android.provider.MediaStore.Video; 29ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kongimport android.util.Log; 30ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong 310d00a8907096b9970ac64f52abbd2bfc1ed751b6Angus Kongimport com.android.gallery3d.exif.ExifInterface; 320d00a8907096b9970ac64f52abbd2bfc1ed751b6Angus Kong 3383a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kongimport java.io.File; 3483a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong 3586d36313d88fe96354f2cdd4f378e5ff8397c458Angus Kong/* 3686d36313d88fe96354f2cdd4f378e5ff8397c458Angus Kong * Service for saving images in the background thread. 3786d36313d88fe96354f2cdd4f378e5ff8397c458Angus Kong */ 38ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kongpublic class MediaSaveService extends Service { 39c40c411683da5db1e393e2172a451c3f9c511811Angus Kong // The memory limit for unsaved image is 20MB. 40c40c411683da5db1e393e2172a451c3f9c511811Angus Kong private static final int SAVE_TASK_MEMORY_LIMIT = 20 * 1024 * 1024; 41c40c411683da5db1e393e2172a451c3f9c511811Angus Kong private static final String TAG = "CAM_" + MediaSaveService.class.getSimpleName(); 42ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong 43ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong private final IBinder mBinder = new LocalBinder(); 44ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong private Listener mListener; 45c40c411683da5db1e393e2172a451c3f9c511811Angus Kong // Memory used by the total queued save request, in bytes. 46c40c411683da5db1e393e2172a451c3f9c511811Angus Kong private long mMemoryUse; 47ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong 48ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong interface Listener { 49d6954f337e20365fc24ecffdd6f30e17c6b31effMichael Kolb public void onQueueStatus(boolean full); 50ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong } 51ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong 52ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong interface OnMediaSavedListener { 53ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong public void onMediaSaved(Uri uri); 54ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong } 55ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong 56ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong class LocalBinder extends Binder { 57ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong public MediaSaveService getService() { 58ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong return MediaSaveService.this; 59ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong } 60ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong } 61ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong 62ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong @Override 63ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong public IBinder onBind(Intent intent) { 64ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong return mBinder; 65ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong } 66ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong 67ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong @Override 68ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong public int onStartCommand(Intent intent, int flag, int startId) { 69ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong return START_STICKY; 70ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong } 71ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong 72ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong @Override 73ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong public void onDestroy() { 74ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong } 75ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong 76ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong @Override 77ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong public void onCreate() { 78c40c411683da5db1e393e2172a451c3f9c511811Angus Kong mMemoryUse = 0; 79ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong } 80ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong 81ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong public boolean isQueueFull() { 82c40c411683da5db1e393e2172a451c3f9c511811Angus Kong return (mMemoryUse >= SAVE_TASK_MEMORY_LIMIT); 83ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong } 84ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong 85ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong public void addImage(final byte[] data, String title, long date, Location loc, 860d00a8907096b9970ac64f52abbd2bfc1ed751b6Angus Kong int width, int height, int orientation, ExifInterface exif, 87ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong OnMediaSavedListener l, ContentResolver resolver) { 88ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong if (isQueueFull()) { 89ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong Log.e(TAG, "Cannot add image when the queue is full"); 90ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong return; 91ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong } 9283a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong ImageSaveTask t = new ImageSaveTask(data, title, date, 9383a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong (loc == null) ? null : new Location(loc), 940d00a8907096b9970ac64f52abbd2bfc1ed751b6Angus Kong width, height, orientation, exif, resolver, l); 95ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong 96c40c411683da5db1e393e2172a451c3f9c511811Angus Kong mMemoryUse += data.length; 97ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong if (isQueueFull()) { 98ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong onQueueFull(); 99ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong } 100ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong t.execute(); 101ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong } 102ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong 103c40c411683da5db1e393e2172a451c3f9c511811Angus Kong public void addImage(final byte[] data, String title, Location loc, 104c40c411683da5db1e393e2172a451c3f9c511811Angus Kong int width, int height, int orientation, ExifInterface exif, 105c40c411683da5db1e393e2172a451c3f9c511811Angus Kong OnMediaSavedListener l, ContentResolver resolver) { 106c40c411683da5db1e393e2172a451c3f9c511811Angus Kong addImage(data, title, System.currentTimeMillis(), loc, width, height, 107c40c411683da5db1e393e2172a451c3f9c511811Angus Kong orientation, exif, l, resolver); 108c40c411683da5db1e393e2172a451c3f9c511811Angus Kong } 109c40c411683da5db1e393e2172a451c3f9c511811Angus Kong 11083a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong public void addVideo(String path, long duration, ContentValues values, 11183a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong OnMediaSavedListener l, ContentResolver resolver) { 11283a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong // We don't set a queue limit for video saving because the file 11383a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong // is already in the storage. Only updating the database. 11483a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong new VideoSaveTask(path, duration, values, l, resolver).execute(); 11583a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong } 11683a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong 117ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong public void setListener(Listener l) { 118ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong mListener = l; 119ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong if (l == null) return; 120d6954f337e20365fc24ecffdd6f30e17c6b31effMichael Kolb l.onQueueStatus(isQueueFull()); 121ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong } 122ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong 123ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong private void onQueueFull() { 124d6954f337e20365fc24ecffdd6f30e17c6b31effMichael Kolb if (mListener != null) mListener.onQueueStatus(true); 125ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong } 126ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong 127ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong private void onQueueAvailable() { 128d6954f337e20365fc24ecffdd6f30e17c6b31effMichael Kolb if (mListener != null) mListener.onQueueStatus(false); 129ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong } 130ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong 13183a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong private class ImageSaveTask extends AsyncTask <Void, Void, Uri> { 132ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong private byte[] data; 133ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong private String title; 134ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong private long date; 135ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong private Location loc; 136ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong private int width, height; 137ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong private int orientation; 1380d00a8907096b9970ac64f52abbd2bfc1ed751b6Angus Kong private ExifInterface exif; 139ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong private ContentResolver resolver; 140ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong private OnMediaSavedListener listener; 141ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong 14283a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong public ImageSaveTask(byte[] data, String title, long date, Location loc, 14383a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong int width, int height, int orientation, ExifInterface exif, 14483a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong ContentResolver resolver, OnMediaSavedListener listener) { 145ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong this.data = data; 146ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong this.title = title; 147ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong this.date = date; 148ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong this.loc = loc; 149ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong this.width = width; 150ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong this.height = height; 151ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong this.orientation = orientation; 1520d00a8907096b9970ac64f52abbd2bfc1ed751b6Angus Kong this.exif = exif; 153ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong this.resolver = resolver; 154ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong this.listener = listener; 155ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong } 156ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong 157ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong @Override 158ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong protected void onPreExecute() { 159ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong // do nothing. 160ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong } 161ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong 162ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong @Override 163ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong protected Uri doInBackground(Void... v) { 164ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong return Storage.addImage( 1650d00a8907096b9970ac64f52abbd2bfc1ed751b6Angus Kong resolver, title, date, loc, orientation, exif, data, width, height); 166ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong } 167ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong 168ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong @Override 169ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong protected void onPostExecute(Uri uri) { 17083a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong if (listener != null) listener.onMediaSaved(uri); 171c40c411683da5db1e393e2172a451c3f9c511811Angus Kong boolean previouslyFull = isQueueFull(); 172c40c411683da5db1e393e2172a451c3f9c511811Angus Kong mMemoryUse -= data.length; 173c40c411683da5db1e393e2172a451c3f9c511811Angus Kong if (isQueueFull() != previouslyFull) onQueueAvailable(); 174ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong } 175ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong } 17683a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong 17783a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong private class VideoSaveTask extends AsyncTask <Void, Void, Uri> { 17883a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong private String path; 17983a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong private long duration; 18083a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong private ContentValues values; 18183a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong private OnMediaSavedListener listener; 18283a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong private ContentResolver resolver; 18383a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong 18483a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong public VideoSaveTask(String path, long duration, ContentValues values, 18583a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong OnMediaSavedListener l, ContentResolver r) { 18683a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong this.path = path; 18783a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong this.duration = duration; 18883a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong this.values = new ContentValues(values); 18983a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong this.listener = l; 19083a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong this.resolver = r; 19183a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong } 19283a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong 19383a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong @Override 19483a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong protected void onPreExecute() { 19583a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong // do nothing. 19683a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong } 19783a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong 19883a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong @Override 19983a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong protected Uri doInBackground(Void... v) { 20083a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong values.put(Video.Media.SIZE, new File(path).length()); 20183a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong values.put(Video.Media.DURATION, duration); 20283a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong Uri uri = null; 20383a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong try { 20483a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong Uri videoTable = Uri.parse("content://media/external/video/media"); 20583a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong uri = resolver.insert(videoTable, values); 20683a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong 20783a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong // Rename the video file to the final name. This avoids other 20883a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong // apps reading incomplete data. We need to do it after we are 20983a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong // certain that the previous insert to MediaProvider is completed. 21083a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong String finalName = values.getAsString( 21183a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong Video.Media.DATA); 21283a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong if (new File(path).renameTo(new File(finalName))) { 21383a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong path = finalName; 21483a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong } 21583a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong 21683a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong resolver.update(uri, values, null, null); 21783a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong } catch (Exception e) { 21883a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong // We failed to insert into the database. This can happen if 21983a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong // the SD card is unmounted. 22083a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong Log.e(TAG, "failed to add video to media store", e); 22183a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong uri = null; 22283a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong } finally { 22383a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong Log.v(TAG, "Current video URI: " + uri); 22483a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong } 22583a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong return uri; 22683a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong } 22783a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong 22883a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong @Override 22983a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong protected void onPostExecute(Uri uri) { 23083a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong if (listener != null) listener.onMediaSaved(uri); 23183a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong } 23283a99ae51a11af28553dfb77ef0ec91148671c9bAngus Kong } 233ce5480e099fda944b9e96e4b750300944c3f4a4fAngus Kong} 234