blob: a3d582e1c964f428925f75973d40b00aa6adb062 [file] [log] [blame]
/*
* 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.content.ContentResolver;
import android.location.Location;
import android.net.Uri;
import android.util.Log;
import java.util.ArrayList;
// We use a queue to store the SaveRequests that have not been completed
// yet. The main thread puts the request into the queue. The saver thread
// gets it from the queue, does the work, and removes it from the queue.
//
// The main thread needs to wait for the saver thread to finish all the work
// in the queue, when the activity's onPause() is called, we need to finish
// all the work, so other programs (like Gallery) can see all the images.
//
// If the queue becomes too long, adding a new request will block the main
// thread until the queue length drops below the threshold (QUEUE_LIMIT).
// If we don't do this, we may face several problems: (1) We may OOM
// because we are holding all the jpeg data in memory. (2) We may ANR
// when we need to wait for saver thread finishing all the work (in
// onPause() or gotoGallery()) because the time to finishing a long queue
// of work may be too long.
class MediaSaver extends Thread {
private static final int SAVE_QUEUE_LIMIT = 3;
private static final String TAG = "MediaSaver";
private ArrayList<SaveRequest> mQueue;
private boolean mStop;
private ContentResolver mContentResolver;
public interface OnMediaSavedListener {
public void onMediaSaved(Uri uri);
}
public MediaSaver(ContentResolver resolver) {
mContentResolver = resolver;
mQueue = new ArrayList<SaveRequest>();
start();
}
// Runs in main thread
public synchronized boolean queueFull() {
return (mQueue.size() >= SAVE_QUEUE_LIMIT);
}
// Runs in main thread
public void addImage(final byte[] data, String title, long date, Location loc,
int width, int height, int orientation, OnMediaSavedListener l) {
SaveRequest r = new SaveRequest();
r.data = data;
r.date = date;
r.title = title;
r.loc = (loc == null) ? null : new Location(loc); // make a copy
r.width = width;
r.height = height;
r.orientation = orientation;
r.listener = l;
synchronized (this) {
while (mQueue.size() >= SAVE_QUEUE_LIMIT) {
try {
wait();
} catch (InterruptedException ex) {
// ignore.
}
}
mQueue.add(r);
notifyAll(); // Tell saver thread there is new work to do.
}
}
// Runs in saver thread
@Override
public void run() {
while (true) {
SaveRequest r;
synchronized (this) {
if (mQueue.isEmpty()) {
notifyAll(); // notify main thread in waitDone
// Note that we can only stop after we saved all images
// in the queue.
if (mStop) break;
try {
wait();
} catch (InterruptedException ex) {
// ignore.
}
continue;
}
if (mStop) break;
r = mQueue.remove(0);
notifyAll(); // the main thread may wait in addImage
}
Uri uri = storeImage(r.data, r.title, r.date, r.loc, r.width, r.height,
r.orientation);
r.listener.onMediaSaved(uri);
}
if (!mQueue.isEmpty()) {
Log.e(TAG, "Media saver thread stopped with " + mQueue.size() + " images unsaved");
mQueue.clear();
}
}
// Runs in main thread
public void finish() {
synchronized (this) {
mStop = true;
notifyAll();
}
}
// Runs in saver thread
private Uri storeImage(final byte[] data, String title, long date,
Location loc, int width, int height, int orientation) {
Uri uri = Storage.addImage(mContentResolver, title, date, loc,
orientation, data, width, height);
return uri;
}
// Each SaveRequest remembers the data needed to save an image.
private static class SaveRequest {
byte[] data;
String title;
long date;
Location loc;
int width, height;
int orientation;
OnMediaSavedListener listener;
}
}