package edu.vanderbilt.cs282.feisele;
import java.net.MalformedURLException;
import java.net.URL;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.text.Editable;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import edu.vanderbilt.cs282.feisele.ThreadedDownloadFragment.OnDownloadFaultHandler;
/**
*
* An activity which prompts the user for an image to download. <h2>Program
* Description</h2>
* <p>
* The purpose of this example is to demonstrate different ways of performing
* background tasks. The purpose of this assignment is to give you experience
* using various Android concurrency models to download bitmap images from a web
* server. The application works as follows:
* <ol>
* <li>The Activity provides a menu of buttons and displays a default image</li>
* <li>The user is prompted to enter the URL for a new bitmap image.</li>
* <li>After entering the desired URL, the user can select one of several
* buttons that provide different ways to download the image concurrently.</li>
* <li>Upon making the selection, a progress dialog is displayed when
* downloading the designated image.</li>
* <li>After the URL download has completed it will be displayed in an
* ImageView.</li>
* <li>The user can reset the image to its default contents by clicking the
* "Reset Image" button.</li>
* <p>
* note: the default image is configured via an XML resource file and the
* default image itself is part of the project's assets.
*
* <h2>Fault Handling</h2>
* If there is a problem in the entered URL a toast is displayed indicating the
* problem.
*
* <h2>Details</h2>
* It uses four Button objects with the label "Run Runnable", "Run Messages",
* "Run Async", and "Reset Image" to run the corresponding hook methods that use
* the URL provided by the user to download the designated bitmap file via one
* of the following three Android concurrency models:
* <dl>
* <dt>Run Runnables</dt>
* <dd><b>Runnables and Handlers</b> model: faults are handled by posting a
* runnable to the handler</dd>
* <dt>Run Messages</dt>
* <dd><b>Messages and Handlers</b> model: faults are handled by sending a
* message to the message handler</dd>
* <dt>Run Async</dt>
* <dd><b>AsyncTask</b> model: faults are handled by running them on the UI
* thread</dd>
* </dl>
* The Button objects that download the bitmap file are connected to the
* corresponding <code>ThreadedDownloadActivity.run*()</code> methods via the
* appropriate <code>android:onClick="..."</code> attributes.
* <p>
*
* @author Fred Eisele <phreed@gmail.com>
*
*/
public class ThreadedDownloadActivity extends LifecycleLoggingActivity
implements OnDownloadFaultHandler {
static private final String TAG = "Threaded Download Activity";
private EditText urlEditText = null;
private ThreadedDownloadFragment imageFragment = null;
/**
* The fragment is used to preserve state across various changes.
* <dl>
* <dt>orientation</dt>
* <dt>startActivity</dt>
* <dd>configuration change doesn't handle properly</dt>
* <dt>keyboard</dt>
* </dl>
* <p>
*
* @param savedInstanceState
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.threaded_download);
this.urlEditText = (EditText) findViewById(R.id.edit_image_url);
final FragmentManager fm = this.getSupportFragmentManager();
final Fragment fobj = fm.findFragmentById(R.id.fragment_container);
if (fobj == null) {
final FragmentTransaction txn = fm.beginTransaction();
this.imageFragment = new ThreadedDownloadFragment();
txn.add(R.id.fragment_container, this.imageFragment);
txn.commit();
} else {
this.imageFragment = (ThreadedDownloadFragment) fobj;
}
}
@Override
public void onResume() {
super.onResume();
}
@Override
public void onPause() {
super.onPause();
}
/**
* Extract the url from the edit text widget. Check that the string in the
* widget is a proper url. If the field is empty then use the value provided
* as the hint. If the field is invalid
* <ul>
* <li>return a null indicating that the action should not be performed.</li>
* <li>generate a toast informing the operator of his error</li>
* <li>mark the field as having an error</li>
* </ul>
*
* @return
*/
private URL getValidUrlFromWidget() {
final Editable urlEditable = this.urlEditText.getText();
if (urlEditable.length() < 1) {
final String urlStr = this.urlEditText.getHint().toString();
try {
return new URL(urlStr);
} catch (MalformedURLException e) {
Log.e(TAG, "hard coded, should never happen");
return null;
}
}
final String urlStr = urlEditable.toString();
try {
return new URL(urlStr);
} catch (MalformedURLException ex) {
Log.i(TAG, "malformed url " + urlStr);
}
final CharSequence errorMsg = this.getResources().getText(
R.string.error_malformed_url);
this.urlEditText.setError(errorMsg);
Toast.makeText(this, errorMsg, Toast.LENGTH_LONG).show();
return null;
}
/**
* Load the default image from the assets.
*
* @param view
*/
public void resetImage(View view) {
if (this.imageFragment == null)
return;
this.imageFragment.resetImage(view);
}
/**
* Each of the following actions will make use of a background thread.
*
* @param view
*/
public void runAsyncTask(View view) {
if (this.imageFragment == null)
return;
final URL url = getValidUrlFromWidget();
this.imageFragment.runAsyncTask(view, url);
}
public void runMessages(View view) {
if (this.imageFragment == null)
return;
final URL url = getValidUrlFromWidget();
this.imageFragment.runMessages(view, url);
}
public void runRunnable(View view) {
if (this.imageFragment == null)
return;
final URL url = getValidUrlFromWidget();
this.imageFragment.runRunnable(view, url);
}
/**
* Should any of the downloads fail produce a warning indicator on the url
* field. As this is most likely called from the background thread
* performing the download the field update is forced to the ui thread.
*/
public void onFault(final CharSequence msg) {
this.runOnUiThread(new Runnable() {
final CharSequence msg_ = msg;
final ThreadedDownloadActivity master = ThreadedDownloadActivity.this;
public void run() {
Toast.makeText(master, msg, Toast.LENGTH_LONG).show();
final Drawable dr = master.getResources().getDrawable(
R.drawable.indicator_input_warn);
dr.setBounds(0, 0, dr.getIntrinsicWidth(),
dr.getIntrinsicHeight());
master.urlEditText.setError(msg_, dr);
}
});
}
}