package edu.vanderbilt.cs282.feisele;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import android.app.ProgressDialog;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.Toast;
public class ThreadedDownloadFragment extends LifecycleLoggingFragment {
static private final String TAG = "Threaded Download Fragment";
/** my oldest daughter */
static private final String DEFAULT_PORT_IMAGE = "raquel_eisele_port_2012.jpg";
static private final String DEFAULT_LAND_IMAGE = "raquel_eisele_land_2012.jpg";
private Bitmap bitmap = null;
private ImageView bitmapImage = null;
private static Handler msgHandler = null;
private ProgressDialog progress;
public interface OnDownloadFaultHandler {
public void onFault(CharSequence msg);
}
/**
*
* @param savedInstanceState
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ThreadedDownloadFragment.msgHandler = initMsgHandler(this);
}
/**
* The bitmap field serves double duty. It serves to hold the downloaded
* bitmap image and, when null, it acts as a flag to indicate that the
* default image should be used.
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState, false);
this.setRetainInstance(true);
final View result = inflater.inflate(R.layout.downloaded_image,
container, false);
this.bitmapImage = (ImageView) result.findViewById(R.id.current_image);
synchronized (this) {
if (this.bitmap == null) {
this.resetImage(null);
} else {
this.bitmapImage.setImageBitmap(this.bitmap);
}
}
return result;
}
/**
* Load the default image from the assets. Just for fun a different asset is
* loaded depending on the screen orientation.
*
* @param view
*/
public void resetImage(View view) {
final AssetManager am = this.getActivity().getAssets();
final InputStream is;
try {
switch (this.getResources().getConfiguration().orientation) {
case Configuration.ORIENTATION_LANDSCAPE:
is = am.open(DEFAULT_LAND_IMAGE);
break;
default:
is = am.open(DEFAULT_PORT_IMAGE);
}
} catch (IOException ex) {
Toast.makeText(this.getActivity(),
R.string.error_opening_default_image, Toast.LENGTH_LONG)
.show();
return;
}
try {
synchronized (this) {
this.bitmap = null;
final Bitmap bitmap = BitmapFactory.decodeStream(is);
this.bitmapImage.setImageBitmap(bitmap);
}
} finally {
try {
is.close();
} catch (IOException ex) {
Log.e(TAG, "cannot load a bitmap asset");
}
}
}
/**
* The workhorse for the class. Download the provided image url. If there is
* a problem an exception is raised and the calling method is expected to
* handle it in an appropriate manner.
*
* @param view
* the button view object (unused)
*/
private Bitmap downloadBitmap(URL url) throws FailedDownload {
try {
final InputStream is = url.openConnection().getInputStream();
return BitmapFactory.decodeStream(is);
} catch (IOException e) {
throw new FailedDownload(this.getResources().getText(
R.string.error_downloading_url));
}
}
/**
* An exception class used when there is a problem with the download.
*/
/* package */static class FailedDownload extends Exception {
private static final long serialVersionUID = 6673968049922918951L;
final public CharSequence msg;
public FailedDownload(CharSequence msg) {
super();
this.msg = msg;
}
}
/**
* Initialize and configure the progress dialog.
*
*/
private void startProgress(CharSequence msg) {
Log.d(TAG, "startProgress");
this.progress = new ProgressDialog(this.getActivity());
this.progress.setTitle(R.string.dialog_progress_title);
this.progress.setMessage(msg);
this.progress.setProgressStyle(ProgressDialog.STYLE_SPINNER);
this.progress.setProgress(0);
this.progress.show();
}
/**
* A new bitmap image has been generated. Update the
*
* @param result
*/
private void setBitmap(Bitmap result) {
try {
synchronized (this) {
this.bitmap = result;
if (this.bitmap != null) {
this.bitmapImage.setImageBitmap(this.bitmap);
}
}
if (this.progress.isShowing()) {
this.progress.dismiss();
}
} catch (IllegalArgumentException ex) {
Log.e(TAG, "can not set bitmap image");
}
}
/**
* Report problems with downloading the image back to the parent activity.
*
* @param errorMsg
*/
private void reportDownloadFault(CharSequence errorMsg) {
final FragmentActivity parent = this.getActivity();
if (parent instanceof OnDownloadFaultHandler) {
((OnDownloadFaultHandler) parent).onFault(errorMsg);
}
this.progress.cancel();
}
/**
* The async task will be stopped if the enclosing activity is stopped. In
* general the intent service should be used for downloading from the
* internet.
*
* @param view
*/
public void runAsyncTask(View view, URL url) {
Log.d(TAG, "runAsyncTask");
if (url == null)
return;
(new AsyncTask<URL, Void, Bitmap>() {
private final ThreadedDownloadFragment master = ThreadedDownloadFragment.this;
@Override
protected Bitmap doInBackground(URL... params) {
if (params.length < 1)
return null;
final URL url = params[0];
try {
return master.downloadBitmap(url);
} catch (FailedDownload ex) {
master.reportDownloadFault(ex.msg);
}
return null;
}
/**
* Set the downloaded image.
*/
@Override
protected void onPostExecute(Bitmap result) {
master.setBitmap(result);
}
@Override
protected void onPreExecute() {
master.startProgress(master.getResources().getText(
R.string.message_progress_async_task));
}
}).execute(url);
}
/**
* Start a new thread to perform the download. Once the download is
* completed the UI thread is notified via a post to on the handler.
*
* @param view
*/
public void runRunnable(View view, final URL url) {
Log.d(TAG, "runRunnable");
if (url == null)
return;
this.startProgress(this.getResources().getText(
R.string.message_progress_run_runnable));
(new Thread(null, new Runnable() {
private final ThreadedDownloadFragment master = ThreadedDownloadFragment.this;
final URL url_ = url;
public void run() {
final Bitmap bitmap;
try {
bitmap = master.downloadBitmap(url_);
} catch (FailedDownload ex) {
master.reportDownloadFault(ex.msg);
return;
}
master.bitmapImage.post(new Runnable() {
public void run() {
master.setBitmap(bitmap);
}
});
}
})).start();
}
/**
* The valid message types for the handler.
*/
protected static enum DownloadState {
/**
* indicate that the download is in progress and the progress spinner
* should be displayed
*/
SET_PROGRESS_VISIBILITY,
/**
* indicate that the download is complete and so the bitmap should be
* displayed
*/
SET_BITMAP,
/**
* indicate that the download has failed and so the default image should
* be shown
*/
SET_ERROR;
static DownloadState[] lookup = DownloadState.values();
}
/**
* Initialized the handler used by the run message method.
*
* @return
*/
private static Handler initMsgHandler(final ThreadedDownloadFragment master) {
return new Handler() {
@Override
public void handleMessage(Message msg) {
switch (DownloadState.lookup[msg.what]) {
case SET_PROGRESS_VISIBILITY: {
master.startProgress(master.getResources().getText(
R.string.message_progress_run_messages));
}
break;
case SET_BITMAP: {
final Bitmap bitmap = (Bitmap) msg.obj;
master.setBitmap(bitmap);
}
break;
case SET_ERROR: {
master.reportDownloadFault((CharSequence) msg.obj);
break;
}
}
}
};
}
/**
* Make use of the message handler to keep the UI updated.
*
*/
public void runMessages(View view, final URL url) {
Log.d(TAG, "runMessages");
if (url == null)
return;
(new Thread(null, new Runnable() {
private final ThreadedDownloadFragment master = ThreadedDownloadFragment.this;
private final Handler handler = ThreadedDownloadFragment.msgHandler;
private final URL url_ = url;
public void run() {
final Message startMsg = handler.obtainMessage(
DownloadState.SET_PROGRESS_VISIBILITY.ordinal(),
ProgressDialog.STYLE_SPINNER);
handler.sendMessage(startMsg);
final Bitmap bitmap;
try {
bitmap = master.downloadBitmap(url_);
} catch (FailedDownload ex) {
final Message errorMsg = handler.obtainMessage(
DownloadState.SET_ERROR.ordinal(), ex.msg);
handler.sendMessage(errorMsg);
return;
}
final Message bitmapMsg = handler.obtainMessage(
DownloadState.SET_BITMAP.ordinal(), bitmap);
handler.sendMessage(bitmapMsg);
}
})).start();
}
}