package edu.vanderbilt.vm.guide.util;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.http.AndroidHttpClient;
import android.os.AsyncTask;
import android.widget.ImageView;
/**
* Handles asynchronous image downloading. Most of this code comes from
* http://android
* -developers.blogspot.com/2010/07/multithreading-for-performance.html
*/
public class ImageDownloader {
private static final Logger logger = LoggerFactory.getLogger("util.ImageDownloader");
/**
* Download an image at the given url and show it in the imageView
* asynchronously. This method spawns an AsyncTask. Only call this method if
* you don't need to potentially cancel the AsyncTask. Note that not
* cancelling the AsyncTask could be very bad since AsyncTask has a static
* thread pool of only 10 threads, so if the threads aren't terminated in a
* timely fashion, no more AsyncTasks can be executed.
*
* @param url The url to download the image from
* @param imageView The ImageView to update
*/
public static void download(String url, ImageView imageView) {
BitmapDownloaderTask task = new BitmapDownloaderTask(imageView);
task.execute(url);
}
public static class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
@SuppressWarnings("unused")
private String url;
private final WeakReference<ImageView> imageViewReference;
private Runnable mOnFinishDownload;
public BitmapDownloaderTask(ImageView imageView) {
imageViewReference = new WeakReference<ImageView>(imageView);
mOnFinishDownload = null;
}
public void setRunOnFinishDownload(Runnable run) {
mOnFinishDownload = run;
}
@Override
// Actual download method, run in the task thread
protected Bitmap doInBackground(String... params) {
// params comes from the execute() call: params[0] is the url.
return downloadBitmap(params[0]);
}
@Override
// Once the image is downloaded, associates it to the imageView
protected void onPostExecute(Bitmap bitmap) {
if (isCancelled()) {
bitmap = null;
}
if (imageViewReference != null) {
ImageView imageView = imageViewReference.get();
if (imageView != null && bitmap != null) {
imageView.setImageBitmap(bitmap);
}
}
if (mOnFinishDownload != null) {
mOnFinishDownload.run();
}
}
}
static Bitmap downloadBitmap(String url) {
logger.debug("Attempting to download image at URL {}", url);
final AndroidHttpClient client = AndroidHttpClient.newInstance("Android");
final HttpGet getRequest = new HttpGet(url);
try {
HttpResponse response = client.execute(getRequest);
logger.trace("Finished download attempt");
final int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
logger.warn("Error {} while retrieving bitmap from {}", statusCode, url);
return null;
}
final HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream inputStream = null;
try {
inputStream = entity.getContent();
// Bitmap decoding
final Bitmap bitmap = BitmapFactory.decodeStream(
new FlushedInputStream(inputStream));
logger.trace("Returning a bitmap");
return bitmap;
} finally {
if (inputStream != null) {
inputStream.close();
}
entity.consumeContent();
}
}
} catch (Exception e) {
// Could provide a more explicit error message for IOException or
// IllegalStateException
getRequest.abort();
logger.warn("Error while retrieving bitmap from {}", url, e);
} finally {
if (client != null) {
client.close();
}
}
logger.trace("Returning null");
return null;
}
static class FlushedInputStream extends FilterInputStream {
public FlushedInputStream(InputStream inputStream) {
super(inputStream);
}
@Override
public long skip(long n) throws IOException {
long totalBytesSkipped = 0L;
while (totalBytesSkipped < n) {
long bytesSkipped = in.skip(n - totalBytesSkipped);
if (bytesSkipped == 0L) {
int bite = read();
if (bite < 0) {
break; // we reached EOF
} else {
bytesSkipped = 1; // we read one byte
}
}
totalBytesSkipped += bytesSkipped;
}
return totalBytesSkipped;
}
}
}