Home | History | Annotate | Download | only in camera
      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 }
    226