/* * 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 { private static final int SAVE_TASK_LIMIT = 3; private static final String TAG = MediaSaveService.class.getSimpleName(); private final IBinder mBinder = new LocalBinder(); private int mTaskNumber; private Listener mListener; 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() { mTaskNumber = 0; } public boolean isQueueFull() { return (mTaskNumber >= SAVE_TASK_LIMIT); } // Runs in main thread 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); mTaskNumber++; if (isQueueFull()) { onQueueFull(); } 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; 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 { 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); mTaskNumber--; if (mTaskNumber == SAVE_TASK_LIMIT - 1) onQueueAvailable(); } } private class VideoSaveTask extends AsyncTask { 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); } } }