blob: 3a4998ebbb0903b7569cc449ba92548407074c14 [file] [log] [blame]
// Copyright 2010 Google Inc. All Rights Reserved.
package com.android.gallery3d.data;
import android.content.ContentResolver;
import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import java.util.HashMap;
import java.util.PriorityQueue;
public class ImageService {
private static final String TAG = "ImageService";
private static final int DECODE_TIMEOUT = 0;
private static final int INITIAL_TIMEOUT = 2000;
private static final int MAXIMAL_TIMEOUT = 32000;
private final HashMap<Integer, DecodeTask> mMap =
new HashMap<Integer, DecodeTask>();
private final PriorityQueue<DecodeTask> mQueue = new PriorityQueue<DecodeTask>();
private final Handler mHandler;
private final ContentResolver mContentResolver;
private boolean mActive = true;
private int mTimeSerial;
private DecodeTask mCurrentTask;
private final DecodeThread mDecodeThread = new DecodeThread();
public ImageService(ContentResolver contentResolver) {
mContentResolver = contentResolver;
mHandler = new Handler() {
@Override
public void handleMessage(Message m) {
if (m.what != DECODE_TIMEOUT) return;
DecodeTask task = mCurrentTask;
if (task != null) {
task.mItem.cancelImageGeneration(mContentResolver, task.mType);
}
}
};
mDecodeThread.start();
}
protected int requestImage(AbstractMediaItem item, int type) {
DecodeTask task = new DecodeTask();
task.mRequestId = ++mTimeSerial;
task.mItem = item;
task.mType = type;
task.mTimeout = INITIAL_TIMEOUT;
synchronized (mQueue) {
mMap.put(task.mRequestId, task);
mQueue.add(task);
if (mQueue.size() == 1) mQueue.notifyAll();
}
return task.mRequestId;
}
protected void cancelRequest(int requestId) {
synchronized (mQueue) {
DecodeTask task = mMap.remove(requestId);
if (task == null) return;
task.mCanceled = true;
if (mQueue.remove(task)) {
task.mItem.onImageCanceled(task.mType);
} else {
task.mItem.cancelImageGeneration(mContentResolver, task.mType);
}
}
}
public void close() {
synchronized (mQueue) {
mActive = false;
mQueue.notifyAll();
}
}
protected DecodeTask nextDecodeTask() {
PriorityQueue<DecodeTask> queue = mQueue;
synchronized (queue) {
try {
while (queue.isEmpty() && mActive) {
queue.wait();
}
} catch (InterruptedException e) {
Log.v(TAG, "decode-thread is interrupted");
Thread.currentThread().interrupt();
return null;
}
return !mActive ? null : queue.remove() ;
}
}
private class DecodeThread extends Thread {
@Override
public void run() {
PriorityQueue<DecodeTask> queue = mQueue;
ContentResolver resolver = mContentResolver;
while (true) {
DecodeTask task = nextDecodeTask();
if (task == null) break;
AbstractMediaItem item = task.mItem;
try {
mCurrentTask = task;
mHandler.sendEmptyMessageDelayed(
DECODE_TIMEOUT, task.mTimeout);
Bitmap bitmap = task.mCanceled
? null
: item.generateImage(resolver, task.mType);
mHandler.removeMessages(DECODE_TIMEOUT);
mCurrentTask = null;
if (bitmap != null) {
task.mItem.onImageReady(task.mType, bitmap);
synchronized (mQueue) {
mMap.remove(task.mRequestId);
}
} else if (task.mCanceled) {
task.mItem.onImageCanceled(task.mType);
} else {
// Try to decode the image again by increasing the
// timeout by a factor of 2 unless MAXIMAL_TIMEOUT
// is reached.
task.mTimeout <<= 1;
if (task.mTimeout > MAXIMAL_TIMEOUT) {
throw new RuntimeException("decode timeout");
} else {
synchronized (mQueue) {
mQueue.add(task);
}
}
}
} catch (Exception e) {
Log.e(TAG, "decode error", e);
task.mItem.onImageError(task.mType, e);
synchronized (mQueue) {
mMap.remove(task.mRequestId);
}
}
}
synchronized (mQueue) {
for (DecodeTask task : mQueue) {
task.mItem.onImageCanceled(task.mType);
}
mQueue.clear();
mMap.clear();
}
}
}
private static class DecodeTask implements Comparable<DecodeTask> {
int mRequestId;
int mTimeout;
int mType;
volatile boolean mCanceled;
AbstractMediaItem mItem;
public int compareTo(DecodeTask task) {
return mTimeout != task.mTimeout
? mTimeout - task.mTimeout
: mRequestId - task.mRequestId;
}
}
}