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.content.ContentResolver;
     20 import android.location.Location;
     21 import android.net.Uri;
     22 import android.util.Log;
     23 
     24 import java.util.ArrayList;
     25 
     26 // We use a queue to store the SaveRequests that have not been completed
     27 // yet. The main thread puts the request into the queue. The saver thread
     28 // gets it from the queue, does the work, and removes it from the queue.
     29 //
     30 // The main thread needs to wait for the saver thread to finish all the work
     31 // in the queue, when the activity's onPause() is called, we need to finish
     32 // all the work, so other programs (like Gallery) can see all the images.
     33 //
     34 // If the queue becomes too long, adding a new request will block the main
     35 // thread until the queue length drops below the threshold (QUEUE_LIMIT).
     36 // If we don't do this, we may face several problems: (1) We may OOM
     37 // because we are holding all the jpeg data in memory. (2) We may ANR
     38 // when we need to wait for saver thread finishing all the work (in
     39 // onPause() or gotoGallery()) because the time to finishing a long queue
     40 // of work may be too long.
     41 class MediaSaver extends Thread {
     42     private static final int SAVE_QUEUE_LIMIT = 3;
     43     private static final String TAG = "MediaSaver";
     44 
     45     private ArrayList<SaveRequest> mQueue;
     46     private boolean mStop;
     47     private ContentResolver mContentResolver;
     48 
     49     public interface OnMediaSavedListener {
     50         public void onMediaSaved(Uri uri);
     51     }
     52 
     53     public MediaSaver(ContentResolver resolver) {
     54         mContentResolver = resolver;
     55         mQueue = new ArrayList<SaveRequest>();
     56         start();
     57     }
     58 
     59     // Runs in main thread
     60     public synchronized boolean queueFull() {
     61         return (mQueue.size() >= SAVE_QUEUE_LIMIT);
     62     }
     63 
     64     // Runs in main thread
     65     public void addImage(final byte[] data, String title, long date, Location loc,
     66                          int width, int height, int orientation, OnMediaSavedListener l) {
     67         SaveRequest r = new SaveRequest();
     68         r.data = data;
     69         r.date = date;
     70         r.title = title;
     71         r.loc = (loc == null) ? null : new Location(loc);  // make a copy
     72         r.width = width;
     73         r.height = height;
     74         r.orientation = orientation;
     75         r.listener = l;
     76         synchronized (this) {
     77             while (mQueue.size() >= SAVE_QUEUE_LIMIT) {
     78                 try {
     79                     wait();
     80                 } catch (InterruptedException ex) {
     81                     // ignore.
     82                 }
     83             }
     84             mQueue.add(r);
     85             notifyAll();  // Tell saver thread there is new work to do.
     86         }
     87     }
     88 
     89     // Runs in saver thread
     90     @Override
     91     public void run() {
     92         while (true) {
     93             SaveRequest r;
     94             synchronized (this) {
     95                 if (mQueue.isEmpty()) {
     96                     notifyAll();  // notify main thread in waitDone
     97 
     98                     // Note that we can only stop after we saved all images
     99                     // in the queue.
    100                     if (mStop) break;
    101 
    102                     try {
    103                         wait();
    104                     } catch (InterruptedException ex) {
    105                         // ignore.
    106                     }
    107                     continue;
    108                 }
    109                 if (mStop) break;
    110                 r = mQueue.remove(0);
    111                 notifyAll();  // the main thread may wait in addImage
    112             }
    113             Uri uri = storeImage(r.data, r.title, r.date, r.loc, r.width, r.height,
    114                     r.orientation);
    115             r.listener.onMediaSaved(uri);
    116         }
    117         if (!mQueue.isEmpty()) {
    118             Log.e(TAG, "Media saver thread stopped with " + mQueue.size() + " images unsaved");
    119             mQueue.clear();
    120         }
    121     }
    122 
    123     // Runs in main thread
    124     public void finish() {
    125         synchronized (this) {
    126             mStop = true;
    127             notifyAll();
    128         }
    129     }
    130 
    131     // Runs in saver thread
    132     private Uri storeImage(final byte[] data, String title, long date,
    133                            Location loc, int width, int height, int orientation) {
    134         Uri uri = Storage.addImage(mContentResolver, title, date, loc,
    135                                    orientation, data, width, height);
    136         return uri;
    137     }
    138 
    139     // Each SaveRequest remembers the data needed to save an image.
    140     private static class SaveRequest {
    141         byte[] data;
    142         String title;
    143         long date;
    144         Location loc;
    145         int width, height;
    146         int orientation;
    147         OnMediaSavedListener listener;
    148     }
    149 }
    150