diff --git a/.gitignore b/.gitignore
index 32d204506..36da55578 100644
--- a/.gitignore
+++ b/.gitignore
@@ -47,8 +47,8 @@ local.properties
#############################################################################
# Built application files
-*.apk
-*.ap_
+#*.apk
+#*.ap_
# Files for the Dalvik VM
*.dex
diff --git a/README.md b/README.md
index f991e94ef..de46cd17c 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,6 @@
POSA-14
=======
-This repository contains assignments and examples for the 2014 offering of the POSA MOOC (see www.coursera.org/course/posa for more information)
+This repository contains assignments and examples for the 2014
+offering of the POSA MOOC (see www.coursera.org/course/posa for more
+information).
diff --git a/ex/PingPong/console/.classpath b/assignments/week-1-assignment-0-v2/.classpath
similarity index 79%
rename from ex/PingPong/console/.classpath
rename to assignments/week-1-assignment-0-v2/.classpath
index 18d70f02c..b10b8d9b2 100644
--- a/ex/PingPong/console/.classpath
+++ b/assignments/week-1-assignment-0-v2/.classpath
@@ -2,5 +2,6 @@
+
diff --git a/assignments/week-1-assignment-0-v2/.project b/assignments/week-1-assignment-0-v2/.project
new file mode 100644
index 000000000..f4aa3946a
--- /dev/null
+++ b/assignments/week-1-assignment-0-v2/.project
@@ -0,0 +1,17 @@
+
+
+ W1-A0-SynchronizedQueueTestv2
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/assignments/week-1-assignment-0-v2/Assignment-Description.txt b/assignments/week-1-assignment-0-v2/Assignment-Description.txt
new file mode 100644
index 000000000..42d3583a3
--- /dev/null
+++ b/assignments/week-1-assignment-0-v2/Assignment-Description.txt
@@ -0,0 +1,98 @@
+Week 1: Programming Assignment 0 v2
+
+[Note, this is a different skeleton for assignment 0, which fixes some
+problems with the "official" version, provides better diagnostics, and
+also has a cleaner design that uses the Template Method pattern to
+decouple the test infrastructure from the student-supplied
+code. However, it is NOT the offical version that will be peer graded
+and is provided solely as a way for students to understand better how
+concurrency and Java Threads work.]
+
+In this (intentionally simple) initial assignment, you will use Java
+Threads to test several implementations of the Java BlockingQueue
+interface. The goal is to learn how to (1) create, (2) start, (3)
+interrupt, and (4) wait for the completion of multiple Java Threads.
+The test program also illustrates some problems that can occur if Java
+Threads concurrently access an object that isn't synchronized
+properly.
+
+In the "src/edu/vuum/mocca" folder in this directory you'll find the
+SynchronizedQueueImpl.java class, which contains the skeleton Java
+code that you'll implement by completing the "TODO - You fill in here"
+comments to provide a working solution. DO NOT CHANGE THE OVERALL
+STRUCTURE OF THE SKELETON - just fill in the "TODO - You fill in here"
+portions!!!
+
+In particular, you'll need to do the following:
+
+. Implement the "TODO" portions of the createThreads() and
+ startThreads() methods so that the two Java mProducer and mConsumer
+ Threads are created and started to run the mProducerRunnable and
+ mConsumerRunnable objects. Please keep the "TODO" comments in the
+ code so that peer reviewers can find them quickly during the
+ assessment phase.
+
+. Implement the "TODO" portion of the interruptThreads() method, which
+ interrupts both the mProducer and mConsumer Threads so they will
+ shutdown.
+
+. Implement the "TODO" portion of the "joinThreads()" method, which
+ waits for both Threads to exit before continuing with the test
+ (which is done for you by the testQueue() template method in the
+ SynchronizedQueue.java file).
+
+. If you'd like to enable verbose debugging output please set the
+ diagnosticsEnabled flag in SynchronizedQueueImpl.java to true (it
+ defaults to false).
+
+All the information needed to write this code is described in these
+videos:
+
+ Section 1: Module 2: Part 1: Overview of Java Threads (Part 1)
+ Section 1: Module 2: Part 2: Overview of Java Threads (Part 2)
+ Section 1: Module 2: Part 3: Motivating Java Synchronization and Scheduling Mechanisms
+
+Make sure to watch these videos and read all the supplied Java code
+carefully prior to starting the assignment. These videos are
+available at
+
+https://class.coursera.org/posa-002/lecture
+
+We'll also discuss this assignment specification (and later its
+solution) in the POSA MOOC "Virtual Office Hours", which are described
+in item #38 at the POSA MOOC FAQ available from
+
+http://www.coursera.org/course/posa
+
+The SynchronizerQueueTest.java file uses JUnit to run the tests. We
+do this to automate the testing process and leverage the integration
+of JUnit with Eclipse, as described here:
+
+http://www.vogella.com/tutorials/JUnit/article.html#eclipse
+
+When you first open the project in Eclipse, you might see compile
+errors if JUnit is not included in your build path. To fix these
+errors, open SynchronizedQueueTest.java, hover over "org.junit," and
+click "Fix project setup." Make sure "Add JUnit 4 library to the
+build path" is selected and then click "OK." At this point, the
+compile errors should disappear!
+
+To run the JUnit tests in Eclipse, right-click on
+SynchronizedQueueTest.java and go to "Run As > JUnit Test". The JUnit
+view will pop up in Eclipse and run the tests contained therein. All
+tests should pass. The ArrayBlockingQueue will pass because your
+testQueue method runs successfully. The tests for BuggyBlockingQueue
+(which is an intentionally flawed class), should "pass" if some error
+occurs while running testQueue (these errors are expected). If a test
+passes a green-check mark will appear next to the test in the JUnit
+view. As long as this JUnit test "passes" successfully your program
+will be be consider "correct" for the purposes of assessing this
+assignment.
+
+If the tests run and "pass," you should see check-marks next to each
+of the tests in the JUnit view. If you set the diagnosticsEnabled
+flag to true then debugging output will be printed to the console as
+the tests run. This debugging output is for informational purposes
+only and have no bearing on whether your program is "correct." If the
+tests run and "pass," you should see a green check-mark next to the
+test in the JUnit view, which is all that matters.
diff --git a/assignments/week-1-assignment-0/src/edu/vuum/mooca/BuggyBlockingQueue.java b/assignments/week-1-assignment-0-v2/src/edu/vuum/mooca/BuggyBlockingQueue.java
similarity index 96%
rename from assignments/week-1-assignment-0/src/edu/vuum/mooca/BuggyBlockingQueue.java
rename to assignments/week-1-assignment-0-v2/src/edu/vuum/mooca/BuggyBlockingQueue.java
index be2ea68f2..2cfebfcc6 100644
--- a/assignments/week-1-assignment-0/src/edu/vuum/mooca/BuggyBlockingQueue.java
+++ b/assignments/week-1-assignment-0-v2/src/edu/vuum/mooca/BuggyBlockingQueue.java
@@ -27,6 +27,13 @@ public BuggyBlockingQueue(int initialSize) {
mList = new ArrayList(initialSize);
}
+ /**
+ * Returns the number of elements in this queue.
+ */
+ public int size() {
+ return mList.size();
+ }
+
/**
* Insert msg at the tail of the queue, but doesn't block if the
* queue is full.
@@ -119,7 +126,4 @@ public Iterator iterator() {
public boolean isEmpty() {
return false;
}
- public int size() {
- return 0;
- }
}
diff --git a/assignments/week-1-assignment-0-v2/src/edu/vuum/mooca/SynchronizedQueue.java b/assignments/week-1-assignment-0-v2/src/edu/vuum/mooca/SynchronizedQueue.java
new file mode 100644
index 000000000..bd6a10c9f
--- /dev/null
+++ b/assignments/week-1-assignment-0-v2/src/edu/vuum/mooca/SynchronizedQueue.java
@@ -0,0 +1,348 @@
+package edu.vuum.mooca;
+import java.util.concurrent.*;
+
+/**
+ * @class SynchronizedQueue
+ *
+ * @brief This class tests the use of Java Threads and several
+ * implementations of the Java BlockingQueue interface. It
+ * plays the role of the Abstract Class in the Template Method
+ * pattern.
+ */
+public abstract class SynchronizedQueue {
+ /**
+ * Keep track of the number of times the producer test iterates.
+ */
+ static volatile int mProducerCounter = 0;
+
+ /**
+ * Keep track of the number of times the consumer test iterates.
+ */
+ static volatile int mConsumerCounter = 0;
+
+ /**
+ * Maximum timeout.
+ */
+ static final int TIMEOUT_SECONDS = 5;
+
+ /**
+ * Error value for a timeout.
+ */
+ static final int TIMEOUT_OCCURRED = -1;
+
+ /**
+ * Error value for a failure.
+ */
+ static final int FAILURE_OCCURRED = -2;
+
+ /**
+ * @class SynchronizedQueueResult
+ *
+ * @brief Enumerated type for return values of testing logic, has
+ * String for easy output.
+ */
+ public enum SynchronizedQueueResult {
+ RAN_PROPERLY("Threads Ran Properly."),
+ JOIN_NEVER_CALLED("Join() never called."),
+ THREADS_NEVER_RAN("Threads never ran."),
+ THREADS_NEVER_INTERUPTED("Threads never interrupted."),
+ THREADS_THREW_EXCEPTION("Thread threw an exception."),
+ THREADS_NEVER_CREATED("Threads never created."),
+ TESTING_LOGIC_THREW_EXCEPTION("Testing Logic threw Exception."),
+ THREADS_TIMEDOUT("Threads Timed-out, Interupt likely not called."),
+ INCORRECT_COUNT("The size of mQueue is not consistent with the number of puts() and takes() performed.");
+
+ /**
+ * String value for the enumerated type.
+ */
+ private String mValue = null;
+
+ /**
+ * Initialize the mValue string.
+ */
+ private SynchronizedQueueResult(String value) {
+ mValue = value;
+ }
+
+ /**
+ * Return the mValue string.
+ */
+ public String getString() {
+ return mValue;
+ }
+ }
+
+ /**
+ * @class QueueAdapter
+ *
+ * @brief Applies a variant of the GoF Adapter pattern that
+ * enables us to test several implementations of the
+ * BlockingQueue interface.
+ */
+ public static class QueueAdapter {
+ /**
+ * Stores the queue that we're adapting.
+ */
+ private BlockingQueue mQueue;
+
+ /**
+ * Store the queue that we're adapting.
+ */
+ public QueueAdapter(BlockingQueue queue) {
+ mQueue = queue;
+ }
+
+ /**
+ * Returns the number of elements in this queue.
+ */
+ int size() {
+ return mQueue.size();
+ }
+
+ /**
+ * Insert msg at the tail of the queue.
+ *
+ * @throws TimeoutException and InterruptedException
+ */
+ public void put(E msg) throws InterruptedException, TimeoutException {
+ // Keep track of how many times we're called.
+ boolean timeoutValue = mQueue.offer(msg,
+ TIMEOUT_SECONDS,
+ TimeUnit.SECONDS);
+ if (timeoutValue == false)
+ throw new TimeoutException();
+
+ mProducerCounter++;
+ }
+
+ /**
+ * Remove msg from the head of the queue.
+ *
+ * @throws TimeoutException
+ * , InterruptedException
+ */
+ public E take() throws InterruptedException, TimeoutException {
+ // Keep track of how many times we're called.
+ E rValue = mQueue.poll(TIMEOUT_SECONDS,
+ TimeUnit.SECONDS);
+
+ if (rValue == null)
+ throw new TimeoutException();
+
+ mConsumerCounter++;
+
+ return rValue;
+ }
+ }
+
+ /**
+ * Adapter object used to test different BlockingQueue
+ * implementations.
+ */
+ private static QueueAdapter mQueue = null;
+
+ /**
+ * This runnable loops for mMaxIterations and calls put() on
+ * mQueue to insert the iteration number into the queue.
+ */
+ protected static Runnable mProducerRunnable = new Runnable() {
+ public void run() {
+ for (int i = 0; i < mMaxIterations; i++)
+ try {
+ mQueue.put(i);
+ if (Thread.interrupted())
+ throw new InterruptedException();
+ } catch (InterruptedException e) {
+ if (diagnosticsEnabled)
+ System.out.println("Thread "
+ + Thread.currentThread().getId()
+ + " in test "
+ + mTestName
+ + " properly interrupted by "
+ + e.toString() + " in producerRunnable");
+ // This isn't an error - it just means that
+ // we've been interrupted by the main Thread.
+ return;
+ } catch (TimeoutException e) {
+ if (diagnosticsEnabled)
+ System.out.println("Thread "
+ + Thread.currentThread().getId()
+ + " in test "
+ + mTestName
+ + " Exception "
+ + e.toString()
+ + " occurred in producerRunnable");
+ // Indicate a timeout.
+ mProducerCounter = TIMEOUT_OCCURRED;
+ return;
+ } catch (Exception e) {
+ if (diagnosticsEnabled)
+ System.out.println("Thread "
+ + Thread.currentThread().getId()
+ + " in test "
+ + mTestName
+ + " Exception "
+ + e.toString()
+ + " occurred in producerRunnable");
+ // Indicate a failure.
+ mProducerCounter = FAILURE_OCCURRED;
+ return;
+ }
+ }
+ };
+
+ /**
+ * This runnable loops for mMaxIterations and calls take() on mQueue to
+ * remove the iteration from the queue.
+ */
+ protected static Runnable mConsumerRunnable = new Runnable() {
+ public void run() {
+ for (int i = 0; i < mMaxIterations; i++)
+ try {
+ if (Thread.interrupted()) {
+ throw new InterruptedException();
+ }
+ Integer result = (Integer) mQueue.take();
+
+ if (diagnosticsEnabled)
+ System.out.println("iteration = " + result);
+ } catch (InterruptedException e) {
+ if (diagnosticsEnabled)
+ System.out.println("Thread "
+ + Thread.currentThread().getId()
+ + " in test "
+ + mTestName
+ + " properly interrupted by "
+ + e.toString() + " in consumerRunnable");
+ // This isn't an error - it just means that
+ // we've been interrupted by the main Thread.
+ return;
+ } catch (TimeoutException e) {
+ if (diagnosticsEnabled)
+ System.out.println("Thread "
+ + Thread.currentThread().getId()
+ + " in test "
+ + mTestName
+ + " Exception "
+ + e.toString()
+ + " occurred in consumerRunnable");
+ // Indicate a timeout.
+ mConsumerCounter = TIMEOUT_OCCURRED;
+ return;
+ } catch (Exception e) {
+ if (diagnosticsEnabled)
+ System.out.println("Thread "
+ + Thread.currentThread().getId()
+ + " in test "
+ + mTestName
+ + " Exception "
+ + e.toString()
+ + " occurred in consumerRunnable");
+ // Indicate a failure.
+ mConsumerCounter = FAILURE_OCCURRED;
+ return;
+ }
+ }
+ };
+
+ protected SynchronizedQueueResult checkResults() {
+ int numberOfRemainingItemsInQueue =
+ mProducerCounter - mConsumerCounter;
+
+ // Do some sanity checking to see if the Threads work as
+ // expected.
+ if (mConsumer == null
+ || mProducer == null)
+ return SynchronizedQueueResult.THREADS_NEVER_CREATED;
+ else if (mConsumer.isAlive()
+ || mProducer.isAlive())
+ return SynchronizedQueueResult.JOIN_NEVER_CALLED;
+ else if (mConsumerCounter == 0
+ || mProducerCounter == 0)
+ return SynchronizedQueueResult.THREADS_NEVER_RAN;
+ else if (mConsumerCounter == mMaxIterations
+ || mProducerCounter == mMaxIterations)
+ return SynchronizedQueueResult.THREADS_NEVER_INTERUPTED;
+ else if (mConsumerCounter == FAILURE_OCCURRED
+ || mProducerCounter == FAILURE_OCCURRED)
+ return SynchronizedQueueResult.THREADS_THREW_EXCEPTION;
+ else if (mConsumerCounter == TIMEOUT_OCCURRED
+ || mProducerCounter == TIMEOUT_OCCURRED)
+ return SynchronizedQueueResult.THREADS_TIMEDOUT;
+ else if (mQueue.size() != numberOfRemainingItemsInQueue)
+ return SynchronizedQueueResult.INCORRECT_COUNT;
+ else
+ return SynchronizedQueueResult.RAN_PROPERLY;
+ }
+
+ /**
+ * Number of iterations to test (the actual test shouldn't run
+ * this many iterations since the Threads ought to be interrupted
+ * long before it gets this far).
+ */
+ public static int mMaxIterations = 1000000;
+
+ /**
+ * The Java Threads that are used to produce and consume messages
+ * on the queue.
+ */
+ protected Thread mConsumer = null;
+ protected Thread mProducer = null;
+
+ /**
+ * The name of the test that's being run, e.g.,
+ */
+ protected static String mTestName = null;
+
+ /**
+ * If this is set to true in SynchronizedQueueImpl.java then lots
+ * of debugging output will be generated.
+ */
+ public static boolean diagnosticsEnabled;
+
+ /**
+ * These are hook methods that play the role of "primitive
+ * operations" in the Template Method pattern. They must be
+ * defined in SynchronizedQueueImpl.java by adding code after the
+ * "TODO" comments.
+ */
+ protected abstract void createThreads();
+ protected abstract void startThreads();
+ protected abstract void interruptThreads();
+ protected abstract void joinThreads() throws InterruptedException;
+
+ /**
+ * This template method runs the test on the queue parameter. It
+ * decouples the test code from the user-defined code using the
+ * Template Method pattern.
+ */
+ public SynchronizedQueueResult testQueue(QueueAdapter queue,
+ String testName) {
+ try {
+ mQueue = queue;
+ mTestName = testName;
+ mProducerCounter = 0;
+ mConsumerCounter = 0;
+
+ // Invoke the various hook methods, which are "primitive
+ // operations" in the Template Method pattern.
+ createThreads();
+ startThreads();
+
+ // Give the Threads a chance to run before interrupting
+ // them. (disabling console output makes them run really
+ // fast).
+ if (diagnosticsEnabled)
+ Thread.sleep(1000);
+ else
+ Thread.sleep(100);
+
+ interruptThreads();
+ joinThreads();
+
+ return checkResults();
+ } catch (Exception e) {
+ return SynchronizedQueueResult.TESTING_LOGIC_THREW_EXCEPTION;
+ }
+ }
+}
diff --git a/assignments/week-1-assignment-0-v2/src/edu/vuum/mooca/SynchronizedQueueImpl.java b/assignments/week-1-assignment-0-v2/src/edu/vuum/mooca/SynchronizedQueueImpl.java
new file mode 100644
index 000000000..c34c74bb7
--- /dev/null
+++ b/assignments/week-1-assignment-0-v2/src/edu/vuum/mooca/SynchronizedQueueImpl.java
@@ -0,0 +1,45 @@
+package edu.vuum.mooca;
+
+/**
+ * @class SynchronizedQueueImpl
+ *
+ * @brief This is where you put your implementation code to to (1)
+ * create, (2) start, (3) interrupt, and (4) wait for the
+ * completion of multiple Java Threads. This class plays the
+ * role of the "Concrete Class" in the Template Method pattern
+ * and isolates the code written by students from the
+ * underlying SynchronizedQueue test infrastructure.
+ *
+ * Make sure to keep all the "TODO" comments in the code below
+ * to make it easy for peer reviewers to find them.
+ */
+public class SynchronizedQueueImpl extends SynchronizedQueue {
+ // TODO - change this to true if you want to see diagnostic
+ // output on the console as the test runs.
+ static {
+ diagnosticsEnabled = false;
+ }
+
+ protected void createThreads() {
+ // TODO - replace the "null" assignments below to create two
+ // Java Threads, one that's passed the mProducerRunnable and
+ // the other that's passed the mConsumerRunnable.
+ mConsumer = null;
+ mProducer = null;
+ }
+
+ protected void startThreads() {
+ // TODO - you fill in here to start the threads. More
+ // interesting results will occur if you start the
+ // consumer first.
+ }
+
+ protected void interruptThreads() {
+ // TODO - you fill in here to interrupt the threads.
+ }
+
+ protected void joinThreads() throws InterruptedException {
+ // TODO - you fill in here to wait for the threads to
+ // exit.
+ }
+}
diff --git a/assignments/week-1-assignment-0-v2/src/edu/vuum/mooca/SynchronizedQueueTest.java b/assignments/week-1-assignment-0-v2/src/edu/vuum/mooca/SynchronizedQueueTest.java
new file mode 100644
index 000000000..aa67caa4d
--- /dev/null
+++ b/assignments/week-1-assignment-0-v2/src/edu/vuum/mooca/SynchronizedQueueTest.java
@@ -0,0 +1,121 @@
+package edu.vuum.mooca;
+import static org.junit.Assert.*;
+
+import java.util.concurrent.ArrayBlockingQueue;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import edu.vuum.mooca.SynchronizedQueue.*;
+
+/**
+ * @class SynchronizedQueueTest
+ *
+ * @brief This class tests queues for proper functionality by
+ * using the testQueue method defined in SynchronizedQueue.java
+ */
+public class SynchronizedQueueTest {
+ /**
+ * Indicates how big the queue should be.
+ */
+ int mQueueSize;
+
+ /**
+ * Run the test for the queue parameter.
+ *
+ * @return result. If SynchronizedQueue test ran properly, returns
+ * null. If not, returns error message.
+ */
+ static SynchronizedQueueResult runQueueTest(String qName,
+ QueueAdapter queue) {
+ if (SynchronizedQueue.diagnosticsEnabled) {
+ System.out.println("Starting "
+ + qName
+ + " test...");
+ if (qName == "BuggyBlockingQueue")
+ System.out.println("An exception may be thrown since "
+ + qName
+ + " is intentially BUGGY.");
+ }
+
+ /**
+ * We have to instantiate this object because Java doesn't
+ * like things being abstract AND static, which makes
+ * implementing the Template Pattern statically more painful
+ * than it should be.
+ */
+ SynchronizedQueueResult result =
+ new SynchronizedQueueImpl().testQueue(queue, qName);
+
+ if (SynchronizedQueue.diagnosticsEnabled) {
+ System.out.println("End " + qName + " test.\n");
+ System.out.println("See JUnit view for results -- \n"
+ + "Green check-marks denote program correctness. \n"
+ + "Blue x-marks indicate a problem with your implementation. \n");
+ }
+
+ if (result != SynchronizedQueueResult.RAN_PROPERLY)
+ return result;
+ else
+ return null;
+ }
+
+ /**
+ * Runs before each test. Sets mQueueSize.
+ * @throws Exception
+ */
+ @Before
+ public void setUp() throws Exception {
+ // Indicate how big the queue should be, which should be
+ // smaller than the number of iterations to induce blocking
+ // behavior.
+ mQueueSize = SynchronizedQueue.mMaxIterations / 10;
+ }
+
+ /**
+ * Tests the ArrayBlockingQueue, which should pass without error.
+ */
+ @Test
+ public void arrayBlockingQueueTest() {
+ // Make the appropriate QueueAdapter for the
+ // ArrayBlockingQueue.
+ QueueAdapter queueAdapter =
+ new QueueAdapter(new ArrayBlockingQueue(mQueueSize));
+
+ // Run a test on the ArrayBlockingQueue.
+ SynchronizedQueueResult errors =
+ runQueueTest("ArrayBlockingQueue", queueAdapter);
+
+ String errorMessage = "";
+
+ if (errors != null)
+ errorMessage = errors.getString();
+
+ assertNull("Error occurred: " +
+ errorMessage,
+ errors);
+ }
+
+ /**
+ * Tests the BuggyBlockingQueue, an intentionally flawed class.
+ * The buggyBlockingQueueTest() will succeed if the testQueue
+ * method fails, i.e., this test succeeds if our queue causes
+ * errors (which is what we expect)!
+ */
+ @Test
+ public void buggyBlockingQueueTest() {
+ // Make the appropriate QueueAdapter for the
+ // BuggyBlockingQueue.
+
+ QueueAdapter queueAdapter =
+ new QueueAdapter(new BuggyBlockingQueue(mQueueSize));
+
+ // Run a test on the BuggyBlockingQueue.
+ SynchronizedQueueResult errors =
+ runQueueTest("BuggyBlockingQueue", queueAdapter);
+
+ assertNotNull("Test should not complete without errors. " +
+ "BuggyBlockingQueue is intended to function incorrectly.",
+ errors);
+ }
+}
diff --git a/assignments/week-1-assignment-0/.project b/assignments/week-1-assignment-0/.project
index 1108d1f6e..5b2ee43b7 100644
--- a/assignments/week-1-assignment-0/.project
+++ b/assignments/week-1-assignment-0/.project
@@ -1,6 +1,6 @@
- SynchronizedQueueTest
+ W1-A0-SynchronizedQueueTest
diff --git a/assignments/week-1-assignment-0/Assignment-Description.txt b/assignments/week-1-assignment-0/Assignment-Description.txt
index e485c2765..76af1a687 100644
--- a/assignments/week-1-assignment-0/Assignment-Description.txt
+++ b/assignments/week-1-assignment-0/Assignment-Description.txt
@@ -1,4 +1,4 @@
-Week 1: Programming Assignment 0
+Week 1: Programming Assignment 0 Description
Released Monday, May 12, 2014
Due Monday, May 26th, 2014
@@ -50,7 +50,7 @@ We'll also discuss this assignment specification (and later its
solution) in the POSA MOOC "Virtual Office Hours", which are described
in item #38 at the POSA MOOC FAQ available from
-http://www.courera.org/course/posa
+http://www.coursera.org/course/posa
The SynchronizerQueueTest.java file uses JUnit to run the tests. We
do this to automate the testing process and leverage the integration
@@ -67,16 +67,15 @@ compile errors should disappear!
To run the JUnit tests in Eclipse, right-click on
SynchronizedQueueTest.java and go to "Run As > JUnit Test". The JUnit
-view will pop up in Eclipse and run the tests contained therein. All
-tests should pass. The ArrayBlockingQueue will pass because your
-testQueue method runs successfully. The tests for BuggyBlockingQueue
-(which is an intentionally flawed class), should "pass" if some error
-occurs while running testQueue (these errors are expected). As long
-as these JUnit tests both "pass" successfully your program will be be
-consider "correct" for the purposes of assessing this assignment.
-
-If the tests run and "pass," you should see check-marks next to each
-of the tests in the JUnit view. As the tests run, you will also find
-output being printed to the console. This text is for informational
-purposes only and have no bearing on whether your program is
-"correct."
+view will pop up in Eclipse and run the test contained therein. If
+your testQueue method runs successfully the ArrayBlockingQueue should
+pass, i.e., a green-check mark will appear next to the test in the
+JUnit view. As long as this JUnit test "passes" successfully your
+program will be be consider "correct" for the purposes of assessing
+this assignment.
+
+As the tests run, you will also find debugging output being printed to
+the console. This debugging output is for informational purposes only
+and have no bearing on whether your program is "correct." If the
+tests run and "pass," you should see a green check-mark next to the
+test in the JUnit view, which is all that matters.
diff --git a/assignments/week-1-assignment-0/src/edu/vuum/mooca/SynchronizedQueue.java b/assignments/week-1-assignment-0/src/edu/vuum/mooca/SynchronizedQueue.java
index c8a8c5edc..c4ffb3c18 100644
--- a/assignments/week-1-assignment-0/src/edu/vuum/mooca/SynchronizedQueue.java
+++ b/assignments/week-1-assignment-0/src/edu/vuum/mooca/SynchronizedQueue.java
@@ -186,7 +186,7 @@ public void run() {
System.out.println("Exception " + e.toString()
+ " occurred in consumerRunnable");
// Indicate a timeout.
- mProducerCounter = TIMEOUT_OCCURRED;
+ mConsumerCounter = TIMEOUT_OCCURRED;
return;
} catch (Exception e) {
System.out.println("Exception " + e.toString()
diff --git a/assignments/week-1-assignment-0/src/edu/vuum/mooca/SynchronizedQueueTest.java b/assignments/week-1-assignment-0/src/edu/vuum/mooca/SynchronizedQueueTest.java
index 2a459546b..427448946 100644
--- a/assignments/week-1-assignment-0/src/edu/vuum/mooca/SynchronizedQueueTest.java
+++ b/assignments/week-1-assignment-0/src/edu/vuum/mooca/SynchronizedQueueTest.java
@@ -68,21 +68,4 @@ public void arrayBlockingQueueTest() {
errors,
errors);
}
-
- /**
- * Tests the BuggyBlockingQueue, an intentionally flawed class.
- * The buggyBlockingQueueTest() will succeed if the testQueue
- * method fails, i.e., this test succeeds if our queue causes
- * errors (which is what we expect)!
- */
- @Test
- public void buggyBlockingQueueTest() {
- QueueAdapter queueAdapter =
- new QueueAdapter(new BuggyBlockingQueue(queueSize));
- String errors = runQueueTest("BuggyBlockingQueue", queueAdapter);
- assertNotNull("Test should not complete without errors. " +
- "BuggyBlockingQueue is intended to function incorrectly.",
- errors);
- }
-
}
diff --git a/assignments/week-2-assignment-1/.classpath b/assignments/week-2-assignment-1/.classpath
index 18d70f02c..3e0fb272a 100644
--- a/assignments/week-2-assignment-1/.classpath
+++ b/assignments/week-2-assignment-1/.classpath
@@ -1,6 +1,7 @@
-
+
+
diff --git a/assignments/week-2-assignment-1/Assignment-Description.txt b/assignments/week-2-assignment-1/Assignment-Description.txt
index 5ce1096ec..cd35d72e6 100644
--- a/assignments/week-2-assignment-1/Assignment-Description.txt
+++ b/assignments/week-2-assignment-1/Assignment-Description.txt
@@ -1,27 +1,27 @@
-Programming Assignment 0
+Week 2: Programming Assignment 1
-In this first (intentionally simple) assignment, you will use a Java
-ReentrantReadWriteLock to implement a subset of the
-java.util.concurrent.atomic.AtomicLong class, which we call
-SimpleAtomicLock. The goal is to understand how to use
+Released Monday, May 19th, 2014
+Due Monday, June 2nd, 2014
+
+In this second assignment, you'll implement a subset of the
+java.util.concurrent.atomic.SimpleAtomicLong class using a Java
+ReentrantReadWriteLock. The goal is to understand how to use
ReentrantReadWriteLock to serialize access to a variable that's shared
-by multiple threads. The SimpleAtomicLongTest.java program creates
-two Threads that increment and decrement the AtomicLong 10,000,000
-times each. If the SimpleAtomicLong implementation is properly
-serialized it's final value should be 0.
+by multiple threads.
The BuggyLongTest.java program shows what happens if concurrent access
to a long isn't properly serialized. This test program works best on
-a multi-core/multi-processor machine, which should actually induce
-failure due to race conditions stemming from parallel execution. If
-this test "succeeds" then your target platform is not sufficiently
-parallel to demonstrate the bug.
-
-In this directory you'll find the SimpleAtomicLong.java class, which
-contains the skeleton Java code that you'll implement by completing
-the "TODO - You fill in here" comments to provide a working solution.
-DO NOT CHANGE THE OVERALL STRUCTURE OF THE SKELETON - just fill in the
-"TODO - You fill in here" portions!!!
+a multi-core/multi-processor machine, which should induce a failure
+due to race conditions stemming from parallel execution. If this test
+"succeeds" then your target platform may not be sufficiently parallel
+to demonstrate the race conditions.
+
+In the src/edu/vuum/mocca directory you'll find the
+SimpleAtomicLong.java class, which contains the skeleton Java code
+that you'll implement by completing the "TODO - You fill in here"
+comments to provide a working solution. DO NOT CHANGE THE OVERALL
+STRUCTURE OF THE SKELETON - just fill in the "TODO - You fill in here"
+portions!!!
In particular, you'll need to do the following:
@@ -34,47 +34,45 @@ In particular, you'll need to do the following:
http://tutorials.jenkov.com/java-util-concurrent/readwritelock.html
-. Make sure to use a readLock() for AtomicLong.get() and writeLock()
- for all the other SimpleAtomicLong methods.
+. Make sure to use a readLock() for SimpleAtomicLong.get() and
+ writeLock() for all the other SimpleAtomicLong methods.
-. The SimpleAtomicLongTest.java program uses various features of Java
- Threads and Runnables, which are described in these videos:
+Note that the SimpleAtomicLongTest.java program uses Java Threads and
+Runnables, which are described in these videos:
Section 1: Module 2: Part 1: Overview of Java Threads (Part 1)
Section 1: Module 2: Part 2: Overview of Java Threads (Part 2)
- Likewise, it also uses the Java CountDownLatch and CyclicBarrier
- classes, which are described in this video:
+Likewise, it also uses the Java CountDownLatch and CyclicBarrier
+classes, which are described in this video:
Section 1: Module 2: Part 9: Java CountDownLatch
-At the moment, all these videos are available in the YouTube playlist
-at
-
-https://www.youtube.com/playlist?list=PLZ9NgFYEMxp4tbiFYip6tDNIEBRUDyPQK
-
-When the 2014 POSA MOOC officially starts these videos will be
-available at
+These videos are available at
https://class.coursera.org/posa-002/lecture
-We'll also discuss this assignment specification (and later its
-solution) in the POSA MOOC "Virtual Office Hours", which are described
-in item #38 at the POSA MOOC FAQ available from
+When you first open the project in Eclipse, you might see compile
+errors if JUnit is not included in your build path. To fix these
+errors, open SynchronizedQueueTest.java, hover over "org.junit," and
+click "Fix project setup." Make sure "Add JUnit 4 library to the
+build path" is selected and then click "OK." At this point, the
+compile errors should disappear!
+
+To run the JUnit tests in Eclipse, right-click on
+SimpleAtomicLongTests.java and Run As > JUnit Test. The JUnit view
+will pop up in Eclipse and run the the tests, which are divided into
+two subsets: multi-threaded and single-threaded tests. The
+single-threaded tests verify functionality of all methods within a
+single thread, while the multi-threaded tests do the same in a
+multi-threaded environment. All tests should run successfully, i.e.,
+a green-check mark will appear next to the test in the JUnit view. As
+long as this JUnit test "passes" successfully your program will be be
+consider "correct" for the purposes of assessing this assignment.
-http://www.courera.org/course/posa
-To compile this code you can either use the provided Eclipse project
-or simply type
-% javac SimpleAtomicLockTest.java SimpleAtomicLock.java
-on the command-line and then run the resulting class file by typing
-% java SimpleAtomicLockTest
-The output for a correct solution should look exactly like this:
-Starting SimpleAtomicLongTest
-test worked
-Finishing SimpleAtomicLongTest
diff --git a/assignments/week-2-assignment-1/src/SimpleAtomicLongTest.java b/assignments/week-2-assignment-1/src/SimpleAtomicLongTest.java
deleted file mode 100644
index a86ed7d4d..000000000
--- a/assignments/week-2-assignment-1/src/SimpleAtomicLongTest.java
+++ /dev/null
@@ -1,132 +0,0 @@
-// Import the necessary Java synchronization and scheduling classes.
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.CyclicBarrier;
-
-/**
- * @class SimpleAtomicLongTest
- *
- * @brief This class tests our implementation of SimpleAtomicLong to ensure
- * it works properly.
- */
-class SimpleAtomicLongTest
-{
- /**
- * Number of iterations to run the test.
- */
- final static long mMaxIterations = 10000000;
-
- /**
- * Barrier synchronizer that controls when the two threads start
- * running the test.
- */
- final static CyclicBarrier mStartBarrier = new CyclicBarrier(2);
-
- /**
- * Barrier synchronizer that controls when the main thread can
- * return.
- */
- final static CountDownLatch mStopLatch = new CountDownLatch(2);
-
- /**
- * An instance of our implementation of SimpleAtomicLong.
- */
- final static SimpleAtomicLong mCounter = new SimpleAtomicLong(0);
-
- /**
- * @brief This class runs the test by invoking a command each time
- * through the loop.
- */
- static class RunTest implements Runnable
- {
- /**
- * A Command that determines what operation is done within the
- * loop.
- */
- private Runnable mCommand;
-
- /**
- * Store the command in a data member field.
- */
- RunTest(Runnable command) {
- mCommand = command;
- }
-
- /**
- * Run the command within a loop.
- */
- public void run() {
- try
- {
- /**
- * Wait for both Threads to start running before
- * beginning the loop.
- */
- mStartBarrier.await();
-
- for (int i = 0; i < mMaxIterations; ++i)
- mCommand.run();
-
- /**
- * Inform the main thread that we're done.
- */
- mStopLatch.countDown();
- }
- catch (Exception e) {
- System.out.println("problem here");
- }
- }
- }
-
- /**
- * Main entry point method that runs the test.
- */
- public static void main(String[] args) {
- try {
- System.out.println("Starting SimpleAtomicLongTest");
-
- /**
- * A Runnable command that decrements the mCounter.
- */
- final Runnable decrementCommand =
- new Runnable() { public void run() { mCounter.getAndDecrement(); } };
-
- /**
- * A Runnable command that decrements the mCounter.
- */
- final Runnable incrementCommand =
- new Runnable() { public void run() { mCounter.getAndIncrement(); } };
-
- /**
- * Start a Thread whose Runnable command decrements the
- * SimpleAtomicLong mMaxIterations number of times.
- */
- new Thread(new RunTest(decrementCommand)).start();
-
- /**
- * Start a Thread whose Runnable command increments the
- * SimpleAtomicLong mMaxIterations number of times.
- */
- new Thread(new RunTest(incrementCommand)).start();
-
- /**
- * Barrier synchronizer that waits for both worker threads
- * to exit before continuing.
- */
- mStopLatch.await();
-
- long result = mCounter.get();
- /**
- * Check to ensure the test worked, i.e., mCounter's value
- * should be 0.
- */
- if (result == 0)
- System.out.println("test worked");
- else
- System.out.println("test failed: mCounter = " + result);
-
- System.out.println("Finishing SimpleAtomicLongTest");
- }
- catch (Exception e) { }
- }
-}
diff --git a/assignments/week-2-assignment-1/src/BuggyLongTest.java b/assignments/week-2-assignment-1/src/edu/vuum/mocca/BuggyLongTest.java
similarity index 92%
rename from assignments/week-2-assignment-1/src/BuggyLongTest.java
rename to assignments/week-2-assignment-1/src/edu/vuum/mocca/BuggyLongTest.java
index 74c467b24..fc854689b 100644
--- a/assignments/week-2-assignment-1/src/BuggyLongTest.java
+++ b/assignments/week-2-assignment-1/src/edu/vuum/mocca/BuggyLongTest.java
@@ -1,4 +1,4 @@
-// Import the necessary Java synchronization and scheduling classes.
+package edu.vuum.mocca;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
@@ -14,7 +14,7 @@ class BuggyLongTest
/**
* Number of iterations to run the test.
*/
- static final long mMaxIterations = 10000000;
+ static final long mMaxIterations = 100000000;
/**
* Barrier synchronizer that controls when the two threads start
@@ -41,7 +41,7 @@ public static void main(String[] args) {
System.out.println("Starting BuggyLongTest");
/**
- * Start a Thread whose Runnable decrements the AtomicLong
+ * Start a Thread whose Runnable decrements the SimpleAtomicLong
* mMaxIterations number of times.
*/
new Thread(new Runnable()
@@ -70,7 +70,7 @@ public static void main(String[] args) {
}).start();
/**
- * Start a Thread whose Runnable increments the AtomicLong
+ * Start a Thread whose Runnable increments the SimpleAtomicLong
* mMaxIterations number of times.
*/
new Thread(new Runnable()
diff --git a/assignments/week-2-assignment-1/src/SimpleAtomicLong.java b/assignments/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLong.java
similarity index 59%
rename from assignments/week-2-assignment-1/src/SimpleAtomicLong.java
rename to assignments/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLong.java
index fc781e4ee..f57064f95 100644
--- a/assignments/week-2-assignment-1/src/SimpleAtomicLong.java
+++ b/assignments/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLong.java
@@ -1,5 +1,7 @@
// Import the necessary Java synchronization and scheduling classes.
+package edu.vuum.mocca;
+
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.Lock;
@@ -16,31 +18,27 @@ class SimpleAtomicLong
* The value that's manipulated atomically via the methods.
*/
private long mValue;
-
+
+
/**
* The ReentrantReadWriteLock used to serialize access to mValue.
*/
- // TODO - replace the null with the appropriate initialization:
- private ReentrantReadWriteLock mRWLock = null;
+ // TODO - add the implementation
/**
* Creates a new SimpleAtomicLong with the given initial value.
*/
- public SimpleAtomicLong(long initialValue)
- {
+ public SimpleAtomicLong(long initialValue) {
// TODO - you fill in here
}
/**
- * @brief Gets the current value.
+ * @brief Gets the current value
*
* @returns The current value
*/
- public long get()
- {
- long value;
- // TODO - you fill in here, using a readLock()
- return value;
+ public long get() {
+ // TODO - you fill in here
}
/**
@@ -48,11 +46,8 @@ public long get()
*
* @returns the updated value
*/
- public long decrementAndGet()
- {
- long value;
- // TODO - you fill in here, using a writeLock()
- return value;
+ public long decrementAndGet() {
+ // TODO - you fill in here
}
/**
@@ -60,11 +55,8 @@ public long decrementAndGet()
*
* @returns the previous value
*/
- public long getAndIncrement()
- {
- long value;
- // TODO - you fill in here, using a writeLock()
- return value;
+ public long getAndIncrement() {
+ // TODO - you fill in here
}
/**
@@ -72,11 +64,8 @@ public long getAndIncrement()
*
* @returns the previous value
*/
- public long getAndDecrement()
- {
- long value;
- // TODO - you fill in here, using a writeLock()
- return value;
+ public long getAndDecrement() {
+ // TODO - you fill in here
}
/**
@@ -84,11 +73,8 @@ public long getAndDecrement()
*
* @returns the updated value
*/
- public long incrementAndGet()
- {
- long value;
- // TODO - you fill in here, using a writeLock()
- return value;
+ public long incrementAndGet() {
+ // TODO - you fill in here
}
}
diff --git a/assignments/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLongMultithreadedTest.java b/assignments/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLongMultithreadedTest.java
new file mode 100644
index 000000000..afde62c6f
--- /dev/null
+++ b/assignments/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLongMultithreadedTest.java
@@ -0,0 +1,322 @@
+package edu.vuum.mocca;
+
+import static org.junit.Assert.*;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * @class SimpleAtomicLongMultithreadedTest
+ *
+ * @brief Test the logic and multithreaded implementation of the
+ * SimpleAtomicLong class by having concurrent threads call the
+ * SimpleAtomicLong instance for various methods.
+ */
+public class SimpleAtomicLongMultithreadedTest {
+
+ /**
+ * Our start points.
+ */
+ final static long INITIAL_VALUE = 0;
+
+ /**
+ * Number of iterations to run the commands.
+ */
+ final static long mMaxIterations = 1000000;
+
+ /**
+ * Barrier synchronizer that controls when the threads start
+ * running the test.
+ */
+ static CyclicBarrier mStartBarrier;
+
+ /**
+ * Barrier synchronizer that controls when the main thread can
+ * return.
+ */
+ static CountDownLatch mStopLatch;
+
+ /**
+ * An instance of our implementation of SimpleAtomicLong, which is
+ * defined as "volatile" to ensure proper visibility of its fields
+ * after construction.
+ */
+ static volatile SimpleAtomicLong mCounter;
+
+ /**
+ * Runnable commands that use the mCounter methods
+ * get()
+ * incrementAndGet()
+ * getAndIncrement()
+ * decrementAndGet()
+ * getAndDecrement()
+ */
+ static Runnable getCommand;
+ static Runnable incrementGetCommand;
+ static Runnable getIncrementCommand;
+ static Runnable decrementGetCommand;
+ static Runnable getDecrementCommand;
+
+ /**
+ * The value of mCounter prior to any changes made by testing.
+ */
+ long preTestValue;
+
+ /**
+ * The number of duplicate threads to run when testing each
+ * individual command.
+ */
+ final int numThreads = 5;
+
+ /**
+ * @class RunTest
+ *
+ * @brief This class runs the test by invoking a command each time
+ * through the loop.
+ */
+ static class RunTest implements Runnable
+ {
+ /**
+ * A Command that determines what operation is done within the
+ * loop.
+ */
+ private Runnable mCommand;
+
+ /**
+ * An integer which keeps track of the number of times the
+ * command has been called. Initially equal to zero.
+ */
+ private long iterations = 0;
+
+ /**
+ * Store the command in a data member field.
+ */
+ RunTest(Runnable command) {
+ mCommand = command;
+ }
+
+ /**
+ * Run the command within a loop.
+ */
+ public void run() {
+ try
+ {
+ /**
+ * Wait for all Threads to start running before
+ * beginning the loop.
+ */
+ mStartBarrier.await();
+
+ for (; iterations < mMaxIterations; ++iterations) {
+ mCommand.run();
+ }
+ /**
+ * Inform the main thread that we're done.
+ */
+ mStopLatch.countDown();
+ } catch (Exception e) {
+ fail("Runnable failed.");
+ }
+ }
+
+ /**
+ * Returns the number of times this command has been performed.
+ * @return iterations
+ */
+ public long getIterations() {
+ return iterations;
+ }
+ }
+
+ /**
+ * Runs prior to all tests. Creates a static instance of
+ * SimpleAtomicLong and all runnable commands.
+ */
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ /**
+ * Instance of SimpleAtomicLong class
+ */
+ mCounter = new SimpleAtomicLong(INITIAL_VALUE);
+
+ /**
+ * Runnable commands that execute get(), incrementAndGet(),
+ * getAndIncrement(), decrementAndGet(), getAndDecrement(),
+ * respectively, on the SimpleAtomicLong instance
+ */
+ getCommand = new Runnable() { public void run() { mCounter.get(); } };
+ incrementGetCommand = new Runnable() { public void run() { mCounter.incrementAndGet(); } };
+ getIncrementCommand = new Runnable() { public void run() { mCounter.getAndIncrement(); } };
+ decrementGetCommand = new Runnable() { public void run() { mCounter.decrementAndGet(); } };
+ getDecrementCommand = new Runnable() { public void run() { mCounter.getAndDecrement(); } };
+ }
+
+ /**
+ * Runs prior to each test. Stores the pre-test value of the mCounter.
+ */
+ @Before
+ public void setUp() throws Exception {
+ preTestValue = mCounter.get();
+ }
+
+ /**
+ * Tests for proper concurrency and functionality of {@code get()}.
+ */
+ @Test
+ public void multiGetTest() {
+ /**
+ * run multiple threads calling mCounter.get().
+ */
+ runThreads(getCommand);
+ /**
+ * The expected post-test value is no change in the pre-test
+ * value.
+ */
+ assertEquals(preTestValue,
+ mCounter.get());
+ }
+
+ /**
+ * Tests for proper concurrency and functionality of {@code
+ * incrementAndGet()}.
+ */
+ @Test
+ public void multiIncrementAndGetTest() {
+ runThreads(incrementGetCommand);
+ /**
+ * expected value after threads are run should be the number
+ * of maximum iterations times the number of threads plus the
+ * pre-test value
+ */
+ assertEquals(preTestValue
+ + mMaxIterations*numThreads,
+ mCounter.get());
+ }
+
+ /**
+ * Tests for proper concurrency and functionality of {@code getAndIncrement()}.
+ */
+ @Test
+ public void multiGetAndIncrementTest() {
+ runThreads(getIncrementCommand);
+ assertEquals(preTestValue
+ + mMaxIterations*numThreads,
+ mCounter.get());
+ }
+
+ /**
+ * Tests for proper concurrency and functionality of {@code
+ * decrementAndGet()}.
+ */
+ @Test
+ public void multiDecrementAndGetTest() {
+ runThreads(decrementGetCommand);
+ /**
+ * Expected value of mCounter after threads have completed
+ * running is the pre-test value minus the maximum iterations
+ * times the number of threads that were run.
+ */
+ assertEquals(preTestValue -
+ mMaxIterations*numThreads,
+ mCounter.get());
+ }
+
+ /**
+ * Tests for proper concurrency and functionality of {@code getAndIncrement()}.
+ */
+ @Test
+ public void multiGetAndDecrementTest() {
+ runThreads(getDecrementCommand);
+ /**
+ * expected value of mCounter after threads have completed running
+ * is the pre-test value minus the maximum iterations times the number
+ * of threads that were run
+ */
+ assertEquals(preTestValue -
+ mMaxIterations*numThreads,
+ mCounter.get());
+ }
+
+ /**
+ * Tests concurrent running of threads performing a variety of
+ * operations on mCounter (e.g. {@code get()}, {@code
+ * getAndIncrement()}, {@code getAndDecrement()},{@code
+ * incrementAndGet()}, and {@code decrementAndGet()}).
+ */
+ @Test
+ public void multiThreadedTest() {
+ /**
+ * Run five threads concurrently, each performing a different
+ * method on the SimpleAtomicLong instance
+ */
+ runThreads(null);
+ /**
+ * Check to ensure the pre-test and post-test values are
+ * equal. This indicates the test was successful.
+ */
+ assertEquals(preTestValue,
+ mCounter.get());
+ }
+
+ /**
+ * Runs numThreads concurrent threads executing the same command.
+ * Has a CyclicBarrier and CountDownLatch to facilitate
+ * concurrency.
+ * @param command
+ */
+ private void runThreads(Runnable command) {
+ mStartBarrier = new CyclicBarrier(numThreads);
+ mStopLatch = new CountDownLatch(numThreads);
+ try {
+ /**
+ * Create an array of RunTests whose Runnable commands
+ * execute on the SimpleAtomicLong mMaxIterations number
+ * of times. If given a command, each thread should run
+ * duplicates. If command is null, then run one of each
+ * type of command.
+ */
+ RunTest[] runTests;
+ if(command == null) {
+ runTests = new RunTest[5];
+ runTests[0] = new RunTest(getCommand);
+ runTests[1] = new RunTest(incrementGetCommand);
+ runTests[2] = new RunTest(decrementGetCommand);
+ runTests[3] = new RunTest(getDecrementCommand);
+ runTests[4] = new RunTest(getIncrementCommand);
+ }
+ else {
+ runTests = new RunTest[numThreads];
+ for(int i = 0; i < runTests.length; i++)
+ runTests[i] = new RunTest(command);
+ }
+
+ /**
+ * Start threads whose Runnable commands execute on the
+ * SimpleAtomicLong mMaxIterations number of times.
+ */
+ for(int i = 0; i < runTests.length; i++)
+ new Thread(runTests[i]).start();
+
+ /**
+ * Barrier synchronizer that waits for all worker threads
+ * to exit before continuing.
+ */
+ mStopLatch.await();
+
+ /**
+ * Check to ensure threads have run.
+ */
+ for(int i = 0; i < runTests.length; i++)
+ assertEquals("Threads have not executed.",
+ mMaxIterations,
+ runTests[i].getIterations());
+ } catch (Exception e) {
+ fail("Exception thrown.");
+ }
+ }
+
+}
diff --git a/assignments/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLongSingleThreadedTest.java b/assignments/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLongSingleThreadedTest.java
new file mode 100644
index 000000000..ef28e3e0a
--- /dev/null
+++ b/assignments/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLongSingleThreadedTest.java
@@ -0,0 +1,235 @@
+package edu.vuum.mocca;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @brief SimpleAtomicLongSingleThreadedTest
+ *
+ * @class Evalutes the logic of the SimpleAtomicLong class by testing
+ * every method with each of the values in mTestValues.
+ */
+public class SimpleAtomicLongSingleThreadedTest {
+ /*
+ * Test possible edge cases at 0, and a large negative and
+ * positive.
+ */
+ final static long[] mTestValues = { -100, -1, 0, 1, 100 };
+
+ /**
+ * Test Constructor.
+ */
+ @Test
+ public void constructorTest() {
+ for (long testValue : mTestValues) {
+ SimpleAtomicLong counter =
+ new SimpleAtomicLong(testValue);
+ Assert.assertNotNull(counter);
+ assertEquals(testValue, counter.get());
+ }
+ }
+
+ /**
+ * Test get()
+ */
+ @Test
+ public void getTest() {
+ for (long testValue : mTestValues) {
+ SimpleAtomicLong counter = new SimpleAtomicLong(testValue);
+ assertEquals(testValue,
+ counter.get());
+ }
+ }
+
+ /**
+ * test decrementAndGet()
+ */
+ @Test
+ public void decrementAndGetTest() {
+ for (long testValue : mTestValues) {
+ final SimpleAtomicLong counter =
+ new SimpleAtomicLong(testValue);
+ decrementAndGetTestLogic(counter,
+ testValue,
+ testValue - 1,
+ testValue - 1);
+ }
+ }
+
+ /**
+ * test getAndDecrement()
+ */
+ @Test
+ public void getAndDecrementTest() {
+ for (long testValue : mTestValues) {
+ final SimpleAtomicLong counter =
+ new SimpleAtomicLong(testValue);
+ getAndDecrementTestLogic(counter,
+ testValue,
+ testValue,
+ testValue - 1);
+ }
+ }
+
+ /**
+ * test incrementAndGet()
+ */
+ @Test
+ public void incrementAndGetTestTest() {
+ for (long testValue : mTestValues) {
+ final SimpleAtomicLong counter =
+ new SimpleAtomicLong(testValue);
+ incrementAndGetTestLogic(counter,
+ testValue,
+ testValue + 1,
+ testValue + 1);
+ }
+ }
+
+ /**
+ * test getAndIncrement()
+ */
+ @Test
+ public void getAndIncrementTest() {
+ for (long testValue : mTestValues) {
+ final SimpleAtomicLong counter =
+ new SimpleAtomicLong(testValue);
+ getAndIncrementTestLogic(counter,
+ testValue,
+ testValue,
+ testValue + 1);
+ }
+ }
+
+ /**
+ * Compares the values expected with the values produced by each test logic.
+ *
+ * @param pre
+ * The 'pre' number produced by the test
+ * @param preValue
+ * The 'pre' number expected
+ * @param result
+ * The 'result' number produced by the test
+ * @param resultValue
+ * The 'result' expected
+ * @param post
+ * The 'post' number produced by the test
+ * @param postValue
+ * The 'post' expected
+ */
+ private void compareResults(long pre, long preValue, long result,
+ long resultValue, long post, long postValue) {
+ assertEquals(pre, preValue);
+ assertEquals(result, resultValue);
+ assertEquals(post, postValue);
+ }
+
+ /**
+ * The Logic of testing decrementAndGet
+ *
+ * @param simpleAtomicLong
+ * The SimpleAtomicLong to be tested
+ * @param preValue
+ * The expected 'pre' value
+ * @param resultValue
+ * The expected 'result' value
+ * @param postValue
+ * The expected 'post' value
+ */
+ public void decrementAndGetTestLogic(SimpleAtomicLong simpleAtomicLong,
+ long preValue, long resultValue, long postValue) {
+ long pre = simpleAtomicLong.get();
+ long result = simpleAtomicLong.decrementAndGet();
+ long post = simpleAtomicLong.get();
+ assertEquals(pre - 1, result);
+ assertEquals(result, post);
+ compareResults(pre,
+ preValue,
+ result,
+ resultValue,
+ post,
+ postValue);
+ }
+
+ /**
+ * The Logic of testing getAndDecrement
+ *
+ * @param simpleAtomicLong
+ * The SimpleAtomicLong to be tested
+ * @param preValue
+ * The expected 'pre' value
+ * @param resultValue
+ * The expected 'result' value
+ * @param postValue
+ * The expected 'post' value
+ */
+ public void getAndDecrementTestLogic(SimpleAtomicLong simpleAtomicLong,
+ long preValue, long resultValue, long postValue) {
+ long pre = simpleAtomicLong.get();
+ long result = simpleAtomicLong.getAndDecrement();
+ long post = simpleAtomicLong.get();
+ assertEquals(pre, result);
+ assertEquals(pre - 1, post);
+ compareResults(pre,
+ preValue,
+ result,
+ resultValue,
+ post,
+ postValue);
+ }
+
+ /**
+ * The Logic of testing incrementAndGet
+ *
+ * @param simpleAtomicLong
+ * The SimpleAtomicLong to be tested
+ * @param preValue
+ * The expected 'pre' value
+ * @param resultValue
+ * The expected 'result' value
+ * @param postValue
+ * The expected 'post' value
+ */
+ public void incrementAndGetTestLogic(SimpleAtomicLong simpleAtomicLong,
+ long preValue, long resultValue, long postValue) {
+ long pre = simpleAtomicLong.get();
+ long result = simpleAtomicLong.incrementAndGet();
+ long post = simpleAtomicLong.get();
+ assertEquals(pre + 1, result);
+ assertEquals(result, post);
+ compareResults(pre,
+ preValue,
+ result,
+ resultValue,
+ post,
+ postValue);
+ }
+
+ /**
+ * The Logic of testing getAndIncrement
+ *
+ * @param simpleAtomicLong
+ * The SimpleAtomicLong to be tested
+ * @param preValue
+ * The expected 'pre' value
+ * @param resultValue
+ * The expected 'result' value
+ * @param postValue
+ * The expected 'post' value
+ */
+ public void getAndIncrementTestLogic(SimpleAtomicLong simpleAtomicLong,
+ long preValue, long resultValue, long postValue) {
+ long pre = simpleAtomicLong.get();
+ long result = simpleAtomicLong.getAndIncrement();
+ long post = simpleAtomicLong.get();
+ assertEquals(pre, result);
+ assertEquals(pre + 1, post);
+ compareResults(pre,
+ preValue,
+ result,
+ resultValue,
+ post, postValue);
+ }
+}
diff --git a/assignments/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLongTests.java b/assignments/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLongTests.java
new file mode 100644
index 000000000..990496762
--- /dev/null
+++ b/assignments/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLongTests.java
@@ -0,0 +1,18 @@
+package edu.vuum.mocca;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+
+
+@RunWith(Suite.class)
+@SuiteClasses({ SimpleAtomicLongMultithreadedTest.class,
+ SimpleAtomicLongSingleThreadedTest.class })
+/**
+ * @class SimpleAtomicLongTest
+ *
+ * @brief Entry point for running all the regression tests for the
+ * SimpleAtomicLong implementation.
+ */
+public class SimpleAtomicLongTests {
+}
diff --git a/assignments/week-3-assignment-2/.classpath b/assignments/week-3-assignment-2/.classpath
index 18d70f02c..b0e3db7ba 100644
--- a/assignments/week-3-assignment-2/.classpath
+++ b/assignments/week-3-assignment-2/.classpath
@@ -1,6 +1,8 @@
+
+
diff --git a/assignments/week-3-assignment-2/Assignment-Description.txt b/assignments/week-3-assignment-2/Assignment-Description.txt
index 1a9d94c75..57c2ddf42 100644
--- a/assignments/week-3-assignment-2/Assignment-Description.txt
+++ b/assignments/week-3-assignment-2/Assignment-Description.txt
@@ -1,38 +1,49 @@
-Programming Assignment 0.5
+Week 3: Programming Assignment 2
+
+Released Monday, May 26th, 2014
+Due Monday, June 9th, 2014
In this assignment, you will use a Java ReentrantLock and Java
-ConditionObject to implement a subset of the Java
+Condition to implement a subset of the Java
java.util.concurrent.Semaphore class, which we call SimpleSemaphore.
-This assignment also reuses the SimpleAtomicLock you implemented for
-Assignment 0, so make sure it's working properly before attempting
-this assignment! The goal of this assignment is to use the
-SimpleSemaphore to correctly implement a resource manager that limits
-the number of Beings from Middle-Earth who can concurrently gaze into
-a Palantir (if you're not a Lord of the Ring's fan see
+This assignment also reuses the SimpleAtomicLong you implemented for
+week-2-assignment-1, so make sure it's compiling and running properly
+before attempting this assignment!
+
+The goal of this assignment is to use the SimpleSemaphore to correctly
+implement a resource manager that limits the number of Beings from
+Middle-Earth who can concurrently gaze into a Palantir. Please see
http://en.wikipedia.org/wiki/Palantir for more information on
-Palantir).
+Palantirs if you're not yet a fan of Tolkein's Lord of the Ring's.
-The PalantirManagerTest.java program creates three Palantiri and five
-Threads (one for each Palantir user) that concurrently attempt to
+The PalantirManagerUnitTest.java program creates three Palantiri and
+five Threads (one for each Palantir user) that concurrently attempt to
acquire a Palantir and gaze into it for a certain amount of time. If
-the SimpleSemaphore and SimpleAtomicLock are implemented properly the
-test should succeed without throwing any exceptions.
+the SimpleSemaphore and SimpleAtomicLong are implemented properly the
+test should succeed without throwing any exceptions, as described
+further below.
In this directory you'll find a number of files, all of which you
should read. However, the only two files you need to modify are
-SimpleAtomicLong.java class and SimpleSemaphore, which contains the
-skeleton Java code that you'll implement by completing the "TODO - You
-fill in here" comments to provide a working solution. DO NOT CHANGE
-THE OVERALL STRUCTURE OF THE SKELETON - just fill in the "TODO - You
-fill in here" portions!!!
+SimpleAtomicLong.java and SimpleSemaphore.java in the
+src/edu/vuum/mocca directory, which contains the skeleton Java code
+that you'll implement by completing the "TODO - You fill in here"
+comments to provide a working solution. DO NOT CHANGE THE OVERALL
+STRUCTURE OF THE SKELETON - just fill in the "TODO - You fill in here"
+portions!!!
In particular, you'll need to do the following:
. Implement the SimpleAtomicLong class, which you should replace with
- your working solution to assignment0.
-
-. Implement the SimpleSemaphore class using a Java ConditionObject and
- Java ReentrantLock, which are covered in these videos:
+ your solution to week-2-assignment-1, after applying any fixes
+ motivated by watching the Virtual Office Hours video of the
+ instructor's solution(s). This class is only used by the
+ PalantirManagerUnitTest.java and should not be used in the
+ SimpleSemaphore implementation itself.
+
+. Implement the SimpleSemaphore class using a Java ConditionObject
+ (accessed via a Condition) and Java ReentrantLock, which are covered
+ in these videos:
Section 1: Module 2: Part 5: Java ReentrantLock
Section 1: Module 2: Part 8: Java ConditionObject
@@ -58,13 +69,7 @@ In particular, you'll need to do the following:
Section 1: Module 2: Part 1: Overview of Java Threads (Part 1)
Section 1: Module 2: Part 2: Overview of Java Threads (Part 2)
-At the moment, all these videos are available in the YouTube playlist
-at
-
-https://www.youtube.com/playlist?list=PLZ9NgFYEMxp4tbiFYip6tDNIEBRUDyPQK
-
-When the 2014 POSA MOOC officially starts these videos will be
-available at
+These videos are available at
https://class.coursera.org/posa-002/lecture
@@ -72,134 +77,38 @@ We'll also discuss this assignment specification (and later its
solution) in the POSA MOOC "Virtual Office Hours", which are described
in item #38 at the POSA MOOC FAQ available from
-http://www.courera.org/course/posa
-
-To compile this code you can either use the provided Eclipse project
-or go into the src directory and simply type
-
-% javac FairnessChecker.java Palantir.java PalantirManager.java
- PalantirManagerTest.java SimpleAtomicLong.java SimpleSemaphore.java
-
-on the command-line and then run the resulting class file by typing
-
-% java PalantirManagerTest
-
-The output for a correct solution should look something this:
-
-----------------------------------------
-Starting PalantirManagerTest
-Pippen is acquiring the palantir
-Pippen is starting to gaze at the MinisTirith palantir
-Aragorn is acquiring the palantir
-Aragorn is starting to gaze at the Orthanc palantir
-Denathor is acquiring the palantir
-Denathor is starting to gaze at the Barad-dur palantir
-Sauron is acquiring the palantir
-Saruman is acquiring the palantir
-Pippen is finished gazing at the MinisTirith palantir
-Pippen is releasing the MinisTirith palantir
-Pippen is acquiring the palantir
-Sauron is starting to gaze at the MinisTirith palantir
-Sauron is finished gazing at the MinisTirith palantir
-Sauron is releasing the MinisTirith palantir
-Sauron is acquiring the palantir
-Saruman is starting to gaze at the MinisTirith palantir
-Saruman is finished gazing at the MinisTirith palantir
-Pippen is starting to gaze at the MinisTirith palantir
-Saruman is releasing the MinisTirith palantir
-Saruman is acquiring the palantir
-Pippen is finished gazing at the MinisTirith palantir
-Pippen is releasing the MinisTirith palantir
-Pippen is acquiring the palantir
-Sauron is starting to gaze at the MinisTirith palantir
-Sauron is finished gazing at the MinisTirith palantir
-Sauron is releasing the MinisTirith palantir
-Sauron is acquiring the palantir
-Saruman is starting to gaze at the MinisTirith palantir
-Saruman is finished gazing at the MinisTirith palantir
-Pippen is starting to gaze at the MinisTirith palantir
-Saruman is releasing the MinisTirith palantir
-Saruman is acquiring the palantir
-Pippen is finished gazing at the MinisTirith palantir
-Pippen is releasing the MinisTirith palantir
-Pippen is acquiring the palantir
-Sauron is starting to gaze at the MinisTirith palantir
-Sauron is finished gazing at the MinisTirith palantir
-Sauron is releasing the MinisTirith palantir
-Sauron is acquiring the palantir
-Saruman is starting to gaze at the MinisTirith palantir
-Saruman is finished gazing at the MinisTirith palantir
-Pippen is starting to gaze at the MinisTirith palantir
-Saruman is releasing the MinisTirith palantir
-Saruman is acquiring the palantir
-Denathor is finished gazing at the Barad-dur palantir
-Denathor is releasing the Barad-dur palantir
-Denathor is acquiring the palantir
-Sauron is starting to gaze at the Barad-dur palantir
-Pippen is finished gazing at the MinisTirith palantir
-Pippen is releasing the MinisTirith palantir
-Pippen is acquiring the palantir
-Saruman is starting to gaze at the MinisTirith palantir
-Saruman is finished gazing at the MinisTirith palantir
-Denathor is starting to gaze at the MinisTirith palantir
-Saruman is releasing the MinisTirith palantir
-Saruman is acquiring the palantir
-Denathor is finished gazing at the MinisTirith palantir
-Denathor is releasing the MinisTirith palantir
-Denathor is acquiring the palantir
-Pippen is starting to gaze at the MinisTirith palantir
-Pippen is finished gazing at the MinisTirith palantir
-Pippen is releasing the MinisTirith palantir
-Saruman is starting to gaze at the MinisTirith palantir
-Saruman is finished gazing at the MinisTirith palantir
-Denathor is starting to gaze at the MinisTirith palantir
-Saruman is releasing the MinisTirith palantir
-Aragorn is finished gazing at the Orthanc palantir
-Aragorn is releasing the Orthanc palantir
-Aragorn is acquiring the palantir
-Aragorn is starting to gaze at the Orthanc palantir
-Denathor is finished gazing at the MinisTirith palantir
-Denathor is releasing the MinisTirith palantir
-Denathor is acquiring the palantir
-Denathor is starting to gaze at the MinisTirith palantir
-Denathor is finished gazing at the MinisTirith palantir
-Denathor is releasing the MinisTirith palantir
-Denathor is acquiring the palantir
-Denathor is starting to gaze at the MinisTirith palantir
-Denathor is finished gazing at the MinisTirith palantir
-Denathor is releasing the MinisTirith palantir
-Sauron is finished gazing at the Barad-dur palantir
-Sauron is releasing the Barad-dur palantir
-Sauron is acquiring the palantir
-Sauron is starting to gaze at the MinisTirith palantir
-Sauron is finished gazing at the MinisTirith palantir
-Sauron is releasing the MinisTirith palantir
-Aragorn is finished gazing at the Orthanc palantir
-Aragorn is releasing the Orthanc palantir
-Aragorn is acquiring the palantir
-Aragorn is starting to gaze at the MinisTirith palantir
-Aragorn is finished gazing at the MinisTirith palantir
-Aragorn is releasing the MinisTirith palantir
-Aragorn is acquiring the palantir
-Aragorn is starting to gaze at the MinisTirith palantir
-Aragorn is finished gazing at the MinisTirith palantir
-Aragorn is releasing the MinisTirith palantir
-Aragorn is acquiring the palantir
-Aragorn is starting to gaze at the MinisTirith palantir
-Aragorn is finished gazing at the MinisTirith palantir
-Aragorn is releasing the MinisTirith palantir
-Finishing PalantirManagerTest
-----------------------------------------
-
-Don't worry if your output occurs in a different order - that's to be
-expected in multi-threaded programs and OS/JVM platforms. Also, don't
-worry about "warnings" since there's an intentional race condition in
-the test since it's possible for one thread to call
+http://www.coursera.org/course/posa
+
+To compile this code you need to use the provided Eclipse project.
+There is a Unit Test Suite that will run all three Unit test files
+included in the test/ directory for you at once. When you first open
+the project in Eclipse, you might see compile errors if JUnit is not
+included in your build path. To fix these errors, open
+SynchronizedQueueTest.java, hover over "org.junit," and click "Fix
+project setup." Make sure "Add JUnit 4 library to the build path" is
+selected and then click "OK." At this point, the compile errors
+should disappear!
+
+Right click on the test suite (AllTests.java) or an individual
+*_UnitTest.java file in Eclipse and select 'Run As' -> 'JUnit
+Test'. When the assignment is complete, all the tests should complete
+successfully. If a test passes a green-check mark will appear next to
+the test in the JUnit view. As long as this JUnit test "passes"
+successfully your program will be be consider "correct" for the
+purposes of assessing this assignment.
+
+If you'd like to enable verbose debugging output please set the
+diagnosticsEnabled flag in PlanantirManagerUnitTest.java to true (it
+defaults to false). However, this will generate a lot of output and
+you'll need to look at it carefully to understand what it's doing. If
+you do this, don't worry about "warnings" since there's an intentional
+race condition in the test since it's possible for one thread to call
mFairnessChecker.addNewThread() and then yield to another thread which
again calls mFairnessChecker.addNewThread() and then goes on without
interruption to call mPalantirManager.acquirePalantir(), which will
fool the fairness checker into wrongly thinking the acquisition wasn't
-fair. we'll just give a warning (rather than an error) if it looks
-like the semaphore acquire() method isn't "fair". The key is not to
-have runtime exceptions in your output!
+fair. We therefore just give a warning (rather than an error) if it
+looks like the semaphore acquire() method isn't "fair". The key is
+not to have runtime exceptions, i.e., you want only "green-check
+marks" in the JUnit view output!
diff --git a/assignments/week-3-assignment-2/src/SimpleSemaphore.java b/assignments/week-3-assignment-2/src/SimpleSemaphore.java
deleted file mode 100644
index fe3f65404..000000000
--- a/assignments/week-3-assignment-2/src/SimpleSemaphore.java
+++ /dev/null
@@ -1,62 +0,0 @@
-import java.util.concurrent.locks.ReentrantLock;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.Condition;
-
-/**
- * @class SimpleSemaphore
- *
- * @brief This class provides a simple counting semaphore
- * implementation using Java a ReentrantLock and a
- * ConditionObject. It must implement both "Fair" and
- * "NonFair" semaphore semantics, just liked Java Semaphores.
- */
-public class SimpleSemaphore {
- /**
- * Constructor initialize the data members.
- */
- public SimpleSemaphore (int permits,
- boolean fair)
- {
- // TODO - you fill in here
- }
-
- /**
- * Acquire one permit from the semaphore in a manner that can
- * be interrupted.
- */
- public void acquire() throws InterruptedException {
- // TODO - you fill in here
- }
-
- /**
- * Acquire one permit from the semaphore in a manner that
- * cannot be interrupted.
- */
- public void acquireUninterruptibly() {
- // TODO - you fill in here
- }
-
- /**
- * Return one permit to the semaphore.
- */
- void release() {
- // TODO - you fill in here
- }
-
- /**
- * Define a ReentrantLock to protect the critical section.
- */
- // TODO - you fill in here
-
- /**
- * Define a ConditionObject to wait while the number of
- * permits is 0.
- */
- // TODO - you fill in here
-
- /**
- * Define a count of the number of available permits.
- */
- // TODO - you fill in here
-}
-
diff --git a/assignments/week-3-assignment-2/src/SimpleAtomicLong.java b/assignments/week-3-assignment-2/src/edu/vuum/mocca/SimpleAtomicLong.java
similarity index 58%
rename from assignments/week-3-assignment-2/src/SimpleAtomicLong.java
rename to assignments/week-3-assignment-2/src/edu/vuum/mocca/SimpleAtomicLong.java
index 17dab3db1..f57064f95 100644
--- a/assignments/week-3-assignment-2/src/SimpleAtomicLong.java
+++ b/assignments/week-3-assignment-2/src/edu/vuum/mocca/SimpleAtomicLong.java
@@ -1,5 +1,7 @@
// Import the necessary Java synchronization and scheduling classes.
+package edu.vuum.mocca;
+
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.Lock;
@@ -16,32 +18,27 @@ class SimpleAtomicLong
* The value that's manipulated atomically via the methods.
*/
private long mValue;
-
+
+
/**
* The ReentrantReadWriteLock used to serialize access to mValue.
*/
- // TODO - replace the null with the appropriate initialization:
- private ReentrantReadWriteLock mRWLock = null;
+ // TODO - add the implementation
/**
* Creates a new SimpleAtomicLong with the given initial value.
*/
- public SimpleAtomicLong(long initialValue)
- {
- long value = 0;
+ public SimpleAtomicLong(long initialValue) {
// TODO - you fill in here
}
/**
- * @brief Gets the current value.
+ * @brief Gets the current value
*
* @returns The current value
*/
- public long get()
- {
- long value = 0;
- // TODO - you fill in here, using a readLock()
- return value;
+ public long get() {
+ // TODO - you fill in here
}
/**
@@ -49,11 +46,8 @@ public long get()
*
* @returns the updated value
*/
- public long decrementAndGet()
- {
- long value = 0;
- // TODO - you fill in here, using a writeLock()
- return value;
+ public long decrementAndGet() {
+ // TODO - you fill in here
}
/**
@@ -61,11 +55,8 @@ public long decrementAndGet()
*
* @returns the previous value
*/
- public long getAndIncrement()
- {
- long value = 0;
- // TODO - you fill in here, using a writeLock()
- return value;
+ public long getAndIncrement() {
+ // TODO - you fill in here
}
/**
@@ -73,11 +64,8 @@ public long getAndIncrement()
*
* @returns the previous value
*/
- public long getAndDecrement()
- {
- long value = 0;
- // TODO - you fill in here, using a writeLock()
- return value;
+ public long getAndDecrement() {
+ // TODO - you fill in here
}
/**
@@ -85,11 +73,8 @@ public long getAndDecrement()
*
* @returns the updated value
*/
- public long incrementAndGet()
- {
- long value = 0;
- // TODO - you fill in here, using a writeLock()
- return value;
+ public long incrementAndGet() {
+ // TODO - you fill in here
}
}
diff --git a/assignments/week-3-assignment-2/src/edu/vuum/mocca/SimpleSemaphore.java b/assignments/week-3-assignment-2/src/edu/vuum/mocca/SimpleSemaphore.java
new file mode 100644
index 000000000..a867818ed
--- /dev/null
+++ b/assignments/week-3-assignment-2/src/edu/vuum/mocca/SimpleSemaphore.java
@@ -0,0 +1,70 @@
+package edu.vuum.mocca;
+
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.Condition;
+
+/**
+ * @class SimpleSemaphore
+ *
+ * @brief This class provides a simple counting semaphore
+ * implementation using Java a ReentrantLock and a
+ * ConditionObject (which is accessed via a Condition). It must
+ * implement both "Fair" and "NonFair" semaphore semantics,
+ * just liked Java Semaphores.
+ */
+public class SimpleSemaphore {
+ /**
+ * Define a Lock to protect the critical section.
+ */
+ // TODO - you fill in here
+
+ /**
+ * Define a Condition that waits while the number of permits is 0.
+ */
+ // TODO - you fill in here
+
+ /**
+ * Define a count of the number of available permits.
+ */
+ // TODO - you fill in here. Make sure that this data member will
+ // ensure its values aren't cached by multiple Threads..
+
+ public SimpleSemaphore(int permits, boolean fair) {
+ // TODO - you fill in here to initialize the SimpleSemaphore,
+ // making sure to allow both fair and non-fair Semaphore
+ // semantics.
+ }
+
+ /**
+ * Acquire one permit from the semaphore in a manner that can be
+ * interrupted.
+ */
+ public void acquire() throws InterruptedException {
+ // TODO - you fill in here.
+ }
+
+ /**
+ * Acquire one permit from the semaphore in a manner that cannot be
+ * interrupted.
+ */
+ public void acquireUninterruptibly() {
+ // TODO - you fill in here.
+ }
+
+ /**
+ * Return one permit to the semaphore.
+ */
+ public void release() {
+ // TODO - you fill in here.
+ }
+
+ /**
+ * Return the number of permits available.
+ */
+ public int availablePermits() {
+ // TODO - you fill in here by changing null to the appropriate
+ // return value.
+ return null;
+ }
+}
diff --git a/assignments/week-3-assignment-2/test/edu/vuum/mocca/AllTests.java b/assignments/week-3-assignment-2/test/edu/vuum/mocca/AllTests.java
new file mode 100644
index 000000000..325a23f41
--- /dev/null
+++ b/assignments/week-3-assignment-2/test/edu/vuum/mocca/AllTests.java
@@ -0,0 +1,12 @@
+package edu.vuum.mocca;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+
+@RunWith(Suite.class)
+@SuiteClasses({PalantirManagerUnitTest.class,
+ SimpleAtomicLongUnitTest.class,
+ SimpleSemaphoreUnitTest.class})
+public class AllTests {
+}
diff --git a/assignments/week-3-assignment-2/src/FairnessChecker.java b/assignments/week-3-assignment-2/test/edu/vuum/mocca/FairnessChecker.java
similarity index 68%
rename from assignments/week-3-assignment-2/src/FairnessChecker.java
rename to assignments/week-3-assignment-2/test/edu/vuum/mocca/FairnessChecker.java
index cf688c746..2d8e1ff6a 100644
--- a/assignments/week-3-assignment-2/src/FairnessChecker.java
+++ b/assignments/week-3-assignment-2/test/edu/vuum/mocca/FairnessChecker.java
@@ -1,3 +1,5 @@
+package edu.vuum.mocca;
+
import java.util.List;
import java.util.ArrayList;
@@ -5,7 +7,10 @@
* @class FairnessChecker
*
* @brief Class that attempts to check whether the SimpleSemaphore
- * implementation is "fair".
+ * implementation is "fair". It has inherent limitations, but
+ * to fix these limitations would require obtrusive
+ * instrumentation within the SimpleSemaphore implementation
+ * itself.
*/
public class FairnessChecker {
/**
@@ -17,7 +22,7 @@ public class FairnessChecker {
/**
* Initialize the FairnessChecker
*/
- public FairnessChecker(int totalEntries) {
+ public FairnessChecker(final int totalEntries) {
mEntryList = new ArrayList(totalEntries);
}
@@ -25,7 +30,7 @@ public FairnessChecker(int totalEntries) {
* Add the name of a Thread that's about to acquire the @code
* SimpleSemaphore. Assumes that Thread name are unique.
*/
- public synchronized void addNewThread(String entry) {
+ public synchronized void addNewThread(final String entry) {
mEntryList.add(entry);
}
@@ -33,7 +38,7 @@ public synchronized void addNewThread(String entry) {
* Returns true if the calling Thread's name is the same as the
* first item in the list, else false.
*/
- public synchronized boolean checkOrder(String entry) {
+ public synchronized boolean checkOrder(final String entry) {
String currentEntry = mEntryList.remove(0);
return currentEntry.equals(entry);
diff --git a/assignments/week-3-assignment-2/src/Palantir.java b/assignments/week-3-assignment-2/test/edu/vuum/mocca/Palantir.java
similarity index 93%
rename from assignments/week-3-assignment-2/src/Palantir.java
rename to assignments/week-3-assignment-2/test/edu/vuum/mocca/Palantir.java
index 023abad56..1ecd8d2bd 100644
--- a/assignments/week-3-assignment-2/src/Palantir.java
+++ b/assignments/week-3-assignment-2/test/edu/vuum/mocca/Palantir.java
@@ -1,3 +1,4 @@
+package edu.vuum.mocca;
/**
* @class Palantir
*
@@ -5,6 +6,7 @@
* Essentially plays the role of a "command" in the Command
* pattern.
*/
+
interface Palantir {
/**
* Gaze into the Palantir (and go into a tranc ;-)).
diff --git a/assignments/week-3-assignment-2/src/PalantirManager.java b/assignments/week-3-assignment-2/test/edu/vuum/mocca/PalantirManager.java
similarity index 92%
rename from assignments/week-3-assignment-2/src/PalantirManager.java
rename to assignments/week-3-assignment-2/test/edu/vuum/mocca/PalantirManager.java
index 5f7486f45..dedf2454a 100644
--- a/assignments/week-3-assignment-2/src/PalantirManager.java
+++ b/assignments/week-3-assignment-2/test/edu/vuum/mocca/PalantirManager.java
@@ -1,3 +1,5 @@
+package edu.vuum.mocca;
+
import java.util.List;
/**
@@ -34,7 +36,7 @@ public class PalantirManager {
* Create a resource manager for the palantiri passed as a
* parameter.
*/
- PalantirManager(List palantiri) {
+ PalantirManager(final List palantiri) {
mMaxPalantiri = palantiri.size();
mPalantiri = palantiri;
used = new boolean[palantiri.size()];
@@ -58,7 +60,7 @@ public Palantir acquirePalantir() {
* Returns the designated @code palantir so that it's
* available for others to use.
*/
- public void releasePalantir(Palantir palantir) {
+ public void releasePalantir(final Palantir palantir) {
if (markAsUnused(palantir))
mAvailable.release();
}
@@ -81,7 +83,7 @@ protected synchronized Palantir getNextAvailablePalantir() {
/**
* Return the @code palantir back to the resource pool.
*/
- protected synchronized boolean markAsUnused(Palantir palantir) {
+ protected synchronized boolean markAsUnused(final Palantir palantir) {
// Linear search is fine for this simple demo.
for (int i = 0; i < mMaxPalantiri; ++i) {
if (palantir == mPalantiri.get(i)) {
diff --git a/assignments/week-3-assignment-2/src/PalantirManagerTest.java b/assignments/week-3-assignment-2/test/edu/vuum/mocca/PalantirManagerUnitTest.java
similarity index 52%
rename from assignments/week-3-assignment-2/src/PalantirManagerTest.java
rename to assignments/week-3-assignment-2/test/edu/vuum/mocca/PalantirManagerUnitTest.java
index f4e5c485c..90edc858f 100644
--- a/assignments/week-3-assignment-2/src/PalantirManagerTest.java
+++ b/assignments/week-3-assignment-2/test/edu/vuum/mocca/PalantirManagerUnitTest.java
@@ -1,28 +1,51 @@
+package edu.vuum.mocca;
+
+import static org.junit.Assert.fail;
+import static org.junit.Assert.assertFalse;
+
+import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
-import java.util.ArrayList;
+
+import org.junit.Test;
/**
- * @class PalantirManagerTest
+ * @class PalantirManagerUnitTest
*
- * @brief This program tests student implementations of
- * SimpleSimpleAtomicLong and SimpleSemaphore, which are used
- * to correctly implement a resource manager that
- * limits the number of Beings from Middle-Earth who
- * can concurrently gaze into a Palantir (if you're not a Lord of the
- * Ring's fan see http://en.wikipedia.org/wiki/Palantir for more
- * information on Palantir).
+ * @brief This program tests that student implementations of
+ * SimpleAtomicLong and SimpleSemaphore correctly implement a
+ * resource manager that limits the number of Beings from
+ * Middle-Earth who can concurrently gaze into a Palantir (see
+ * http://en.wikipedia.org/wiki/Palantir for more information
+ * on Palantirs if you're not a Lord of the Ring's fan yet
+ * ;-)).
*/
-class PalantirManagerTest
-{
+public class PalantirManagerUnitTest {
+ /**
+ * If this is set to true in then lots of debugging output will be
+ * generated.
+ */
+ public static boolean diagnosticsEnabled = false;
+
/**
* Total number of times each Palantir user gets to gaze into a
* Palantir.
*/
final static int mMaxPalantirSessions = 5;
+ /**
+ * Total number of active Threads.
+ */
static volatile long mMaxActiveThreads = 0;
+ /**
+ * Keep track of whether a runtime exception occurs.
+ */
+ boolean mFailed = false;
+
+ /**
+ * Count of the number of Active Threads.
+ */
static SimpleAtomicLong mActiveThreads = new SimpleAtomicLong(0);
/**
@@ -36,8 +59,10 @@ class PalantirManagerTest
* implementation is "fair".
*/
static FairnessChecker mFairnessChecker = null;
-
- /* Runnable passed to each Thread that uses a Palantir. */
+
+ /**
+ * Runnable passed to each Thread that uses a Palantir.
+ */
static Runnable usePalantir = new Runnable() {
/**
* This is the main loop run by each Being of Middle-Earth
@@ -47,8 +72,9 @@ public void run() {
// Bound the total number of times that each user can
// gaze into a Palantir.
for (int i = 0; i < mMaxPalantirSessions; ++i) {
- System.out.println(Thread.currentThread().getName()
- + " is acquiring the palantir");
+ if (diagnosticsEnabled)
+ System.out.println(Thread.currentThread().getName()
+ + " is acquiring the palantir");
// Used to check for Semaphore fairness.
mFairnessChecker.addNewThread(Thread.currentThread().getName());
@@ -68,24 +94,27 @@ public void run() {
// the acquisition wasn't fair. we'll just give a
// warning (rather than an error) if it looks like
// the semaphore acquire() method isn't "fair".
- if (!mFairnessChecker.checkOrder(Thread.currentThread().getName()))
- System.out.println(Thread.currentThread().getName()
- + ": warning, semaphore acquire may not be fair");
+ if (!mFairnessChecker.checkOrder(Thread.currentThread()
+ .getName())) {
+ if (diagnosticsEnabled)
+ System.out.println(Thread.currentThread().getName()
+ + ": warning, semaphore acquire may not be fair");
+ }
// Ensure that the Semaphore implementation is
// correctly limiting the number of Palantir
// gazers.
long activeThreads = mActiveThreads.getAndIncrement();
- if (mMaxActiveThreads < activeThreads)
- {
- System.out.println("too many threads = "
- + activeThreads);
- throw new RuntimeException();
- }
- System.out.println(Thread.currentThread().getName()
- + " is starting to gaze at the "
- + palantir.name()
- + " palantir");
+ if (mMaxActiveThreads < activeThreads) {
+ System.out.println("too many threads = "
+ + activeThreads);
+ throw new RuntimeException();
+ }
+ if (diagnosticsEnabled)
+ System.out.println(Thread.currentThread().getName()
+ + " is starting to gaze at the "
+ + palantir.name()
+ + " palantir");
// Gaze at the Palantir for the time alloted in
// the command.
@@ -95,23 +124,25 @@ public void run() {
// Palantir.
mActiveThreads.decrementAndGet();
- System.out.println(Thread.currentThread().getName()
- + " is finished gazing at the "
- + palantir.name()
- + " palantir");
+ if (diagnosticsEnabled)
+ System.out.println(Thread.currentThread().getName()
+ + " is finished gazing at the "
+ + palantir.name()
+ + " palantir");
// Return the Palantir back to the shared pool so
// other Beings can gaze at it.
mPalantirManager.releasePalantir(palantir);
- System.out.println(Thread.currentThread().getName()
- + " is releasing the "
- + palantir.name() +
- " palantir");
+ if (diagnosticsEnabled)
+ System.out.println(Thread.currentThread().getName()
+ + " has released the "
+ + palantir.name()
+ + " palantir");
}
-
+
}
- };
+ };
/**
* This factory creates a list of Palantiri.
@@ -120,51 +151,57 @@ static List makePalantiri() {
List palantiri = new ArrayList();
// MinasTirith Palantir
- palantiri.add (new Palantir() {
- public void gaze() {
+ palantiri.add(new Palantir() {
+ public void gaze() {
try {
- Thread.sleep(10);
+ Thread.sleep(10);
+ } catch (InterruptedException e) {
}
- catch (InterruptedException e) {}
}
- public String name() {return "MinasTirith";}
+ public String name() {
+ return "MinasTirith";
+ }
});
// Orthanc Palantir
- palantiri.add (new Palantir() {
+ palantiri.add(new Palantir() {
public void gaze() {
try {
Thread.sleep(150);
+ } catch (InterruptedException e) {
}
- catch (InterruptedException e) {}
}
- public String name() {return "Orthanc";}
+ public String name() {
+ return "Orthanc";
+ }
});
// Barad-dur Palantir
- palantiri.add (new Palantir() {
- public void gaze()
- {
+ palantiri.add(new Palantir() {
+ public void gaze() {
try {
// The unblinking eye gazes for a long time
// ;-)
Thread.sleep(100);
+ } catch (InterruptedException e) {
}
- catch (InterruptedException e) {}
}
- public String name() {return "Barad-dur";}
+ public String name() {
+ return "Barad-dur";
+ }
});
return palantiri;
}
- /**
- * Main entry point method that runs the test.
- */
- public static void main(String[] args) {
+
+ @Test
+ public void testPalantirManager() {
try {
- System.out.println("Starting PalantirManagerTest");
+ if (diagnosticsEnabled)
+ System.out.println("Starting PalantirManagerTest");
// Get the list of available Palantiri.
- List palantiri = makePalantiri();
+ List palantiri =
+ PalantirManagerUnitTest.makePalantiri();
// Limit the number of users (threads) that can gaze into
// the available Palantiri.
@@ -189,23 +226,46 @@ public static void main(String[] args) {
// Start all the Threads that Middle-Earth Beings use to
// gaze into the Palantir.
- for (ListIterator iterator = palantirUsers.listIterator();
+ for (ListIterator iterator = palantirUsers.listIterator();
iterator.hasNext();
- )
- iterator.next().start();
+ ) {
+ Thread t = iterator.next();
+ // Catch runtime exceptions and induce a JUnit test
+ // failure.
+ t.setUncaughtExceptionHandler
+ (new Thread.UncaughtExceptionHandler() {
+ public void uncaughtException(Thread t,
+ Throwable e) {
+ System.out.println(t
+ + " throws exception: "
+ + e);
+ mFailed = true;
+ }
+ });
+ t.start();
+ }
// Barrier synchronization that waits for all the Threads
// to exit.
- for (ListIterator iterator = palantirUsers.listIterator();
+ for (ListIterator iterator = palantirUsers.listIterator();
iterator.hasNext();
)
iterator.next().join();
- System.out.println("Finishing PalantirManagerTest");
- }
- catch (Exception e) {
- System.out.println("Something went wrong");
+ // Make sure we haven't failed.
+ assertFalse(mFailed);
+
+ if (diagnosticsEnabled)
+ System.out.println("Finishing PalantirManagerTest");
+ } catch (Exception e) {
+ if (diagnosticsEnabled)
+ System.out.println("A "
+ + e.getMessage()
+ + " Exception was thrown");
+ fail("A "
+ + e.getMessage()
+ + " Exception was thrown");
}
}
-
+
}
diff --git a/assignments/week-3-assignment-2/test/edu/vuum/mocca/SimpleAtomicLongUnitTest.java b/assignments/week-3-assignment-2/test/edu/vuum/mocca/SimpleAtomicLongUnitTest.java
new file mode 100644
index 000000000..69895b8ac
--- /dev/null
+++ b/assignments/week-3-assignment-2/test/edu/vuum/mocca/SimpleAtomicLongUnitTest.java
@@ -0,0 +1,93 @@
+package edu.vuum.mocca;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Test;
+
+/**
+ * @class SimpleAtomicLongUnitTest
+ *
+ * @brief Simple unit test for the SimpleAtomicLong clas that ensures
+ * the version submitted for this assignment works correctly.
+ */
+public class SimpleAtomicLongUnitTest {
+ @Test
+ public void testSimpleAtomicLong() {
+ SimpleAtomicLong testLong = new SimpleAtomicLong(0);
+ assertNotNull(testLong);
+ }
+
+ @Test
+ public void testGet() {
+ SimpleAtomicLong testLong = new SimpleAtomicLong(0);
+ assertEquals(testLong.get(), 0);
+
+ SimpleAtomicLong testLong2 = new SimpleAtomicLong(100);
+ assertEquals(testLong2.get(), 100);
+
+ SimpleAtomicLong testLong3 = new SimpleAtomicLong(-100);
+ assertEquals(testLong3.get(), -100);
+ }
+
+ @Test
+ public void testDecrementAndGet() {
+ SimpleAtomicLong testLong = new SimpleAtomicLong(0);
+ assertEquals(testLong.decrementAndGet(), -1);
+ assertEquals(testLong.get(), -1);
+
+ SimpleAtomicLong testLong2 = new SimpleAtomicLong(100);
+ assertEquals(testLong2.decrementAndGet(), 99);
+ assertEquals(testLong2.get(), 99);
+
+ SimpleAtomicLong testLong3 = new SimpleAtomicLong(-100);
+ assertEquals(testLong3.decrementAndGet(), -101);
+ assertEquals(testLong3.get(), -101);
+ }
+
+ @Test
+ public void testIncrementAndGet() {
+ SimpleAtomicLong testLong = new SimpleAtomicLong(0);
+ assertEquals(testLong.incrementAndGet(), 1);
+ assertEquals(testLong.get(), 1);
+
+ SimpleAtomicLong testLong2 = new SimpleAtomicLong(100);
+ assertEquals(testLong2.incrementAndGet(), 101);
+ assertEquals(testLong2.get(), 101);
+
+ SimpleAtomicLong testLong3 = new SimpleAtomicLong(-100);
+ assertEquals(testLong3.incrementAndGet(), -99);
+ assertEquals(testLong3.get(), -99);
+ }
+
+ @Test
+ public void testGetAndIncrement() {
+ SimpleAtomicLong testLong = new SimpleAtomicLong(0);
+ assertEquals(testLong.getAndIncrement(), 0);
+ assertEquals(testLong.get(), 1);
+
+ SimpleAtomicLong testLong2 = new SimpleAtomicLong(100);
+ assertEquals(testLong2.getAndIncrement(), 100);
+ assertEquals(testLong2.get(), 101);
+
+ SimpleAtomicLong testLong3 = new SimpleAtomicLong(-100);
+ assertEquals(testLong3.getAndIncrement(), -100);
+ assertEquals(testLong3.get(), -99);
+ }
+
+ @Test
+ public void testGetAndDecrement() {
+ SimpleAtomicLong testLong = new SimpleAtomicLong(0);
+ assertEquals(testLong.getAndDecrement(), 0);
+ assertEquals(testLong.get(), -1);
+
+ SimpleAtomicLong testLong2 = new SimpleAtomicLong(100);
+ assertEquals(testLong2.getAndDecrement(), 100);
+ assertEquals(testLong2.get(), 99);
+
+ SimpleAtomicLong testLong3 = new SimpleAtomicLong(-100);
+ assertEquals(testLong3.getAndDecrement(), -100);
+ assertEquals(testLong3.get(), -101);
+ }
+
+}
diff --git a/assignments/week-3-assignment-2/test/edu/vuum/mocca/SimpleSemaphoreUnitTest.java b/assignments/week-3-assignment-2/test/edu/vuum/mocca/SimpleSemaphoreUnitTest.java
new file mode 100644
index 000000000..8cdd59390
--- /dev/null
+++ b/assignments/week-3-assignment-2/test/edu/vuum/mocca/SimpleSemaphoreUnitTest.java
@@ -0,0 +1,68 @@
+package edu.vuum.mocca;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.concurrent.Semaphore;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * @class SimpleSemaphoreUnitTest
+ *
+ * @brief Simple unit test for the SimpleSemaphore that just tests
+ * single-threaded logic.
+ */
+public class SimpleSemaphoreUnitTest {
+ @Test
+ public void testSimpleSemaphore() {
+ SimpleSemaphore simpleSemaphore = new SimpleSemaphore(2, true);
+ assertNotNull(simpleSemaphore);
+ }
+
+ @Test
+ public void testAcquire() throws InterruptedException {
+ SimpleSemaphore simpleSemaphore = new SimpleSemaphore(2, true);
+ assertEquals(simpleSemaphore.availablePermits(), 2);
+ simpleSemaphore.acquire();
+ assertEquals(simpleSemaphore.availablePermits(), 1);
+ simpleSemaphore.acquire();
+ assertEquals(simpleSemaphore.availablePermits(), 0);
+ }
+
+ @Test
+ public void testAcquireUninterruptibly() throws InterruptedException {
+ SimpleSemaphore simpleSemaphore = new SimpleSemaphore(2, true);
+ assertEquals(simpleSemaphore.availablePermits(), 2);
+ simpleSemaphore.acquireUninterruptibly();
+ assertEquals(simpleSemaphore.availablePermits(), 1);
+ simpleSemaphore.acquireUninterruptibly();
+ assertEquals(simpleSemaphore.availablePermits(), 0);
+ }
+
+ @Test
+ public void testRelease() throws InterruptedException {
+ SimpleSemaphore simpleSemaphore = new SimpleSemaphore(2, true);
+ assertEquals(simpleSemaphore.availablePermits(), 2);
+ simpleSemaphore.acquire();
+ assertEquals(simpleSemaphore.availablePermits(), 1);
+ simpleSemaphore.acquire();
+ assertEquals(simpleSemaphore.availablePermits(), 0);
+ simpleSemaphore.release();
+ assertEquals(simpleSemaphore.availablePermits(), 1);
+ simpleSemaphore.release();
+ assertEquals(simpleSemaphore.availablePermits(), 2);
+ }
+
+ @Test
+ public void testAvailablePermits() throws InterruptedException{
+ SimpleSemaphore simpleSemaphore = new SimpleSemaphore(2, true);
+ assertEquals(simpleSemaphore.availablePermits(), 2);
+ simpleSemaphore.acquire();
+ assertEquals(simpleSemaphore.availablePermits(), 1);
+ }
+}
diff --git a/assignments/week-4-assignment-3/.classpath b/assignments/week-4-assignment-3/.classpath
index 18d70f02c..b0e3db7ba 100644
--- a/assignments/week-4-assignment-3/.classpath
+++ b/assignments/week-4-assignment-3/.classpath
@@ -1,6 +1,8 @@
+
+
diff --git a/assignments/week-4-assignment-3/Assignment-Description.txt b/assignments/week-4-assignment-3/Assignment-Description.txt
index c6452c2ed..66cb926d8 100644
--- a/assignments/week-4-assignment-3/Assignment-Description.txt
+++ b/assignments/week-4-assignment-3/Assignment-Description.txt
@@ -1,11 +1,15 @@
-Programming Assignment 1
+Week 4: Programming Assignment 3
+
+Released Monday, June 2nd, 2014
+Due Monday, June 16th, 2014
In this assignment, you will implement a Java program that creates two
instances of the PlayPingPongThread and then starts these thread
instances to correctly alternate printing "Ping" and "Pong",
respectively, on the console display. This assignment also reuses the
-SimpleSemaphore you implemented for the previous assignment, so make
-sure it's working properly before attempting this assignment!
+SimpleSemaphore you implemented for the previous assignment, so please
+apply any fixes motivated by watching the Virtual Office Hours video
+of the instructor's solution(s).
In this directory you'll find the PingPongRight.java class, which
contains the skeleton Java code that you'll implement by completing
@@ -16,7 +20,9 @@ DO NOT CHANGE THE OVERALL STRUCTURE OF THE SKELETON - just fill in the
In particular, you'll need to do the following:
. Implement the SimpleSemaphore class, which you should replace with
- your working solution from the previous assignment.
+ your working solution from the previous assignment, after applying
+ any fixes motivated by watching the Virtual Office Hours video of
+ the instructor's solution(s).
. Use your SimpleSemaphore to implement the PlayPingPongThread class
and fix the problems with the earlier PingPongWrong solution, which
@@ -43,13 +49,19 @@ In particular, you'll need to do the following:
Section 1: Module 2: Part 1: Overview of Java Threads (Part 1)
Section 1: Module 2: Part 2: Overview of Java Threads (Part 2)
-Note, at the moment, all these videos are available in the YouTube
-playlist at
-
-https://www.youtube.com/playlist?list=PLZ9NgFYEMxp4tbiFYip6tDNIEBRUDyPQK
+. A more sophisticated example of a concurrent ping-pong program is
+ is shown in these videos:
-When the 2014 POSA MOOC officially starts these videos will be
-available at
+ Section 1: Module 2: Part 10: Java Synchronization and Scheduling Example (Part 1)
+ Section 1: Module 2: Part 11: Java Synchronization and Scheduling Example (Part 2)
+
+ This programming assignment isn't as sophisticated (e.g., it doesn't
+ implement a framework and doesn't use as many GoF patterns), but I
+ suggest you watch these videos to learn more about how binary
+ semaphores can be used to correctly alternate printing "ping" and
+ "pong" from two Java Threads.
+
+These videos are available at
https://class.coursera.org/posa-002/lecture
@@ -60,7 +72,7 @@ in item #38 at the POSA MOOC FAQ available from
http://www.courera.org/course/posa
To compile this code you can either use the provided Eclipse project
-or go into the src directory and simply type
+or go into the src/edu/vuum/mooca directory and simply type
% javac PingPongRight.java SimpleSemaphore.java
@@ -68,33 +80,45 @@ and to run the resulting class file simply type
% java PingPongRight
-The correct solution should look exactly like this:
+The correct solution when you run PingPongRight should look exactly
+like this:
Ready...Set...Go!
-Ping!(1)
-Pong!(1)
-Ping!(2)
-Pong!(2)
-Ping!(3)
-Pong!(3)
-Ping!(4)
-Pong!(4)
-Ping!(5)
-Pong!(5)
-Ping!(6)
-Pong!(6)
-Ping!(7)
-Pong!(7)
-Ping!(8)
-Pong!(8)
-Ping!(9)
-Pong!(9)
-Ping!(10)
-Pong!(10)
+Ping! (1)
+ Pong! (1)
+Ping! (2)
+ Pong! (2)
+Ping! (3)
+ Pong! (3)
+Ping! (4)
+ Pong! (4)
+Ping! (5)
+ Pong! (5)
+Ping! (6)
+ Pong! (6)
+Ping! (7)
+ Pong! (7)
+Ping! (8)
+ Pong! (8)
+Ping! (9)
+ Pong! (9)
+Ping! (10)
+ Pong! (10)
Done!
-
-
-
-
+There is also a Unit Test Suite that will run the PingPongRightTest
+file included in the test/edu/vuum/mooca directory for you at. When
+you first open the project in Eclipse, you might see compile errors if
+JUnit is not included in your build path. To fix these errors, open
+SynchronizedQueueTest.java, hover over "org.junit," and click "Fix
+project setup." Make sure "Add JUnit 4 library to the build path" is
+selected and then click "OK." At this point, the compile errors
+should disappear!
+
+Right click on the PingPongRightTest.java file in Eclipse and select
+'Run As' -> 'JUnit Test'. When the assignment is complete, the test
+should complete successfully. If the test passes a green-check mark
+will appear next to the test in the JUnit view. As long as this JUnit
+test "passes" successfully your program will be be consider "correct"
+for the purposes of assessing this assignment.
diff --git a/assignments/week-4-assignment-3/src/PingPongRight.java b/assignments/week-4-assignment-3/src/PingPongRight.java
deleted file mode 100644
index 53b8411d4..000000000
--- a/assignments/week-4-assignment-3/src/PingPongRight.java
+++ /dev/null
@@ -1,100 +0,0 @@
-// Import the necessary Java synchronization and scheduling classes.
-
-import java.util.concurrent.CountDownLatch;
-
-/**
- * @class PingPongRight
- *
- * @brief This class implements a Java program that creates two
- * instances of the PlayPingPongThread and start these thread
- * instances to correctly alternate printing "Ping" and "Pong",
- * respectively, on the console display.
- */
-public class PingPongRight {
- /**
- * Number of iterations to run the test program.
- */
- public static int mMaxIterations = 10;
-
- /**
- * Latch that will be decremented each time a thread exits.
- */
- public static CountDownLatch latch = null; // TODO - You fill in here
-
- /**
- * @class PlayPingPongThread
- *
- * @brief This class implements the ping/pong processing algorithm
- * using the SimpleSemaphore to alternate printing "ping"
- * and "pong" to the console display.
- */
- public static class PlayPingPongThread extends Thread
- {
- /**
- * Constructor initializes the data member.
- */
- public PlayPingPongThread (/* TODO - You fill in here */)
- {
- // TODO - You fill in here.
- }
-
- /**
- * Main event loop that runs in a separate thread of control
- * and performs the ping/pong algorithm using the
- * SimpleSemaphores.
- */
- public void run ()
- {
- // TODO - You fill in here.
- }
-
- /**
- * String to print (either "ping!" or "pong"!) for each
- * iteration.
- */
- // TODO - You fill in here.
-
- /**
- * The two SimpleSemaphores use to alternate pings and pongs.
- */
- // TODO - You fill in here.
- }
-
- /**
- * The main() entry point method into PingPongRight program.
- */
- public static void main(String[] args) {
- try {
- // Create the ping and pong SimpleSemaphores that control
- // alternation between threads.
-
- // TODO - You fill in here.
-
- System.out.println("Ready...Set...Go!");
-
- // Create the ping and pong threads, passing in the string
- // to print and the appropriate SimpleSemaphores.
- PlayPingPongThread ping =
- new PlayPingPongThread(/* TODO - You fill in here */);
- PlayPingPongThread pong =
- new PlayPingPongThread(/* TODO - You fill in here */);
-
- // Initiate the ping and pong threads, which will call the
- // run() hook method.
- ping.start();
- pong.start();
-
- // Use barrier synchronization to wait for both threads to
- // finish.
-
- // TODO - replace replace the following line with a
- // CountDownLatch barrier synchronizer call that waits for
- // both threads to finish.
- throw new java.lang.InterruptedException();
- }
- catch (java.lang.InterruptedException e)
- {}
-
- System.out.println("Done!");
- }
-}
diff --git a/assignments/week-4-assignment-3/src/SimpleSemaphore.java b/assignments/week-4-assignment-3/src/SimpleSemaphore.java
deleted file mode 100644
index d3c534373..000000000
--- a/assignments/week-4-assignment-3/src/SimpleSemaphore.java
+++ /dev/null
@@ -1,61 +0,0 @@
-import java.util.concurrent.locks.ReentrantLock;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.Condition;
-
-/**
- * @class SimpleSemaphore
- *
- * @brief This class provides a simple counting semaphore
- * implementation using Java a ReentrantLock and a
- * ConditionObject. It must implement both "Fair" and
- * "NonFair" semaphore semantics, just liked Java Semaphores.
- */
-public class SimpleSemaphore {
- /**
- * Constructor initialize the data members.
- */
- public SimpleSemaphore (int permits,
- boolean fair)
- {
- // TODO - you fill in here
- }
-
- /**
- * Acquire one permit from the semaphore in a manner that can
- * be interrupted.
- */
- public void acquire() throws InterruptedException {
- // TODO - you fill in here
- }
-
- /**
- * Acquire one permit from the semaphore in a manner that
- * cannot be interrupted.
- */
- public void acquireUninterruptibly() {
- // TODO - you fill in here
- }
-
- /**
- * Return one permit to the semaphore.
- */
- void release() {
- // TODO - you fill in here
- }
-
- /**
- * Define a ReentrantLock to protect the critical section.
- */
- // TODO - you fill in here
-
- /**
- * Define a ConditionObject to wait while the number of
- * permits is 0.
- */
- // TODO - you fill in here
-
- /**
- * Define a count of the number of available permits.
- */
- // TODO - you fill in here
-}
diff --git a/assignments/week-4-assignment-3/src/edu/vuum/mocca/PingPongRight.java b/assignments/week-4-assignment-3/src/edu/vuum/mocca/PingPongRight.java
new file mode 100644
index 000000000..4fd24024f
--- /dev/null
+++ b/assignments/week-4-assignment-3/src/edu/vuum/mocca/PingPongRight.java
@@ -0,0 +1,157 @@
+package edu.vuum.mocca;
+
+// Import the necessary Java synchronization and scheduling classes.
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * @class PingPongRight
+ *
+ * @brief This class implements a Java program that creates two
+ * instances of the PlayPingPongThread and start these thread
+ * instances to correctly alternate printing "Ping" and "Pong",
+ * respectively, on the console display.
+ */
+public class PingPongRight {
+ /**
+ * Number of iterations to run the test program.
+ */
+ public final static int mMaxIterations = 10;
+
+ /**
+ * Latch that will be decremented each time a thread exits.
+ */
+ public static CountDownLatch mLatch = null;
+
+ /**
+ * @class PlayPingPongThread
+ *
+ * @brief This class implements the ping/pong processing algorithm
+ * using the SimpleSemaphore to alternate printing "ping"
+ * and "pong" to the console display.
+ */
+ public static class PlayPingPongThread extends Thread {
+ /**
+ * Constants to distinguish between ping and pong
+ * SimpleSemaphores, if you choose to use an array of
+ * SimpleSemaphores. If you don't use this implementation
+ * feel free to remove these constants.
+ */
+ private final static int FIRST_SEMA = 0;
+ private final static int SECOND_SEMA = 1;
+
+ /**
+ * Maximum number of loop iterations.
+ */
+ private int mMaxLoopIterations = 0;
+
+ /**
+ * String to print (either "ping!" or "pong"!) for each
+ * iteration.
+ */
+ // TODO - You fill in here.
+
+ /**
+ * Two SimpleSemaphores use to alternate pings and pongs. You
+ * can use an array of SimpleSemaphores or just define them as
+ * two data members.
+ */
+ // TODO - You fill in here.
+
+ /**
+ * Constructor initializes the data member(s).
+ */
+ public PlayPingPongThread(String stringToPrint,
+ SimpleSemaphore semaphoreOne,
+ SimpleSemaphore semaphoreTwo,
+ int maxIterations) {
+ // TODO - You fill in here.
+ }
+
+ /**
+ * Main event loop that runs in a separate thread of control
+ * and performs the ping/pong algorithm using the
+ * SimpleSemaphores.
+ */
+ public void run() {
+ /**
+ * This method runs in a separate thread of control and
+ * implements the core ping/pong algorithm.
+ */
+
+ // TODO - You fill in here.
+ }
+
+ /**
+ * Method for acquiring the appropriate SimpleSemaphore.
+ */
+ private void acquire() {
+ // TODO fill in here
+ }
+
+ /**
+ * Method for releasing the appropriate SimpleSemaphore.
+ */
+ private void release() {
+ // TODO fill in here
+ }
+ }
+
+ /**
+ * The method that actually runs the ping/pong program.
+ */
+ public static void process(String startString,
+ String pingString,
+ String pongString,
+ String finishString,
+ int maxIterations) throws InterruptedException {
+
+ // TODO initialize this by replacing null with the appropriate
+ // constructor call.
+ mLatch = null;
+
+ // Create the ping and pong SimpleSemaphores that control
+ // alternation between threads.
+
+ // TODO - You fill in here, make pingSema start out unlocked.
+ SimpleSemaphore pingSema = null;
+ // TODO - You fill in here, make pongSema start out locked.
+ SimpleSemaphore pongSema = null;
+
+ System.out.println(startString);
+
+ // Create the ping and pong threads, passing in the string to
+ // print and the appropriate SimpleSemaphores.
+ PlayPingPongThread ping = new PlayPingPongThread(/*
+ * TODO - You fill in
+ * here
+ */);
+ PlayPingPongThread pong = new PlayPingPongThread(/*
+ * TODO - You fill in
+ * here
+ */);
+
+ // TODO - Initiate the ping and pong threads, which will call
+ // the run() hook method.
+
+ // TODO - replace the following line with a barrier
+ // synchronizer call to mLatch that waits for both threads to
+ // finish.
+ throw new java.lang.InterruptedException();
+
+ System.out.println(finishString);
+ }
+
+ /**
+ * The main() entry point method into PingPongRight program.
+ *
+ * @throws InterruptedException
+ */
+ public static void main(String[] args) throws InterruptedException {
+ process("Ready...Set...Go!",
+ "Ping! ",
+ " Pong! ",
+ "Done!",
+ mMaxIterations);
+ }
+}
+
diff --git a/assignments/week-4-assignment-3/src/edu/vuum/mocca/SimpleSemaphore.java b/assignments/week-4-assignment-3/src/edu/vuum/mocca/SimpleSemaphore.java
new file mode 100644
index 000000000..5e473adf6
--- /dev/null
+++ b/assignments/week-4-assignment-3/src/edu/vuum/mocca/SimpleSemaphore.java
@@ -0,0 +1,66 @@
+package edu.vuum.mocca;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * @class SimpleSemaphore
+ *
+ * @brief This class provides a simple counting semaphore implementation using
+ * Java a ReentrantLock and a ConditionObject (which is accessed via a
+ * Condition). It must implement both "Fair" and "NonFair" semaphore
+ * semantics, just liked Java Semaphores.
+ */
+public class SimpleSemaphore {
+ /**
+ * Define a ReentrantLock to protect the critical section.
+ */
+ // TODO - you fill in here
+
+ /**
+ * Define a Condition that waits while the number of permits is 0.
+ */
+ // TODO - you fill in here
+
+ /**
+ * Define a count of the number of available permits.
+ */
+ // TODO - you fill in here. Make sure that this data member will
+ // ensure its values aren't cached by multiple Threads..
+
+ public SimpleSemaphore(int permits, boolean fair) {
+ // TODO - you fill in here to initialize the SimpleSemaphore,
+ // making sure to allow both fair and non-fair Semaphore
+ // semantics.
+ }
+
+ /**
+ * Acquire one permit from the semaphore in a manner that can be
+ * interrupted.
+ */
+ public void acquire() throws InterruptedException {
+ // TODO - you fill in here.
+ }
+
+ /**
+ * Acquire one permit from the semaphore in a manner that cannot be
+ * interrupted.
+ */
+ public void acquireUninterruptibly() {
+ // TODO - you fill in here.
+ }
+
+ /**
+ * Return one permit to the semaphore.
+ */
+ void release() {
+ // TODO - you fill in here.
+ }
+
+ /**
+ * Return the number of permits available.
+ */
+ public int availablePermits() {
+ // TODO - you fill in here to return the correct result
+ return 0;
+ }
+}
diff --git a/assignments/week-4-assignment-3/test/edu/vuum/mocca/PingPongRightTest.java b/assignments/week-4-assignment-3/test/edu/vuum/mocca/PingPongRightTest.java
new file mode 100644
index 000000000..5148fba59
--- /dev/null
+++ b/assignments/week-4-assignment-3/test/edu/vuum/mocca/PingPongRightTest.java
@@ -0,0 +1,100 @@
+package edu.vuum.mocca;
+
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @class PingPongRightTest
+ *
+ * @brief This JUnit test checks the PingPong program to make sure it's working
+ * properly.
+ */
+public class PingPongRightTest {
+ /*
+ * These data members are used to capture [
+ * System.out.println() ] for testing.
+ */
+ private final ByteArrayOutputStream outContent =
+ new ByteArrayOutputStream();
+ private final ByteArrayOutputStream errContent =
+ new ByteArrayOutputStream();
+
+ /*
+ * Setup the output Capturing.
+ */
+ @Before
+ public void setUpStreams() {
+ System.setOut(new PrintStream(outContent));
+ System.setErr(new PrintStream(errContent));
+ }
+
+ /*
+ * Tear-down the output Capturing.
+ */
+ @After
+ public void cleanUpStreams() {
+ System.setOut(null);
+ System.setErr(null);
+ }
+
+ /*
+ * Test the process(...) logic/accuracy
+ *
+ * Gives some helpful error outputs for some simple error states.
+ */
+ @Test(timeout = 3000)
+ // This test will only run for 3000ms => 3 seconds, otherwise if fails
+ public void test() throws InterruptedException, IOException {
+ for (int i = 0; i < 10; i++) {
+ PingPongRight.process("start", "a", "b", "end", 10);
+ String outResults = outContent.toString();
+
+ if (outResults == null || outResults.length() == 0) {
+ fail("No output at all.");
+ }
+
+ //We need to split things up for Windows and UNIX due to
+ //differences in the way that newlines are handled.
+ boolean windowsTrue = outResults.equals(testResultWindows);
+ boolean unixTrue = outResults.equals(testResultUnix);
+ boolean pingAllFirstTrue = outResults.equals(pingAllFirst);
+ boolean pongAllFirstTrue = outResults.equals(pongAllFirst);
+
+ if (errContent.toString().length() != 0)
+ fail("There was error text.");
+
+ if (pingAllFirstTrue)
+ fail("Ping Thread completed before Pong started.");
+
+ if (pongAllFirstTrue)
+ fail("Pong Thread completed before Ping started.");
+
+ if (!(windowsTrue || unixTrue))
+ fail("Output was wrong.\n"
+ + "--- Received output ---\n"
+ + outResults
+ + "--- Expected output ---\n"
+ + testResultWindows);
+
+ outContent.reset();
+ }
+ }
+
+ // This is what should be output \n was replaced for visible
+ // endlines for inclusion into single line.
+ String testResultUnix =
+ "start\na(1)\nb(1)\na(2)\nb(2)\na(3)\nb(3)\na(4)\nb(4)\na(5)\nb(5)\na(6)\nb(6)\na(7)\nb(7)\na(8)\nb(8)\na(9)\nb(9)\na(10)\nb(10)\nend\n";
+ String testResultWindows =
+ "start\r\na(1)\r\nb(1)\r\na(2)\r\nb(2)\r\na(3)\r\nb(3)\r\na(4)\r\nb(4)\r\na(5)\r\nb(5)\r\na(6)\r\nb(6)\r\na(7)\r\nb(7)\r\na(8)\r\nb(8)\r\na(9)\r\nb(9)\r\na(10)\r\nb(10)\r\nend\r\n";
+ String pingAllFirst =
+ "start\r\na(1)\r\nb(1)\r\na(2)\r\na(3)\r\na(4)\r\na(5)\r\na(6)\r\na(7)\r\na(8)\r\na(9)\r\na(10)\r\nb(2)\r\nb(3)\r\nb(4)\r\nb(5)\r\nb(6)\r\nb(8)\r\nb(7)\r\nb(9)\r\nb(10)\r\nend\r\n";
+ String pongAllFirst =
+ "start\r\nb(1)\r\nb(2)\r\nb(3)\r\nb(4)\r\nb(5)\r\nb(6)\r\nb(8)\r\nb(7)\r\nb(9)\r\nb(10)\r\na(1)\r\na(2)\r\na(3)\r\na(4)\r\na(5)\r\na(6)\r\na(7)\r\na(8)\r\na(9)\r\na(10)\r\nend\r\n";
+}
diff --git a/assignments/week-5-assignment-4/Assignment-Description.txt b/assignments/week-5-assignment-4/Assignment-Description.txt
index cad036f73..066243159 100644
--- a/assignments/week-5-assignment-4/Assignment-Description.txt
+++ b/assignments/week-5-assignment-4/Assignment-Description.txt
@@ -1,21 +1,28 @@
-Programming Assignment 4
+Week 5: Programming Assignment 4
+
+Released Monday, June 9th, 2014
+Due Monday, June 23rd, 2014
In this assignment, you will implement a program that uses a
-pattern-oriented framework to spawn threads that correctly alternate
-printing "Ping" and "Pong", respectively on either the command-line
-display (of the Eclipse IDE) or in an Android TextView (in either the
-AVD emulator or on an actual Android device). In this directory
-you'll find Java source code, AndroidManifest.xml, and associated XML
-metadata files. The src/edu/vuum/mocca/ directory contains the
-skeleton Java code that you'll implement by completing the "TODO - You
-fill in here" comments to provide a working solution. DO NOT CHANGE
-THE OVERALL STRUCTURE OF THE SKELETON - just fill in the "TODO - You
-fill in here" portions!!!
-
-In particular, you'll need to do the following:
-
-. Read & understand the PingPongThreadSema PingPongThreadCond classes,
- which are covered in these videos:
+pattern-oriented ping-pong framework to spawn threads that correctly
+alternate printing "Ping" and "Pong", respectively on either the
+command-line display (of the Eclipse IDE) or in an Android TextView
+(in either the AVD emulator or on an actual Android device).
+
+In this directory you'll find Java source code, AndroidManifest.xml,
+and associated XML metadata files. The directory
+W5-A4-Android/src/edu/vuum/mocca/ contains the skeleton Java code that
+you'll implement by completing the "TODO - You fill in here" comments
+to provide a working solution. DO NOT CHANGE THE OVERALL STRUCTURE OF
+THE SKELETON - just fill in the "TODO - You fill in here" portions!!!
+
+Although there are a lot of files, the actual amount of code you need
+to write is small - the main goal is to understand the
+pattern-oriented ping-pong framework and how to apply the Android
+HaMeR framework. In particular, you'll need to do the following:
+
+. Read & understand the PingPongThreadSema and PingPongThreadCond
+ classes, which are covered in these videos:
Section 1: Module 2: Part 10: Java Synchronization and Schedule Example
@@ -29,8 +36,8 @@ In particular, you'll need to do the following:
. Implement the hook methods in the AndroidPlatformStrategy.java class
that use a Java CountDownLatch and Activity's runOnUiThread() method
- to post a Runnable command to the UI Thread, which are covered
- in these videos:
+ to post a Runnable command to the UI Thread, which are covered in
+ these videos:
Section 1: Module 2: Part 9: Java CountDownLatch
Section 1: Module 2: Part 10: Java Synchronization and Scheduling Example
@@ -44,13 +51,7 @@ In particular, you'll need to do the following:
Section 1: Module 3: Part 3: Overview of Android Handler
Section 1: Module 3: Part 4: Posting and Processing Runnables to Android Handler
-Note, at the moment, all these videos are available in the YouTube
-playlist at
-
-https://www.youtube.com/playlist?list=PLZ9NgFYEMxp4tbiFYip6tDNIEBRUDyPQK
-
-When the 2014 POSA MOOC officially starts these videos will be
-available at
+These videos are available at
https://class.coursera.org/posa-002/lecture
@@ -61,32 +62,43 @@ in item #38 at the POSA MOOC FAQ available from
http://www.courera.org/course/posa
You'll need to build this Android application in the Eclipse ADT
-environment. The correct solution should look like this (for both the
-Eclipse command-line and Android versions):
+environment. By default, the output for the correct solution should
+look similar to this (for both the Eclipse command-line and Android
+versions):
Ready...Set...Go!
-Ping!(1)
-Pong!(1)
-Ping!(2)
-Pong!(2)
-Ping!(3)
-Pong!(3)
-Ping!(4)
-Pong!(4)
-Ping!(5)
-Pong!(5)
-Ping!(6)
-Pong!(6)
-Ping!(7)
-Pong!(7)
-Ping!(8)
-Pong!(8)
-Ping!(9)
-Pong!(9)
-Ping!(10)
-Pong!(10)
+ping (1)
+_pong (1)
+ping (2)
+_pong (2)
+ping (3)
+_pong (3)
+ping (4)
+_pong (4)
+ping (5)
+_pong (5)
+ping (6)
+_pong (6)
+ping (7)
+_pong (7)
+ping (8)
+_pong (8)
+ping (9)
+_pong (9)
+ping (10)
+_pong (10)
Done!
+However, this assignment only requires you to create and test the
+Android version. BTW, the test assumes that each line of text is
+formatted: + '\n'
+
+To Run the Android Application, right click on the 'W5-A4-Android'
+project and select [Run As] -> [Android Application]
+
+To Run the Android Unit Test, right click on the 'W5-A4-Android-Test'
+project and select [Run As] -> [Android Unit Test]
+
diff --git a/assignments/week-5-assignment-4/W5-A4-Android-Test/.classpath b/assignments/week-5-assignment-4/W5-A4-Android-Test/.classpath
new file mode 100644
index 000000000..2bf50ad70
--- /dev/null
+++ b/assignments/week-5-assignment-4/W5-A4-Android-Test/.classpath
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/assignments/week-5-assignment-4/W5-A4-Android-Test/.project b/assignments/week-5-assignment-4/W5-A4-Android-Test/.project
new file mode 100644
index 000000000..3f10b9d9c
--- /dev/null
+++ b/assignments/week-5-assignment-4/W5-A4-Android-Test/.project
@@ -0,0 +1,33 @@
+
+
+ W5-A4-Android-Test
+
+
+
+
+
+ com.android.ide.eclipse.adt.ResourceManagerBuilder
+
+
+
+
+ com.android.ide.eclipse.adt.PreCompilerBuilder
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ com.android.ide.eclipse.adt.ApkBuilder
+
+
+
+
+
+ com.android.ide.eclipse.adt.AndroidNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/assignments/week-5-assignment-4/W5-A4-Android-Test/AndroidManifest.xml b/assignments/week-5-assignment-4/W5-A4-Android-Test/AndroidManifest.xml
new file mode 100644
index 000000000..a226ce2e0
--- /dev/null
+++ b/assignments/week-5-assignment-4/W5-A4-Android-Test/AndroidManifest.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/assignments/week-5-assignment-4/libs/android-support-v4.jar b/assignments/week-5-assignment-4/W5-A4-Android-Test/libs/android-support-v4.jar
similarity index 63%
rename from assignments/week-5-assignment-4/libs/android-support-v4.jar
rename to assignments/week-5-assignment-4/W5-A4-Android-Test/libs/android-support-v4.jar
index 96644edbe..9056828a0 100644
Binary files a/assignments/week-5-assignment-4/libs/android-support-v4.jar and b/assignments/week-5-assignment-4/W5-A4-Android-Test/libs/android-support-v4.jar differ
diff --git a/assignments/week-5-assignment-4/W5-A4-Android-Test/libs/robotium-solo-5.1.jar b/assignments/week-5-assignment-4/W5-A4-Android-Test/libs/robotium-solo-5.1.jar
new file mode 100644
index 000000000..1931dd97f
Binary files /dev/null and b/assignments/week-5-assignment-4/W5-A4-Android-Test/libs/robotium-solo-5.1.jar differ
diff --git a/assignments/week-5-assignment-4/proguard-project.txt b/assignments/week-5-assignment-4/W5-A4-Android-Test/proguard-project.txt
similarity index 100%
rename from assignments/week-5-assignment-4/proguard-project.txt
rename to assignments/week-5-assignment-4/W5-A4-Android-Test/proguard-project.txt
diff --git a/assignments/week-5-assignment-4/project.properties b/assignments/week-5-assignment-4/W5-A4-Android-Test/project.properties
similarity index 96%
rename from assignments/week-5-assignment-4/project.properties
rename to assignments/week-5-assignment-4/W5-A4-Android-Test/project.properties
index 4ab125693..a3ee5ab64 100644
--- a/assignments/week-5-assignment-4/project.properties
+++ b/assignments/week-5-assignment-4/W5-A4-Android-Test/project.properties
@@ -11,4 +11,4 @@
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# Project target.
-target=android-19
+target=android-17
diff --git a/assignments/week-5-assignment-4/W5-A4-Android-Test/res/drawable-hdpi/ic_launcher.png b/assignments/week-5-assignment-4/W5-A4-Android-Test/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 000000000..96a442e5b
Binary files /dev/null and b/assignments/week-5-assignment-4/W5-A4-Android-Test/res/drawable-hdpi/ic_launcher.png differ
diff --git a/assignments/week-5-assignment-4/W5-A4-Android-Test/res/drawable-mdpi/ic_launcher.png b/assignments/week-5-assignment-4/W5-A4-Android-Test/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 000000000..359047dfa
Binary files /dev/null and b/assignments/week-5-assignment-4/W5-A4-Android-Test/res/drawable-mdpi/ic_launcher.png differ
diff --git a/assignments/week-5-assignment-4/W5-A4-Android-Test/res/drawable-xhdpi/ic_launcher.png b/assignments/week-5-assignment-4/W5-A4-Android-Test/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..71c6d760f
Binary files /dev/null and b/assignments/week-5-assignment-4/W5-A4-Android-Test/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/assignments/week-5-assignment-4/res/values-v11/styles.xml b/assignments/week-5-assignment-4/W5-A4-Android-Test/res/values-v11/styles.xml
similarity index 100%
rename from assignments/week-5-assignment-4/res/values-v11/styles.xml
rename to assignments/week-5-assignment-4/W5-A4-Android-Test/res/values-v11/styles.xml
diff --git a/assignments/week-5-assignment-4/res/values-v14/styles.xml b/assignments/week-5-assignment-4/W5-A4-Android-Test/res/values-v14/styles.xml
similarity index 100%
rename from assignments/week-5-assignment-4/res/values-v14/styles.xml
rename to assignments/week-5-assignment-4/W5-A4-Android-Test/res/values-v14/styles.xml
diff --git a/assignments/week-5-assignment-4/W5-A4-Android-Test/res/values/strings.xml b/assignments/week-5-assignment-4/W5-A4-Android-Test/res/values/strings.xml
new file mode 100644
index 000000000..1711cecf9
--- /dev/null
+++ b/assignments/week-5-assignment-4/W5-A4-Android-Test/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+
+ W5-A4-Android-Test
+
+
diff --git a/assignments/week-5-assignment-4/res/values/styles.xml b/assignments/week-5-assignment-4/W5-A4-Android-Test/res/values/styles.xml
similarity index 100%
rename from assignments/week-5-assignment-4/res/values/styles.xml
rename to assignments/week-5-assignment-4/W5-A4-Android-Test/res/values/styles.xml
diff --git a/assignments/week-5-assignment-4/W5-A4-Android-Test/src/edu/vuum/mocca/test/PingPongActivityTest.java b/assignments/week-5-assignment-4/W5-A4-Android-Test/src/edu/vuum/mocca/test/PingPongActivityTest.java
new file mode 100644
index 000000000..dcab37447
--- /dev/null
+++ b/assignments/week-5-assignment-4/W5-A4-Android-Test/src/edu/vuum/mocca/test/PingPongActivityTest.java
@@ -0,0 +1,90 @@
+package edu.vuum.mocca.test;
+
+import android.content.Context;
+import android.test.ActivityInstrumentationTestCase2;
+import android.widget.Button;
+import android.widget.TextView;
+import android.view.WindowManager;
+
+import com.robotium.solo.Solo;
+
+import edu.vuum.mocca.PingPongActivity;
+
+/**
+ * @class PingPongActivityTest
+ *
+ * @brief Implements a Robotium test for the PingPongActivity.
+ */
+public class PingPongActivityTest
+ extends ActivityInstrumentationTestCase2 {
+ /**
+ * Constructor initializes the superclass.
+ */
+ public PingPongActivityTest() {
+ super(PingPongActivity.class);
+ }
+
+ /**
+ * This is the handle for Robotium, which allows us to interact
+ * with the UI.
+ */
+ Solo mSolo;
+
+ /**
+ * The context of this project, not the target project.
+ */
+ Context mContext;
+
+ Button playButton_;
+ TextView outputTextView_;
+
+ /**
+ * Called before each test is run to perform the initialization.
+ */
+ public void setUp() throws Exception {
+ super.setUp();
+
+ // Setup Robotium and get the EditText View.
+ mSolo = new Solo(getInstrumentation(), getActivity());
+
+ mContext = getInstrumentation().getContext();
+
+ playButton_ = (Button) mSolo.getView(edu.vuum.mocca.R.id.play_button);
+
+ outputTextView_ = (TextView) mSolo
+ .getView(edu.vuum.mocca.R.id.pingpong_output);
+
+ // Prevent lockscreen from preventing test.
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+ }
+ });
+
+ getInstrumentation().callActivityOnStart(getActivity());
+ getInstrumentation().callActivityOnResume(getActivity());
+ }
+
+ /**
+ * No need for @Test because of use of
+ * ActivityInstrumentationTestCase2 which makes each method in
+ * this class a @Test method TODO find better explanation, 100%
+ * proper.
+ */
+ public void testPlayPingPongButtonPress() throws Exception {
+ Thread.sleep(TestOptions.ACCEPTABLE_STARTUP_LENGTH);
+
+ // Asserttrue(outputTextView_.isAttachedToWindow() == true);
+ assertTrue(outputTextView_.getText().length() == 0);
+
+ // Click on the 'play' button
+ mSolo.clickOnView(mSolo.getView(edu.vuum.mocca.R.id.play_button));
+
+ // wait for the threads to execute
+ Thread.sleep(TestOptions.ACCEPTABLE_RUNTIME_LENGTH);
+
+ assertTrue(outputTextView_.getText().toString()
+ .equals(TestOptions.ANDROID_TEXTVIEW));
+ }
+}
diff --git a/assignments/week-5-assignment-4/W5-A4-Android-Test/src/edu/vuum/mocca/test/TestOptions.java b/assignments/week-5-assignment-4/W5-A4-Android-Test/src/edu/vuum/mocca/test/TestOptions.java
new file mode 100644
index 000000000..fb1fb4c55
--- /dev/null
+++ b/assignments/week-5-assignment-4/W5-A4-Android-Test/src/edu/vuum/mocca/test/TestOptions.java
@@ -0,0 +1,38 @@
+package edu.vuum.mocca.test;
+
+/**
+ * @class Options
+ *
+ * @brief Holds global constants for the testing suite. More convenient than
+ * grabbing resources from /res and trying to finagle a working Context
+ * out of the test classes every time we want to use TEST_URI.
+ *
+ */
+public class TestOptions {
+ /**
+ * Time we should wait for things to run.
+ */
+ static final long ACCEPTABLE_STARTUP_LENGTH = 1000;
+
+ /**
+ * Time we should wait for things to run.
+ */
+ static final long ACCEPTABLE_RUNTIME_LENGTH = 2000;
+
+ /**
+ * Time we should wait for things to download.
+ */
+ static final long LONG_WAIT_TIME = 5000;
+
+ /**
+ * Various test strings.
+ */
+ static final String JAVA_CONSOLE_UNIX = "Ready...Set...Go!\nping (1)\n_pong (1)\nping (2)\n_pong (2)\nping (3)\n_pong (3)\nping (4)\n_pong (4)\nping (5)\n_pong (5)\nping (6)\n_pong (6)\nping (7)\n_pong (7)\nping (8)\n_pong (8)\nping (9)\n_pong (9)\nping (10)\n_pong (10)\nDone!\n";
+ static final String PING_ALL_FIRST_UNIX = "Ready...Set...Go!\nping (1)\nping (2)\nping (3)\nping (4)\nping (5)\nping (6)\nping (7)\nping (8)\nping (9)\nping (10)\n_pong (1)\n_pong (2)\n_pong (3)\n_pong (4)\n_pong (5)\n_pong (6)\n_pong (7)\n_pong (8)\n_pong (9)\n_pong (10)\nDone!\n";
+ static final String PING_ALL_FIRST_WINDOWS = "Ready...Set...Go!\r\nping (1)\r\nping (2)\r\nping (3)\r\nping (4)\r\nping (5)\r\nping (6)\r\nping (7)\r\nping (8)\r\nping (9)\r\nping (10)\r\n_pong (1)\r\n_pong (2)\r\n_pong (3)\r\n_pong (4)\r\n_pong (5)\r\n_pong (6)\r\n_pong (7)\r\n_pong (8)\r\n_pong (9)\r\n_pong (10)\r\nDone!\r\n";
+ static final String PONG_ALL_FIRST_WINDOWS = "Ready...Set...Go!\r\n_pong (1)\r\n_pong (2)\r\n_pong (3)\r\n_pong (4)\r\n_pong (5)\r\n_pong (6)\r\n_pong (7)\r\n_pong (8)\r\n_pong (9)\r\n_pong (10)\r\nping (1)\r\nping (2)\r\nping (3)\r\nping (4)\r\nping (5)\r\nping (6)\r\nping (7)\r\nping (8)\r\nping (9)\r\nping (10)\r\nDone!";
+ static final String PONG_ALL_FIRST_UNIX = "Ready...Set...Go!\n_pong (1)\n_pong (2)\n_pong (3)\n_pong (4)\n_pong (5)\n_pong (6)\n_pong (7)\n_pong (8)\n_pong (9)\n_pong (10)\nping (1)\nping (2)\nping (3)\nping (4)\nping (5)\nping (6)\nping (7)\nping (8)\nping (9)\nping (10)\nDone!";
+ static final String JAVA_CONSOLE_WINDOWS = "Ready...Set...Go!\r\nping (1)\r\n_pong (1)\r\nping (2)\r\n_pong (2)\r\nping (3)\r\n_pong (3)\r\nping (4)\r\n_pong (4)\r\nping (5)\r\n_pong (5)\r\nping (6)\r\n_pong (6)\r\nping (7)\r\n_pong (7)\r\nping (8)\r\n_pong (8)\r\nping (9)\r\n_pong (9)\r\nping (10)\r\n_pong (10)\r\nDone!";
+ static final String ANDROID_TEXTVIEW = "Ready...Set...Go!\nping (1)\n_pong (1)\nping (2)\n_pong (2)\nping (3)\n_pong (3)\nping (4)\n_pong (4)\nping (5)\n_pong (5)\nping (6)\n_pong (6)\nping (7)\n_pong (7)\nping (8)\n_pong (8)\nping (9)\n_pong (9)\nping (10)\n_pong (10)\nDone!\n";
+
+}
diff --git a/assignments/week-5-assignment-4/W5-A4-Android/.classpath b/assignments/week-5-assignment-4/W5-A4-Android/.classpath
new file mode 100644
index 000000000..51769745b
--- /dev/null
+++ b/assignments/week-5-assignment-4/W5-A4-Android/.classpath
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/ex/ThreadedDownload/.project b/assignments/week-5-assignment-4/W5-A4-Android/.project
similarity index 96%
rename from ex/ThreadedDownload/.project
rename to assignments/week-5-assignment-4/W5-A4-Android/.project
index 55858c982..5aad4ab5c 100644
--- a/ex/ThreadedDownload/.project
+++ b/assignments/week-5-assignment-4/W5-A4-Android/.project
@@ -1,6 +1,6 @@
- ThreadedDownloads
+ W5-A4-Android
diff --git a/assignments/week-5-assignment-4/AndroidManifest.xml b/assignments/week-5-assignment-4/W5-A4-Android/AndroidManifest.xml
similarity index 86%
rename from assignments/week-5-assignment-4/AndroidManifest.xml
rename to assignments/week-5-assignment-4/W5-A4-Android/AndroidManifest.xml
index b7a50564e..949fedabf 100644
--- a/assignments/week-5-assignment-4/AndroidManifest.xml
+++ b/assignments/week-5-assignment-4/W5-A4-Android/AndroidManifest.xml
@@ -1,12 +1,11 @@
-
+ android:minSdkVersion="14"
+ android:targetSdkVersion="19" />
+
+
+
+
+
diff --git a/assignments/week-5-assignment-4/W5-A4-Android/res/values-v14/styles.xml b/assignments/week-5-assignment-4/W5-A4-Android/res/values-v14/styles.xml
new file mode 100644
index 000000000..a91fd0372
--- /dev/null
+++ b/assignments/week-5-assignment-4/W5-A4-Android/res/values-v14/styles.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
diff --git a/assignments/week-5-assignment-4/res/values/dimens.xml b/assignments/week-5-assignment-4/W5-A4-Android/res/values/dimens.xml
similarity index 100%
rename from assignments/week-5-assignment-4/res/values/dimens.xml
rename to assignments/week-5-assignment-4/W5-A4-Android/res/values/dimens.xml
diff --git a/assignments/week-5-assignment-4/res/values/strings.xml b/assignments/week-5-assignment-4/W5-A4-Android/res/values/strings.xml
similarity index 71%
rename from assignments/week-5-assignment-4/res/values/strings.xml
rename to assignments/week-5-assignment-4/W5-A4-Android/res/values/strings.xml
index 0beb6a933..cd5196b9a 100644
--- a/assignments/week-5-assignment-4/res/values/strings.xml
+++ b/assignments/week-5-assignment-4/W5-A4-Android/res/values/strings.xml
@@ -1,8 +1,9 @@
-
- PingPongFinal
+
+ PlayPingPongSettingsPlay PingPong!Reset Game
+
diff --git a/assignments/week-5-assignment-4/W5-A4-Android/res/values/styles.xml b/assignments/week-5-assignment-4/W5-A4-Android/res/values/styles.xml
new file mode 100644
index 000000000..6ce89c7ba
--- /dev/null
+++ b/assignments/week-5-assignment-4/W5-A4-Android/res/values/styles.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
diff --git a/assignments/week-5-assignment-4/src/edu/vuum/mocca/AndroidPlatformStrategy.java b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/AndroidPlatformStrategy.java
similarity index 91%
rename from assignments/week-5-assignment-4/src/edu/vuum/mocca/AndroidPlatformStrategy.java
rename to assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/AndroidPlatformStrategy.java
index 83dc2a05d..bc684b5ce 100644
--- a/assignments/week-5-assignment-4/src/edu/vuum/mocca/AndroidPlatformStrategy.java
+++ b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/AndroidPlatformStrategy.java
@@ -45,7 +45,7 @@ public AndroidPlatformStrategy(Object output,
/** Do any initialization needed to start a new game. */
public void begin()
{
- /** Reset the CountDownLatch. */
+ /** (Re)initialize the CountDownLatch. */
// TODO - You fill in here.
}
@@ -71,12 +71,6 @@ public void awaitDone()
// TODO - You fill in here.
}
- /** Returns the platform name in a String. */
- public String platformName()
- {
- return System.getProperty("java.specification.vendor");
- }
-
/**
* Error log formats the message and displays it for the
* debugging purposes.
@@ -86,4 +80,3 @@ public void errorLog(String javaFile, String errorMessage)
Log.e(javaFile, errorMessage);
}
}
-
diff --git a/assignments/week-5-assignment-4/src/edu/vuum/mocca/ConsolePlatformStrategy.java b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/ConsolePlatformStrategy.java
similarity index 78%
rename from assignments/week-5-assignment-4/src/edu/vuum/mocca/ConsolePlatformStrategy.java
rename to assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/ConsolePlatformStrategy.java
index c67f981ca..c83233f16 100644
--- a/assignments/week-5-assignment-4/src/edu/vuum/mocca/ConsolePlatformStrategy.java
+++ b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/ConsolePlatformStrategy.java
@@ -17,13 +17,13 @@ public class ConsolePlatformStrategy extends PlatformStrategy
* Latch to decrement each time a thread exits to control when the
* play() method returns.
*/
- private static CountDownLatch mLatch = new CountDownLatch(2);
+ private static CountDownLatch mLatch = null;
/** Contains information for outputting to console window. */
PrintStream mOutput;
/** Ctor. */
- ConsolePlatformStrategy(Object output)
+ public ConsolePlatformStrategy(Object output)
{
mOutput = (PrintStream) output;
}
@@ -31,14 +31,14 @@ public class ConsolePlatformStrategy extends PlatformStrategy
/** Do any initialization needed to start a new game. */
public void begin()
{
- mLatch = new CountDownLatch(2);
+ mLatch = new CountDownLatch(NUMBER_OF_THREADS);
}
/** Print the outputString to the display. */
public void print(String outputString)
{
/** Print to the console window. */
- System.out.println(outputString);
+ mOutput.println(outputString);
}
/** Indicate that a game thread has finished running. */
@@ -56,18 +56,13 @@ public void awaitDone()
}
}
- /** Returns a string revealing the platform in use. */
- public String platformName()
- {
- return System.getProperty("java.specification.vendor");
- }
-
/**
* Error log formats the message and displays it for the debugging
* purposes.
*/
public void errorLog(String javaFile, String errorMessage)
{
- System.out.println(javaFile + " " + errorMessage);
+ mOutput.println(javaFile + " " + errorMessage);
}
}
+
diff --git a/ex/PingPong/console/src/edu/vuum/mocca/Main.java b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/Main.java
similarity index 96%
rename from ex/PingPong/console/src/edu/vuum/mocca/Main.java
rename to assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/Main.java
index 9026c80e6..03533c545 100644
--- a/ex/PingPong/console/src/edu/vuum/mocca/Main.java
+++ b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/Main.java
@@ -12,7 +12,7 @@ public class Main
* The Java virtual machine requires the instantiation of a main
* method to run the console version of the PlayPingPong app.
*/
- public static void main(String[] args)
+ public static void main(String[] args)
{
/**
* Initializes the Platform singleton with the appropriate
diff --git a/assignments/week-5-assignment-4/src/edu/vuum/mocca/Options.java b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/Options.java
similarity index 79%
rename from assignments/week-5-assignment-4/src/edu/vuum/mocca/Options.java
rename to assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/Options.java
index 11d85015f..f07a88350 100644
--- a/assignments/week-5-assignment-4/src/edu/vuum/mocca/Options.java
+++ b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/Options.java
@@ -1,9 +1,5 @@
package edu.vuum.mocca;
-import java.io.File;
-import java.nio.*;
-import java.util.*;
-
/**
* @class Options
*
@@ -62,18 +58,20 @@ public String syncMechanism()
*/
public boolean parseArgs(String argv[])
{
- for (int argc = 0; argc < argv.length; argc += 2)
- if (argv[argc].equals("-i"))
- mMaxIterations = Integer.parseInt(argv[argc + 1]);
- else if (argv[argc].equals("-s"))
- mSyncMechanism = argv[argc + 1];
- else if (argv[argc].equals("-t"))
- mMaxTurns = Integer.parseInt(argv[argc + 1]);
- else
- {
- printUsage();
- return false;
- }
+ if (argv != null) {
+ for (int argc = 0; argc < argv.length; argc += 2)
+ if (argv[argc].equals("-i"))
+ mMaxIterations = Integer.parseInt(argv[argc + 1]);
+ else if (argv[argc].equals("-s"))
+ mSyncMechanism = argv[argc + 1];
+ else if (argv[argc].equals("-t"))
+ mMaxTurns = Integer.parseInt(argv[argc + 1]);
+ else
+ {
+ printUsage();
+ return false;
+ }
+ }
return true;
}
diff --git a/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PingPongActivity.java b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PingPongActivity.java
new file mode 100644
index 000000000..3d283d399
--- /dev/null
+++ b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PingPongActivity.java
@@ -0,0 +1,76 @@
+package edu.vuum.mocca;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+/**
+ * @class MainActivity
+ *
+ * @brief Initial start up screen for the android GUI.
+ */
+public class PingPongActivity extends Activity {
+ /** TextView that PingPong will be "played" upon */
+ private TextView mAndroidPingPongOutput;
+
+ /** Button that allows playing and resetting of the game */
+ private Button mPlayButton;
+
+ /** Variables to track state of the game */
+ private static int PLAY = 0;
+ private static int RESET = 1;
+ private int mGameState = PLAY;
+
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Sets the content view to the xml file, activity_ping_pong.
+ setContentView(R.layout.activity_ping_pong);
+ mAndroidPingPongOutput =
+ (TextView) findViewById(R.id.pingpong_output);
+ mPlayButton = (Button) findViewById(R.id.play_button);
+
+ // Initializes the Platform singleton with the appropriate
+ // Platform strategy, which in this case will be the
+ // AndroidPlatform.
+ PlatformStrategy.instance
+ (new PlatformStrategyFactory
+ (mAndroidPingPongOutput,
+ this).makePlatformStrategy());
+
+ // Initializes the Options singleton.
+ Options.instance().parseArgs(null);
+ }
+
+ /** Sets the action of the button on click state. */
+ public void playButtonClicked(View view) {
+ if (mGameState == PLAY) {
+ // Use a factory method to create the appropriate type of
+ // OutputStrategy.
+ PlayPingPong pingPong =
+ new PlayPingPong(PlatformStrategy.instance(),
+ Options.instance().maxIterations(),
+ Options.instance().maxTurns(),
+ Options.instance().syncMechanism());
+
+ // Play ping-pong with the designated number of
+ // iterations.
+ new Thread(pingPong).start();
+ mPlayButton.setText(R.string.reset_button);
+ mGameState = RESET;
+ } else if (mGameState == RESET) {
+
+ // Empty TextView and prepare the UI to play another game.
+ mAndroidPingPongOutput.setText(R.string.empty_string);
+ mPlayButton.setText(R.string.play_button);
+ mGameState = PLAY;
+ } else {
+ // Notify the player that something has gone wrong and
+ // reset.
+ mAndroidPingPongOutput.setText("Unknown State entered!");
+ mGameState = RESET;
+ }
+ }
+}
diff --git a/assignments/week-5-assignment-4/src/edu/vuum/mocca/PlatformStrategy.java b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PlatformStrategy.java
similarity index 87%
rename from assignments/week-5-assignment-4/src/edu/vuum/mocca/PlatformStrategy.java
rename to assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PlatformStrategy.java
index 1280920f2..8ae0fadef 100644
--- a/assignments/week-5-assignment-4/src/edu/vuum/mocca/PlatformStrategy.java
+++ b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PlatformStrategy.java
@@ -12,6 +12,9 @@
*/
public abstract class PlatformStrategy
{
+ /** Number of threads used to play ping-pong. */
+ protected static final int NUMBER_OF_THREADS = 2;
+
/** The singleton @a PlatformStrategy instance. */
private static PlatformStrategy mUniqueInstance = null;
@@ -42,11 +45,13 @@ public static PlatformStrategy instance(PlatformStrategy platform)
/** Barrier that waits for all the game threads to finish. */
public abstract void awaitDone();
- /**
+ /**
* Returns the name of the platform in a string. e.g., Android or
* a JVM.
+ * @deprecated This method is just here for backwards
+ * compatibility with the skeletons.
*/
- public abstract String platformName();
+ public String platformName() { return ""; }
/**
* Error log formats the message and displays it for the debugging
diff --git a/assignments/week-5-assignment-4/src/edu/vuum/mocca/PlatformStrategyFactory.java b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PlatformStrategyFactory.java
similarity index 68%
rename from assignments/week-5-assignment-4/src/edu/vuum/mocca/PlatformStrategyFactory.java
rename to assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PlatformStrategyFactory.java
index a655f6a18..95d6bb962 100644
--- a/assignments/week-5-assignment-4/src/edu/vuum/mocca/PlatformStrategyFactory.java
+++ b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PlatformStrategyFactory.java
@@ -11,21 +11,29 @@
public class PlatformStrategyFactory
{
/**
- * This interface uses the Strategy pattern to create @a PlatformStrategy
- * implementations at runtime.
+ * This interface uses the Strategy pattern to create @a
+ * PlatformStrategy implementations at runtime.
*/
private static interface IPlatformStrategyFactoryStrategy
{
public PlatformStrategy execute();
}
+ /**
+ * Enumeration distinguishing platforms Android from plain ol' Java.
+ */
+ public enum PlatformType {
+ ANDROID,
+ PLAIN_JAVA
+ }
+
/**
* HashMap used to map strings containing the Java platform names
* and dispatch the execute() method of the associated @a PlatformStrategy
* implementation.
*/
- private HashMap mPlatformStrategyMap =
- new HashMap();
+ private HashMap mPlatformStrategyMap =
+ new HashMap();
/**
* Ctor that stores the objects that perform output for a
@@ -39,7 +47,7 @@ public PlatformStrategyFactory(final Object output,
* The "The Android Project" string maps to a command object
* that creates an @a AndroidPlatformStrategy implementation.
*/
- mPlatformStrategyMap.put("The Android Project",
+ mPlatformStrategyMap.put(PlatformType.ANDROID,
new IPlatformStrategyFactoryStrategy()
{
/**
@@ -58,7 +66,7 @@ public PlatformStrategy execute()
* The "Sun Microsystems Inc." string maps to a command object
* that creates an @a ConsolePlatformStrategy implementation.
*/
- mPlatformStrategyMap.put("Sun Microsystems Inc.",
+ mPlatformStrategyMap.put(PlatformType.PLAIN_JAVA,
new IPlatformStrategyFactoryStrategy()
{
public PlatformStrategy execute()
@@ -66,19 +74,26 @@ public PlatformStrategy execute()
return new ConsolePlatformStrategy(output);
}
});
+ }
- /**
- * The "Oracle Corporation" string maps to a command object
- * that creates an @a ConsolePlatformStrategy implementation.
- */
- mPlatformStrategyMap.put("Oracle Corporation",
- new IPlatformStrategyFactoryStrategy()
- {
- public PlatformStrategy execute()
- {
- return new ConsolePlatformStrategy(output);
- }
- });
+ /**
+ * Returns the name of the platform in a string. e.g., Android or
+ * a JVM.
+ */
+ public static String platformName()
+ {
+ return System.getProperty("java.specification.vendor");
+ }
+
+ /**
+ * Returns the type of the platformm e.g. Android or
+ * a JVM.
+ */
+ public static PlatformType platformType() {
+ if(platformName().indexOf("Android") >= 0)
+ return PlatformType.ANDROID;
+ else
+ return PlatformType.PLAIN_JAVA;
}
/**
@@ -87,8 +102,8 @@ public PlatformStrategy execute()
*/
public PlatformStrategy makePlatformStrategy()
{
- String name = System.getProperty("java.specification.vendor");
+ PlatformType type = platformType();
- return mPlatformStrategyMap.get(name).execute();
+ return mPlatformStrategyMap.get(type).execute();
}
}
diff --git a/ex/PingPong/console/src/edu/vuum/mocca/PlayPingPong.java b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PlayPingPong.java
similarity index 62%
rename from ex/PingPong/console/src/edu/vuum/mocca/PlayPingPong.java
rename to assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PlayPingPong.java
index 2e18703c7..e3111342a 100644
--- a/ex/PingPong/console/src/edu/vuum/mocca/PlayPingPong.java
+++ b/assignments/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PlayPingPong.java
@@ -3,66 +3,61 @@
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
-import java.util.concurrent.CountDownLatch;
/**
* @class PlayPingPong
- *
- * @brief This class implements a Java program that creates two
- * threads, Ping and Pong, to alternately print "Ping" and
- * "Pong", respectively, on the display. It uses the Template
- * Method, Strategy, and Factory Method patterns to factor out
- * common code and simplify the program design.
+ *
+ * @brief This class implements a Java program that creates two threads, Ping
+ * and Pong, to alternately print "Ping" and "Pong", respectively, on the
+ * display. It uses the Template Method, Strategy, and Factory Method
+ * patterns to factor out common code and simplify the program design.
*/
-public class PlayPingPong implements Runnable
-{
+public class PlayPingPong implements Runnable {
/**
* Number of iterations to ping/pong.
*/
private static volatile int mMaxIterations;
/** Maximum number of iterations per "turn" (defaults to 1). */
- private static volatile int mMaxTurns = 1;
+ private static int mMaxTurns = 1;
/**
- * Keeps track of the platform that we're running on, e.g.,
- * Android vs. Console.
+ * Keeps track of the platform that we're running on, e.g., Android vs.
+ * Console.
*/
private static volatile PlatformStrategy mPlatformStrategy;
/**
- * Which synchronization to use, e.g., "SEMA" vs. "COND".
- * Defaults to "SEMA".
+ * Which synchronization to use, e.g., "SEMA" vs. "COND". Defaults to
+ * "SEMA".
*/
private static String mSyncMechanism = "SEMA";
-
+
/**
- * Constants used to distinguish between ping and pong threads.
+ * Constants used to distinguish between ping and pong threads.
*/
private final static int PING_THREAD = 0;
private final static int PONG_THREAD = 1;
/**
* @Brief PingPongThread
- *
+ *
* @class This class implements the core ping/pong algorithm, but
- * defers the scheduling aspect to subclasses. It plays
- * the role of the "Abstract Class" in the Template Method
+ * defers the scheduling aspect to subclasses. It plays the
+ * role of the "Abstract Class" in the Template Method
* pattern.
*/
- static abstract class PingPongThread extends Thread
- {
- /**
+ static abstract class PingPongThread extends Thread {
+ /**
* Constructor initializes the various fields.
*/
- PingPongThread(String stringToPrint)
- {
+ PingPongThread(String stringToPrint) {
mStringToPrint = stringToPrint;
}
/**
- * Abstract hook methods that determine the ping/pong
- * scheduling protocol in the run() template method.
+ * Abstract hook methods that determine the ping/pong scheduling
+ * protocol in the run() template method.
*/
abstract void acquire();
abstract void release();
@@ -70,28 +65,25 @@ static abstract class PingPongThread extends Thread
/**
* Sets the id of the other thread.
*/
- void setOtherThreadId(long id) {}
-
+ void setOtherThreadId(long id) {
+ }
+
/**
* This method runs in a separate thread of control and
- * implements the core ping/pong algorithm. It plays the role
+ * implements the core ping/pong algorithm. It plays the role
* of the "template method" in the Template Method pattern.
*/
- public void run()
- {
- for (int loopsDone = 1;
- loopsDone <= mMaxIterations;
- ++loopsDone) {
+ public void run() {
+ for (int loopsDone = 1; loopsDone <= mMaxIterations; ++loopsDone) {
// Perform the template method protocol for printing a
- // "ping" or a "pong" on the display. Note that the
+ // "ping" or a "pong" on the display. Note that the
// acquire() and release() hook methods that control
// the scheduling of the threads are deferred to
// subclasses.
acquire();
- mPlatformStrategy.print
- (mStringToPrint + "(" + loopsDone + ")");
+ mPlatformStrategy.print(mStringToPrint + "(" + loopsDone + ")");
release();
}
@@ -104,19 +96,18 @@ public void run()
// Data member that indicates the string to print (typically a
// "ping" or a "pong").
- protected String mStringToPrint;
+ protected final String mStringToPrint;
}
/**
* @class PingPongThreadSema
- *
- * @brief This class uses semaphores to implement the acquire()
- * and release() hook methods that schedule the ping/pong
- * algorithm. It plays the role of the "Concrete Class" in
- * the Template Method pattern.
+ *
+ * @brief This class uses semaphores to implement the acquire() and
+ * release() hook methods that schedule the ping/pong algorithm. It
+ * plays the role of the "Concrete Class" in the Template Method
+ * pattern.
*/
- static class PingPongThreadSema extends PingPongThread
- {
+ static class PingPongThreadSema extends PingPongThread {
/**
* Semaphores that schedule the ping/pong algorithm
*/
@@ -128,10 +119,8 @@ static class PingPongThreadSema extends PingPongThread
private final static int FIRST_SEMA = 0;
private final static int SECOND_SEMA = 1;
- PingPongThreadSema(String stringToPrint,
- Semaphore firstSema,
- Semaphore secondSema)
- {
+ PingPongThreadSema(String stringToPrint, Semaphore firstSema,
+ Semaphore secondSema) {
super(stringToPrint);
mSemas[FIRST_SEMA] = firstSema;
mSemas[SECOND_SEMA] = secondSema;
@@ -140,16 +129,14 @@ static class PingPongThreadSema extends PingPongThread
/**
* Hook method for ping/pong acquire.
*/
- void acquire()
- {
+ void acquire() {
mSemas[FIRST_SEMA].acquireUninterruptibly();
}
/**
* Hook method for ping/pong release.
*/
- void release()
- {
+ void release() {
mSemas[SECOND_SEMA].release();
}
@@ -163,8 +150,7 @@ void release()
* plays the role of the "Concrete Class" in the Template Method
* pattern.
*/
- static class PingPongThreadCond extends PingPongThread
- {
+ static class PingPongThreadCond extends PingPongThread {
/**
* Semaphores that schedule the ping/pong algorithm.
*/
@@ -179,7 +165,7 @@ static class PingPongThreadCond extends PingPongThread
* Number of times we've iterated thus far in our "turn".
*/
private int mIterationCount = 0;
-
+
/**
* Id for the other thread.
*/
@@ -189,9 +175,8 @@ static class PingPongThreadCond extends PingPongThread
* Thread whose turn it currently is.
*/
private static long mThreadOwner;
-
- public void setOtherThreadId(long otherThreadId)
- {
+
+ public void setOtherThreadId(long otherThreadId) {
this.mOtherThreadId = otherThreadId;
}
@@ -201,18 +186,14 @@ public void setOtherThreadId(long otherThreadId)
private final static int FIRST_COND = 0;
private final static int SECOND_COND = 1;
- PingPongThreadCond(String stringToPrint,
- ReentrantLock lock,
- Condition firstCond,
- Condition secondCond,
- boolean isOwner)
- {
+ PingPongThreadCond(String stringToPrint, ReentrantLock lock,
+ Condition firstCond, Condition secondCond, boolean isOwner) {
super(stringToPrint);
mIterationCount = mMaxTurns;
mLock = lock;
mConds[FIRST_COND] = firstCond;
mConds[SECOND_COND] = secondCond;
- if (isOwner)
+ if (isOwner)
mThreadOwner = this.getId();
}
@@ -247,14 +228,11 @@ void release() {
}
/**
- * Constructor stores the PlatformStrategy and the number of
- * iterations to play ping/pong.
+ * Constructor stores the PlatformStrategy and the number of iterations to
+ * play ping/pong.
*/
- public PlayPingPong (PlatformStrategy platformStrategy,
- int maxIterations,
- int maxTurns,
- String syncMechanism)
- {
+ public PlayPingPong(PlatformStrategy platformStrategy, int maxIterations,
+ int maxTurns, String syncMechanism) {
// The PlatformStrategy being used.
mPlatformStrategy = platformStrategy;
@@ -263,57 +241,62 @@ public PlayPingPong (PlatformStrategy platformStrategy,
// Number of iterations to perform pings and pongs per "turn".
mMaxTurns = maxTurns;
-
+
// Which synchronization to use (e.g., "SEMA" vs. "COND").
mSyncMechanism = syncMechanism;
}
- private void makePingPongThreads(String schedMechanism,
- PingPongThread[] pingPongThreads)
- {
+ static String pingString = "ping ";
+ static String pongString = "_pong ";
+
+ static boolean checkedStringFormatting = false;
+
+ private void formatStrings() {
+ if (!checkedStringFormatting) {
+ PlatformStrategyFactory.PlatformType type =
+ PlatformStrategyFactory.PlatformType.ANDROID;
+
+ if (PlatformStrategyFactory.platformType().equals(type))
+ pingString += " ";
+ checkedStringFormatting = true;
+ }
+ }
+
+ private void makePingPongThreads(String schedMechanism,
+ PingPongThread[] pingPongThreads) {
+ formatStrings();
if (schedMechanism.equals("SEMA")) {
// Create the semaphores that schedule threads
- // printing "ping" and "pong" in the correct
+ // printing "ping " and "_pong" in the correct
// alternating order.
Semaphore pingSema = new Semaphore(1); // Starts out unlocked.
Semaphore pongSema = new Semaphore(0);
-
- pingPongThreads[PING_THREAD] =
- new PingPongThreadSema("ping", pingSema, pongSema);
- pingPongThreads[PONG_THREAD] =
- new PingPongThreadSema("pong", pongSema, pingSema);
- }
- else if (schedMechanism.equals("COND")) {
+
+ pingPongThreads[PING_THREAD] = new PingPongThreadSema(pingString,
+ pingSema, pongSema);
+ pingPongThreads[PONG_THREAD] = new PingPongThreadSema(pongString,
+ pongSema, pingSema);
+ } else if (schedMechanism.equals("COND")) {
ReentrantLock lock = new ReentrantLock();
Condition pingCond = lock.newCondition();
Condition pongCond = lock.newCondition();
- int numberOfTurnsEach = 2;
-
- pingPongThreads[PING_THREAD] =
- new PingPongThreadCond("ping",
- lock,
- pingCond,
- pongCond,
- true);
- pingPongThreads[PONG_THREAD] =
- new PingPongThreadCond("pong",
- lock,
- pongCond,
- pingCond,
- false);
+
+ pingPongThreads[PING_THREAD] = new PingPongThreadCond(pingString,
+ lock, pingCond, pongCond, true);
+ pingPongThreads[PONG_THREAD] = new PingPongThreadCond(pongString,
+ lock, pongCond, pingCond, false);
pingPongThreads[PING_THREAD]
.setOtherThreadId(pingPongThreads[PONG_THREAD].getId());
pingPongThreads[PONG_THREAD]
.setOtherThreadId(pingPongThreads[PING_THREAD].getId());
}
}
-
+
/**
- * Start running the ping/pong code, which can be called from a
- * main() function in a Java class, an Android Activity, etc.
- */
- public void run()
- {
+ * Start running the ping/pong code, which can be called from a main()
+ * function in a Java class, an Android Activity, etc.
+ */
+ public void run() {
// Indicate a new game is beginning.
mPlatformStrategy.begin();
@@ -325,24 +308,22 @@ public void run()
pingPongThreads[PING_THREAD] = null;
pingPongThreads[PONG_THREAD] = null;
- /**
- * Create the appropriate type of threads with the designated
- * scheduling mechanism (e.g., "SEMA" for Semaphores, "COND"
- * for ConditionObjects, etc.).
+ /**
+ * Create the appropriate type of threads with the designated scheduling
+ * mechanism (e.g., "SEMA" for Semaphores, "COND" for ConditionObjects,
+ * etc.).
*/
- makePingPongThreads(mSyncMechanism,
- pingPongThreads);
+ makePingPongThreads(mSyncMechanism, pingPongThreads);
/**
- * Start ping and pong threads, which calls their run()
- * methods.
+ * Start ping and pong threads, which calls their run() methods.
*/
pingPongThreads[PING_THREAD].start();
pingPongThreads[PONG_THREAD].start();
/**
- * Barrier synchronization to wait for all work to be done
- * before exiting play().
+ * Barrier synchronization to wait for all work to be done before
+ * exiting play().
*/
mPlatformStrategy.awaitDone();
diff --git a/assignments/week-5-assignment-4/ic_launcher-web.png b/assignments/week-5-assignment-4/ic_launcher-web.png
deleted file mode 100644
index a18cbb48c..000000000
Binary files a/assignments/week-5-assignment-4/ic_launcher-web.png and /dev/null differ
diff --git a/assignments/week-5-assignment-4/res/drawable-hdpi/ic_launcher.png b/assignments/week-5-assignment-4/res/drawable-hdpi/ic_launcher.png
deleted file mode 100644
index 288b66551..000000000
Binary files a/assignments/week-5-assignment-4/res/drawable-hdpi/ic_launcher.png and /dev/null differ
diff --git a/assignments/week-5-assignment-4/res/drawable-mdpi/ic_launcher.png b/assignments/week-5-assignment-4/res/drawable-mdpi/ic_launcher.png
deleted file mode 100644
index 6ae570b4d..000000000
Binary files a/assignments/week-5-assignment-4/res/drawable-mdpi/ic_launcher.png and /dev/null differ
diff --git a/assignments/week-5-assignment-4/res/drawable-xhdpi/ic_launcher.png b/assignments/week-5-assignment-4/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100644
index d4fb7cd9d..000000000
Binary files a/assignments/week-5-assignment-4/res/drawable-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/assignments/week-5-assignment-4/res/drawable-xxhdpi/ic_launcher.png b/assignments/week-5-assignment-4/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100644
index 85a608158..000000000
Binary files a/assignments/week-5-assignment-4/res/drawable-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/assignments/week-5-assignment-4/src/edu/vuum/mocca/PingPongActivity.java b/assignments/week-5-assignment-4/src/edu/vuum/mocca/PingPongActivity.java
deleted file mode 100644
index 4296cd074..000000000
--- a/assignments/week-5-assignment-4/src/edu/vuum/mocca/PingPongActivity.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package edu.vuum.mocca;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.View;
-import android.widget.Button;
-import android.widget.TextView;
-
-/**
- * @class MainActivity
- *
- * @brief Initial start up screen for the android GUI.
- */
-public class PingPongActivity extends Activity
-{
- /** TextView that PingPong will be "played" upon */
- private TextView mAndroidPingPongOutput;
-
- /** Button that allows playing and resetting of the game */
- private Button mPlayButton;
-
- /** Variables to track state of the game */
- private static int PLAY = 0;
- private static int RESET = 1;
- private int mGameState = PLAY;
-
- protected void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
-
- /** Sets the content view to the xml file, activity_ping_pong. */
- setContentView(R.layout.activity_ping_pong);
- mAndroidPingPongOutput = (TextView) findViewById(R.id.pingpong_output);
- mPlayButton = (Button) findViewById(R.id.play_button);
-
- /**
- * Initializes the Platform singleton with the appropriate
- * Platform strategy, which in this case will be the
- * AndroidPlatform.
- */
- PlatformStrategy.instance
- (new PlatformStrategyFactory
- (mAndroidPingPongOutput, this).makePlatformStrategy());
-
- /** Initializes the Options singleton. */
- String args[] = new String[] { "PlayPingPongGUI" };
- Options.instance().parseArgs(args);
- }
-
- /** Sets the action of the button on click state. */
- public void playButtonClicked(View view)
- {
- if(mGameState == PLAY) {
- /**
- * Use a factory method to create the appropriate type of
- * OutputStrategy.
- */
- PlayPingPong pingPong =
- new PlayPingPong(PlatformStrategy.instance(),
- Options.instance().maxIterations(),
- Options.instance().maxTurns(),
- Options.instance().syncMechanism());
-
- /**
- * Play ping-pong with the designated number of
- * iterations.
- */
- new Thread (pingPong).start();
- mPlayButton.setText(R.string.reset_button);
- mGameState = RESET;
- }
- else if(mGameState == RESET) {
-
- /** Empty TextView and prepare the UI to play another game */
- mAndroidPingPongOutput.setText(R.string.empty_string);
- mPlayButton.setText(R.string.play_button);
- mGameState = PLAY;
- }
- else {
- /** Notify the player that something has gone wrong and reset */
- mAndroidPingPongOutput.setText("Unknown State entered!");
- mGameState = RESET;
- }
- }
-}
diff --git a/assignments/week-6-assignment-5/Assignment-Description.txt b/assignments/week-6-assignment-5/Assignment-Description.txt
new file mode 100644
index 000000000..b1752ceaf
--- /dev/null
+++ b/assignments/week-6-assignment-5/Assignment-Description.txt
@@ -0,0 +1,114 @@
+Week 6: Programming Assignment 5
+
+Released Monday, June 16th, 2014
+Due Monday, June 30th, 2014
+
+In this assignment, you will implement a program that downloads images
+using started services and then displays them in the context of the UI
+Thread. The user can optionally select one of two different Started
+Services whose implementations you will complete:
+
+. A simple DownloadIntentService that uses on the Android
+ IntentService framework to download files in a single background
+ Thread and
+
+. A more scalable ThreadPoolDownloadService that uses a
+ ThreadPoolExecutor to download several files concurrently within a
+ fixed pool of Threads.
+
+In this directory you'll find Java source code, AndroidManifest.xml,
+and associated XML metadata files. The directory
+W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/ contains
+the skeleton Java code that you'll implement by completing the "TODO -
+You fill in here" comments to provide a working solution. DO NOT
+CHANGE THE OVERALL STRUCTURE OF THE SKELETON - just fill in the "TODO
+- You fill in here" portions!!!
+
+The main goal of this project is to understand the pattern-oriented
+threaded download application and how to start and interact with
+Services using the IntentService and Messenger IPC mechanisms. In
+particular, you'll need to do the following:
+
+. Understand how to launch Started Services using Intents and receive
+ replies from Services using the Messenger IPC mechanism in the
+ DownloadActivity class, which are covered in these videos:
+
+ Section 1: Module 3: Part 5: Sending and Handling Messages with Android Handler
+ Section 2: Module 1: Part 2: Programming Started Services (Part 1)
+ Section 2: Module 1: Part 3: Programming Started Services (Part 2)
+
+. Understand how to implement the DownloadIntentService and
+ ThreadPoolDownloadService by handling Intents, doing work in the
+ background, and sending results the Messenger IPC mechanism covered
+ in these videos:
+
+ Section 2: Module 1: Part 4: Android IntentService
+ Section 2: Module 1: Part 6: Service to Activity Communication via Android Messenger
+
+. Understand factory methods and implement the makeIntent() factory
+ methods in each service, which are similar to the
+ DownloadService.makeIntent() factory method covered in this video:
+
+ Section 2: Module 1: Part 3: Programming Started Services (Part 2)
+
+. Understand the ThreadPoolExecutor class and utilize a factory method
+ in the Executors class to create a new thread pool, which is
+ (briefly) outlined in this video
+
+ Section 1: Module 3: Part 7: The AsyncTask Framework (Part 2)
+
+ and described further in the documentation at:
+
+ http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html
+
+ There's also an example that shows how to use a Executor thread pool
+ available at
+
+ https://github.com/douglascraigschmidt/POSA-14/tree/master/ex/UniqueIDGeneratorApplication
+
+. Understand the methods provided in the DownloadUtils class, which
+ contains functionality that is common to both the
+ DownloadIntentService and ThreadPoolDownloadService.
+
+These videos are available at
+
+https://class.coursera.org/posa-002/lecture
+
+We'll also discuss this assignment specification (and later its
+solution) in the POSA MOOC "Virtual Office Hours", which are described
+in item #38 at the POSA MOOC FAQ available from
+
+http://www.courera.org/course/posa
+
+You'll need to build this Android application in the Eclipse ADT
+environment. Unlike previous assignments, this program is
+user-driven, so output will vary depending on which URL is provided.
+In addition to using the default URL (which is available from
+https://d396qusza40orc.cloudfront.net/posa/ka.png) you should also
+test this application by finding an Internet URL to an image (ending
+in .jpg, .gif, .png etc.) and see if your application correctly
+displays the image.
+
+We provide several JUnit tests that should test your program's
+functionality. The unit tests test the Service classes independently
+of the DownloadActivity, and then test the overall functionality of
+the program. By default, the unit tests try to download the default
+URL and then check if the proper image was downloaded using Bitmap
+comparison.
+
+To Run the Android Application, right click on the 'W6-A5-Android'
+project and select [Run As] -> [Android Application]
+
+To Run the Android Unit Test, right click on the 'W6-A5-Android-Test'
+project and select [Run As] -> [Android Unit Test]
+
+By default, the application and test program do an "offline" download
+since students often run this code an emulator or some other
+environment with slow/no Internet connectivity. If you'd like to
+run/test it in the Internet please change the DOWNLOAD_OFFLINE boolean
+in
+
+W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/DownloadUtils.java
+
+to false.
+
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/.classpath b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/.classpath
new file mode 100644
index 000000000..de7aa1845
--- /dev/null
+++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/.classpath
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/.project b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/.project
new file mode 100644
index 000000000..04245676b
--- /dev/null
+++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/.project
@@ -0,0 +1,34 @@
+
+
+ W6-A5-ThreadedDownloads-StartedServices-Tests
+
+
+ week-6-assignment-5
+
+
+
+ com.android.ide.eclipse.adt.ResourceManagerBuilder
+
+
+
+
+ com.android.ide.eclipse.adt.PreCompilerBuilder
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ com.android.ide.eclipse.adt.ApkBuilder
+
+
+
+
+
+ com.android.ide.eclipse.adt.AndroidNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/AndroidManifest.xml b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/AndroidManifest.xml
new file mode 100644
index 000000000..2d80b1796
--- /dev/null
+++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/AndroidManifest.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/project.properties b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/project.properties
new file mode 100644
index 000000000..a3ee5ab64
--- /dev/null
+++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/project.properties
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-17
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-hdpi/ic_launcher.png b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 000000000..96a442e5b
Binary files /dev/null and b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-hdpi/ic_launcher.png differ
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-ldpi/ic_launcher.png b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 000000000..99238729d
Binary files /dev/null and b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-ldpi/ic_launcher.png differ
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-mdpi/ic_launcher.png b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 000000000..359047dfa
Binary files /dev/null and b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-mdpi/ic_launcher.png differ
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-nodpi/dougs.jpg b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-nodpi/dougs.jpg
new file mode 100644
index 000000000..e31e8b98b
Binary files /dev/null and b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-nodpi/dougs.jpg differ
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-xhdpi/ic_launcher.png b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..71c6d760f
Binary files /dev/null and b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/values/strings.xml b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/values/strings.xml
new file mode 100644
index 000000000..111dfea6e
--- /dev/null
+++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+
+
+ W6-A5-ThreadedDownloads-StartedServices-Tests
+
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/AllTests.java b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/AllTests.java
new file mode 100644
index 000000000..0d878f947
--- /dev/null
+++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/AllTests.java
@@ -0,0 +1,22 @@
+package edu.vuum.mocca.test;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * @class AllTests
+ *
+ * @brief Combine all the unit tests into one suite, so that you can run them together with one click.
+ */
+public class AllTests {
+ public static Test suite() {
+ TestSuite suite = new TestSuite(AllTests.class.getName());
+ //$JUnit-BEGIN$
+ suite.addTestSuite(DownloadIntentServiceTests.class);
+ suite.addTestSuite(DownloadUtilsTests.class);
+ suite.addTestSuite(ThreadPoolDownloadServiceTests.class);
+ suite.addTestSuite(DownloadActivityTests.class);
+ //$JUnit-END$
+ return suite;
+ }
+}
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/DownloadActivityTests.java b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/DownloadActivityTests.java
new file mode 100644
index 000000000..991dd170e
--- /dev/null
+++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/DownloadActivityTests.java
@@ -0,0 +1,121 @@
+package edu.vuum.mocca.test;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.test.ActivityInstrumentationTestCase2;
+import android.widget.EditText;
+import android.view.WindowManager;
+
+import com.robotium.solo.Solo;
+
+import edu.vuum.mocca.DownloadActivity;
+
+/**
+ * @class DownloadActivityTests
+ *
+ * @brief Tests the functionality of DownloadActivity. Current tests
+ * only press buttons and check that the correct image was downloaded.
+ */
+public class DownloadActivityTests
+ extends
+ ActivityInstrumentationTestCase2 {
+ /**
+ * Constructor initializes the superclass
+ */
+ public DownloadActivityTests() {
+ super(DownloadActivity.class);
+ }
+
+ /**
+ * This is the handle for Robotium, which allows us to interact
+ * with the UI.
+ */
+ Solo mSolo;
+
+ /**
+ * The context of this project, not the target project.
+ */
+ Context mContext;
+
+ /**
+ * The activity we're testing
+ */
+ DownloadActivity mActivity;
+
+ /**
+ * Store the bitmap that we expect to be downloaded.
+ */
+ Bitmap mExpected;
+
+ /**
+ * Called before each test is run to perform the initialization.
+ */
+ public void setUp() throws Exception{
+ super.setUp();
+
+ mActivity = getActivity();
+
+ // Setup Robotium and get the EditText View.
+ mSolo = new Solo(getInstrumentation(), mActivity);
+
+ mContext = getInstrumentation().getContext();
+
+ EditText edit_ = (EditText) mSolo.getView(edu.vuum.mocca.R.id.url);
+ mSolo.clearEditText(edit_);
+ mSolo.enterText(edit_, Options.TEST_URI);
+
+ mExpected = BitmapFactory.decodeResource(mContext.getResources(),
+ R.drawable.dougs);
+
+ getInstrumentation().callActivityOnStart(mActivity);
+ getInstrumentation().callActivityOnResume(mActivity);
+
+ // Let us dismiss the lockscreen
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+ }
+ });
+
+ // Wait for things to settle
+ Thread.sleep(Options.SHORT_WAIT_TIME);
+ }
+
+ /**
+ * Push the button and see if the correct image is displayed.
+ */
+ public void testThreadPoolButton() throws Exception {
+
+ // Make sure the current image isn't the one we're looking for
+ assertFalse(mExpected.sameAs(mActivity.mCurrentBitmap));
+
+ // Click on the "Start ThreadPool Button"
+ mSolo.clickOnView(mSolo.getView(edu.vuum.mocca.R.id.thread_pool_button));
+
+ // Wait for the image to download
+ Thread.sleep(Options.LONG_WAIT_TIME);
+
+ // Check if we displayed the correct image
+ assertTrue(mExpected.sameAs(mActivity.mCurrentBitmap));
+ }
+
+ /**
+ * Push the button and see if the correct image is displayed.
+ */
+ public void testIntentServiceButton() throws Exception {
+
+ // Make sure the current image isn't the one we're looking for
+ assertFalse(mExpected.sameAs(mActivity.mCurrentBitmap));
+
+ // Click on the "Start ThreadPool Button"
+ mSolo.clickOnView(mSolo.getView(edu.vuum.mocca.R.id.intent_service_button));
+
+ // Wait for the image to download
+ Thread.sleep(Options.LONG_WAIT_TIME);
+
+ // Check if we displayed the correct image
+ assertTrue(mExpected.sameAs(mActivity.mCurrentBitmap));
+ }
+}
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/DownloadIntentServiceTests.java b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/DownloadIntentServiceTests.java
new file mode 100644
index 000000000..8148d4dac
--- /dev/null
+++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/DownloadIntentServiceTests.java
@@ -0,0 +1,162 @@
+package edu.vuum.mocca.test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.test.ServiceTestCase;
+import edu.vuum.mocca.DownloadIntentService;
+import edu.vuum.mocca.DownloadUtils;
+
+/**
+ * @class DownloadIntentServiceTests
+ *
+ * @brief Tests the functionality of the DownloadIntentService.
+ */
+public class DownloadIntentServiceTests
+ extends
+ ServiceTestCase {
+ /**
+ * Use this latch to wait for messages to be received by the
+ * Handler's thread.
+ */
+ static CountDownLatch mLatch;
+
+ /**
+ * Stores the Uri received by the Handler.
+ */
+ static String mReceivedUri = null;
+
+ /**
+ * The Handler that receives a Message reply from the
+ * IntentService.
+ */
+ static MessageHandler mHandler;
+
+ /**
+ * Store the intent from makeIntent() to test proper creations.
+ */
+ Intent mIntent;
+
+ /**
+ * The context of THIS project, not the target project
+ */
+ Context mContext;
+
+ /**
+ * Constructor initializes the superclass.
+ */
+ public DownloadIntentServiceTests() {
+ super(DownloadIntentService.class);
+ }
+
+ /**
+ * @class MessageHandler
+ *
+ * @brief Make a handler to catch Messages sent from the service.
+ */
+ static class MessageHandler extends Handler {
+ /**
+ * Allows us to explicitly specify which thread's Looper to use.
+ */
+ public MessageHandler(Looper myLooper) {
+ super(myLooper);
+ }
+
+ /**
+ * Handle return messages from the IntentService. Store the
+ * received Uri and notify the JUnit thread.
+ */
+ public void handleMessage(Message msg) {
+ // We don't know what tag they'll use for the path, so we
+ // have to search for it.
+ mReceivedUri = Utilities.searchForPath(msg);
+
+ // Let the unit test thread know we've gotten a message.
+ mLatch.countDown();
+ }
+ }
+
+ /**
+ * This method is called before each test is called to perform
+ * initialization activities.
+ */
+ public void setUp() throws Exception{
+ super.setUp();
+
+ // Make an intent with the handler using the factory method.
+ mIntent = DownloadIntentService.makeIntent(getContext(),
+ new Handler(),
+ Options.TEST_URI);
+
+ // Get the context for THIS project, not the target project.
+ mContext = getContext().createPackageContext(this.getClass().getPackage().getName(),
+ Context.CONTEXT_IGNORE_SECURITY);
+
+ }
+
+ /**
+ * Check that the intent starts the correct service.
+ */
+ public void test_makeIntent_Class () {
+ assertTrue(Utilities.checkClass(mIntent, DownloadIntentService.class));
+ }
+
+ /**
+ * Check that the intent has the correct Uri.
+ */
+ public void test_makeIntent_Uri () {
+ assertTrue(Utilities.checkUri(mIntent));
+ }
+
+ /**
+ * Check that the intent has a Messenger attached.
+ */
+ public void test_makeIntent_Messenger () {
+ assertTrue(Utilities.checkMessenger(mIntent));
+ }
+
+ /**
+ * Try starting the service
+ */
+ public void test_startService () throws Exception {
+
+ mLatch = new CountDownLatch(1);
+
+ // Start a thread to handle the message when it's sent.
+ new Thread(new Runnable() {
+ public void run() {
+ Looper.prepare();
+ mHandler = new MessageHandler(Looper.myLooper());
+ Looper.loop();
+ }
+ }).start();
+
+ // Wait for the handler to get instantiated
+ Thread.sleep(Options.SHORT_WAIT_TIME);
+
+ //Create an Intent to start the service
+ mIntent = DownloadIntentService.makeIntent(getContext(),
+ mHandler,
+ Options.TEST_URI);
+
+ // Start the service
+ startService(mIntent);
+
+ assertNotNull(getService());
+
+ // Wait for it to send us a Message (or time out)
+ mLatch.await(Options.LONG_WAIT_TIME, TimeUnit.MILLISECONDS);
+
+ // Check if we got a Message or timed out
+ assertNotNull(mReceivedUri);
+
+ // Check that the image actually downloaded.
+ assertTrue(Utilities.checkDownloadedImage(mContext, mReceivedUri));
+ }
+}
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/DownloadUtilsTests.java b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/DownloadUtilsTests.java
new file mode 100644
index 000000000..d82d0087b
--- /dev/null
+++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/DownloadUtilsTests.java
@@ -0,0 +1,202 @@
+package edu.vuum.mocca.test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.test.ActivityInstrumentationTestCase2;
+import edu.vuum.mocca.DownloadActivity;
+import edu.vuum.mocca.DownloadUtils;
+
+/**
+ * @class DownloadUtilsTests
+ *
+ * @brief Test the functionality of the DownloadUtils class
+ */
+public class DownloadUtilsTests
+ extends
+ ActivityInstrumentationTestCase2 {
+ /**
+ * Constructor initializes the superclass.
+ */
+ public DownloadUtilsTests () {
+ super (DownloadActivity.class);
+ }
+
+ // The intent returned by makeMessengerIntent()
+ Intent mIntent;
+
+ // The bundle that is part of the intent
+ Bundle mExtras;
+
+ /**
+ * Use this latch to wait for messages to be received by the
+ * Handler's thread.
+ */
+ static CountDownLatch mLatch;
+
+ /**
+ * Stores the Uri received by the Handler.
+ */
+ static String mReceivedUri = null;
+
+ /**
+ * The Handler that receives a Message reply from the
+ * IntentService.
+ */
+ static MessageHandler mHandler;
+
+ /**
+ * @class MessageHandler
+ *
+ * @brief Define a handler to catch messages from sendPath()
+ */
+ static class MessageHandler extends Handler {
+ /**
+ * Constructor initializes the superclass
+ */
+ public MessageHandler(Looper myLooper) {
+ super(myLooper);
+ }
+
+ /**
+ * Handle return messages from the IntentService. Store the
+ * received Uri and notify the JUnit thread.
+ */
+ public void handleMessage(Message msg) {
+
+ // We don't know what tag they'll use for the path, so we
+ // have to search for it.
+ mReceivedUri = Utilities.searchForPath(msg);
+
+ // Let the unit test thread know we've gotten a message.
+ mLatch.countDown();
+ }
+ }
+
+ /**
+ * This method is called before each test is run to perform
+ * initialization activities.
+ */
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ // Make an arbitrary Messenger intent.
+ mIntent = DownloadUtils.makeMessengerIntent(getActivity(),
+ DownloadUtilsTests.class,
+ new MessageHandler(Looper.myLooper()),
+ Options.TEST_URI);
+ mExtras = mIntent.getExtras();
+ }
+
+ /**
+ * Try downloading a file.
+ */
+ public void test_downloadFile () {
+ Context context = getInstrumentation().getContext();
+ String result = DownloadUtils.downloadFile(getActivity(),
+ Uri.parse(Options.TEST_URI));
+
+ assertTrue(Utilities.checkDownloadedImage(context, result));
+ }
+
+ /**
+ * Check that the intent has a Messenger attached.
+ */
+ public void test_makeMessengerIntent_Messenger_Extra () {
+ assertTrue(Utilities.checkMessenger(mIntent));
+ }
+
+ /**
+ * Check that the intent has the proper Uri attached
+ */
+ public void test_makeMessengerIntent_Uri_Extra () {
+ assertTrue(Utilities.checkUri(mIntent));
+ }
+
+ /**
+ * Check that the intent will start the class we told it to
+ */
+ public void test_makeMessengerIntent_Class () {
+ assertTrue(Utilities.checkClass(mIntent, DownloadUtilsTests.class));
+ }
+
+ /**
+ * Try sending a message using sendPath().
+ */
+ public void test_sendPath () throws Exception {
+ mLatch = new CountDownLatch(1);
+
+ // Start a thread to catch the message from sendPath()
+ new Thread ( new Runnable () {
+ public void run() {
+ Looper.prepare();
+ mHandler = new MessageHandler(Looper.myLooper());
+ Looper.loop();
+ }
+ }).start();
+
+ // Wait for the handler to instantiate
+ Thread.sleep(Options.SHORT_WAIT_TIME);
+
+ // Send a message to ourselves
+ DownloadUtils.sendPath(Options.TEST_URI, new Messenger(mHandler));
+
+ // Wait for it to get here
+ mLatch.await(Options.LONG_WAIT_TIME, TimeUnit.MILLISECONDS);
+
+ // See if we got the message or timed out.
+ assertNotNull(mReceivedUri);
+
+ // Check that the Uri is correct
+ assertTrue(mReceivedUri.equals(Options.TEST_URI));
+
+ // Other tests use this
+ mReceivedUri = null;
+ }
+
+ /**
+ * Test that the downloadAndRespond method properly uses the other
+ * two methods in DownloadUtils.
+ */
+ public void test_downloadAndRespond() throws Exception {
+ mLatch = new CountDownLatch(1);
+
+ // Start a thread to handle messages we send to ourselves
+ new Thread ( new Runnable () {
+ public void run() {
+ Looper.prepare();
+ mHandler = new MessageHandler(Looper.myLooper());
+ Looper.loop();
+ }
+ }).start();
+
+ // Wait for mHandler to instantiate
+ Thread.sleep(Options.SHORT_WAIT_TIME);
+
+ // Download the image and send a message to ourselves
+ DownloadUtils.downloadAndRespond(getActivity(),
+ Uri.parse(Options.TEST_URI),
+ new Messenger(mHandler));
+
+ // Wait for it to get here.
+ mLatch.await(Options.LONG_WAIT_TIME, TimeUnit.MILLISECONDS);
+
+ // Check if we timed out or got the message
+ assertNotNull(mReceivedUri);
+
+ // Make sure the image downloaded correctly.
+ assertTrue(Utilities.checkDownloadedImage(getInstrumentation().getContext(), mReceivedUri));
+
+ // Other tests use this
+ mReceivedUri = null;
+ }
+}
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/Options.java b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/Options.java
new file mode 100644
index 000000000..042f8f648
--- /dev/null
+++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/Options.java
@@ -0,0 +1,34 @@
+package edu.vuum.mocca.test;
+
+/**
+ * @class Options
+ *
+ * @brief Holds global constants for the testing suite. More
+ * convenient than grabbing resources from /res and trying to
+ * finagle a working Context out of the test classes every time
+ * we want to use TEST_URI.
+ *
+ */
+public class Options {
+
+ /**
+ * The online URL of the image we're using for testing.
+ */
+ static final String TEST_URI = "https://d396qusza40orc.cloudfront.net/posa/dougs-xsmall.jpg";
+
+ /**
+ * Whatever image we're testing, store it in res/drawable-nodpi so
+ * we can compare it to what's downloaded.
+ */
+ static final int TEST_IMAGE = R.drawable.dougs;
+
+ /**
+ * Time we should wait for things to instantiate.
+ */
+ static final long SHORT_WAIT_TIME = 10000;
+
+ /**
+ * Time we should wait for things to download.
+ */
+ static final long LONG_WAIT_TIME = 25000;
+}
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/ThreadPoolDownloadServiceTests.java b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/ThreadPoolDownloadServiceTests.java
new file mode 100644
index 000000000..c1cc6020a
--- /dev/null
+++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/ThreadPoolDownloadServiceTests.java
@@ -0,0 +1,168 @@
+package edu.vuum.mocca.test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.test.ServiceTestCase;
+import edu.vuum.mocca.DownloadUtils;
+import edu.vuum.mocca.ThreadPoolDownloadService;
+
+/**
+ * @class ThreadPoolDownloadServiceTests
+ *
+ * @brief Tests the functionality of the ThreadPoolDownloadService.
+ */
+public class ThreadPoolDownloadServiceTests
+ extends
+ ServiceTestCase {
+ /**
+ * Use this latch to wait for messages to be received by the
+ * Handler's thread.
+ */
+ static CountDownLatch mLatch;
+
+ /**
+ * Stores the Uri received by the Handler.
+ */
+ static String mReceivedUri = null;
+
+ /**
+ * The Handler that receives a Message reply from the
+ * IntentService.
+ */
+ static MessageHandler mHandler;
+
+ /**
+ * Store the intent from makeIntent() to test proper creations.
+ */
+ Intent mIntent;
+
+ /**
+ * The context of THIS project, not the target project
+ */
+ Context mContext;
+
+ /**
+ * Constructor initializes the superclass.
+ */
+ public ThreadPoolDownloadServiceTests() {
+ super(ThreadPoolDownloadService.class);
+ }
+
+ /**
+ * @class MessageHandler
+ *
+ * @brief Define a private handler to catch messages from the service.
+ */
+ static class MessageHandler extends Handler {
+ /**
+ * Constructor initializes the superclass
+ */
+ public MessageHandler(Looper myLooper) {
+ super(myLooper);
+ }
+
+ /**
+ * Handle return messages from the IntentService. Store the
+ * received Uri and notify the JUnit thread.
+ */
+ public void handleMessage(Message msg) {
+ // We don't know what tag they'll use for the path, so we
+ // have to search for it.
+ mReceivedUri = Utilities.searchForPath(msg);
+
+ // Let the unit test thread know we've gotten a message.
+ mLatch.countDown();
+ }
+ }
+
+ /**
+ * This is called once before each test is run.
+ */
+ public void setUp() throws Exception{
+ super.setUp();
+
+ // Make an intent using the factory method
+ mIntent = ThreadPoolDownloadService.makeIntent(getContext(),
+ new Handler(),
+ Options.TEST_URI);
+
+ // Get the context for THIS project, not the target project.
+ mContext = getContext().createPackageContext(this.getClass().getPackage().getName(),
+ Context.CONTEXT_IGNORE_SECURITY);
+
+ }
+
+ /**
+ * Check that the intent will start the correct service.
+ */
+ public void test_makeIntent_Class () {
+ assertTrue(Utilities.checkClass(mIntent, ThreadPoolDownloadService.class));
+ }
+
+ /**
+ * Check that the intent has the correct uri.
+ */
+ public void test_makeIntent_Uri () {
+ assertTrue(Utilities.checkUri(mIntent));
+ }
+
+ /**
+ * Check that the intent has a Messenger attached.
+ */
+ public void test_makeIntent_Messenger () {
+ assertTrue(Utilities.checkMessenger(mIntent));
+ }
+
+ /**
+ * Test starting the service.
+ */
+ public void test_startService () throws Exception{
+
+ mLatch = new CountDownLatch(1);
+
+ // Start a thread to handle the message when it's sent.
+ new Thread(new Runnable() {
+ public void run() {
+ Looper.prepare();
+ mHandler = new MessageHandler(Looper.myLooper());
+ Looper.loop();
+ }
+ }).start();
+
+ // Wait for the handler to get instantiated
+ Thread.sleep(Options.SHORT_WAIT_TIME);
+
+ //Create an Intent to start the service
+ mIntent = ThreadPoolDownloadService.makeIntent(getContext(),
+ mHandler,
+ Options.TEST_URI);
+
+ // Start the service
+ startService(mIntent);
+
+ // Check if the service actually started
+ assertNotNull(getService());
+
+ // Wait for the service to download and send us a message
+ mLatch.await(Options.LONG_WAIT_TIME, TimeUnit.MILLISECONDS);
+
+ // See if we timed out or actually got a message
+ assertNotNull(mReceivedUri);
+
+ // Get the context for THIS project, not the target project
+ Context context = getContext().createPackageContext(this.getClass().getPackage().getName(),
+ Context.CONTEXT_IGNORE_SECURITY);
+
+ // Check that the file downloaded correctly
+ assertTrue(Utilities.checkDownloadedImage(context, mReceivedUri));
+ }
+}
+
+
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/Utilities.java b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/Utilities.java
new file mode 100644
index 000000000..107d69d8e
--- /dev/null
+++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/Utilities.java
@@ -0,0 +1,86 @@
+package edu.vuum.mocca.test;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.Messenger;
+import edu.vuum.mocca.DownloadUtils;
+
+/**
+ * @class Utilities
+ *
+ * @brief A few checks are repeated several times, such as checking
+ * whether a Messenger is present in an Intent, or if the proper
+ * Uri is set.
+ */
+public class Utilities {
+ /**
+ * Check that the proper Uri is set for the provided Intent.
+ */
+ static boolean checkUri(Intent intent) {
+ return Options.TEST_URI.equals(intent.getData().toString());
+ }
+
+ /**
+ * Check that the provided Intent contains a Messenger extra.
+ */
+ static boolean checkMessenger(Intent intent) {
+ Bundle extras = intent.getExtras();
+
+ // We don't know what tag they'll use for the Messenger, so we
+ // have to search for it.
+ Messenger messenger = null;
+
+ for (String key : extras.keySet()) {
+ if (extras.get(key) instanceof Messenger)
+ messenger = (Messenger) extras.get(key);
+ }
+
+ return (messenger != null);
+ }
+
+ /**
+ * Check that the provided Intent will start the expected class.
+ */
+ static boolean checkClass(Intent intent, Class> class_) {
+ return intent.getComponent().getClassName().equals(class_.getName());
+ }
+
+
+ /**
+ * Check if the downloaded Bitmap is equivalent to the expected
+ * bitmap.
+ */
+ static boolean checkDownloadedImage(Context test_context, String result) {
+ Bitmap downloaded = BitmapFactory.decodeFile(result);
+
+ Bitmap expected = BitmapFactory.decodeResource(test_context.getResources(),
+ Options.TEST_IMAGE);
+ return downloaded.sameAs(expected);
+ }
+
+ /**
+ * Search for the pathname that should be bundled in the provided
+ * Message. (Returns the last String extra found).
+ */
+ static String searchForPath (Message msg) {
+ Bundle msg_extras = msg.getData();
+
+ String path = null;
+ for (String key : msg_extras.keySet()) {
+ if (msg_extras.get(key) instanceof String)
+ path = (String) msg_extras.get(key);
+ }
+
+ return path;
+ }
+
+}
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/.classpath b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/.classpath
new file mode 100644
index 000000000..980bef1db
--- /dev/null
+++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/.classpath
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/.gitignore b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/.gitignore
new file mode 100644
index 000000000..102b6fc94
--- /dev/null
+++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/.gitignore
@@ -0,0 +1 @@
+*.properties
\ No newline at end of file
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/.project b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/.project
new file mode 100644
index 000000000..7ec10d267
--- /dev/null
+++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/.project
@@ -0,0 +1,33 @@
+
+
+ W6-A5-ThreadedDownloads-StartedServices
+
+
+
+
+
+ com.android.ide.eclipse.adt.ResourceManagerBuilder
+
+
+
+
+ com.android.ide.eclipse.adt.PreCompilerBuilder
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ com.android.ide.eclipse.adt.ApkBuilder
+
+
+
+
+
+ com.android.ide.eclipse.adt.AndroidNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/AndroidManifest.xml b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/AndroidManifest.xml
new file mode 100644
index 000000000..33958ae4e
--- /dev/null
+++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/AndroidManifest.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/libs/robotium-solo-5.1.jar b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/libs/robotium-solo-5.1.jar
new file mode 100644
index 000000000..1931dd97f
Binary files /dev/null and b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/libs/robotium-solo-5.1.jar differ
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/project.properties b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/project.properties
new file mode 100644
index 000000000..a3ee5ab64
--- /dev/null
+++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/project.properties
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-17
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable-hdpi/ic_launcher.png b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 000000000..96a442e5b
Binary files /dev/null and b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable-hdpi/ic_launcher.png differ
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable-mdpi/ic_launcher.png b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 000000000..359047dfa
Binary files /dev/null and b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable-mdpi/ic_launcher.png differ
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable-xhdpi/ic_launcher.png b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..71c6d760f
Binary files /dev/null and b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable/ka.png b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable/ka.png
new file mode 100644
index 000000000..3a751e9cd
Binary files /dev/null and b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable/ka.png differ
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/layout/activity_download.xml b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/layout/activity_download.xml
new file mode 100644
index 000000000..0ae335689
--- /dev/null
+++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/layout/activity_download.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/raw/dougs.jpg b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/raw/dougs.jpg
new file mode 100644
index 000000000..e31e8b98b
Binary files /dev/null and b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/raw/dougs.jpg differ
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/values-v11/styles.xml b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/values-v11/styles.xml
new file mode 100644
index 000000000..3c02242ad
--- /dev/null
+++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/values-v11/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/values-v14/styles.xml b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/values-v14/styles.xml
new file mode 100644
index 000000000..a91fd0372
--- /dev/null
+++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/values-v14/styles.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/values/strings.xml b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/values/strings.xml
new file mode 100644
index 000000000..605931a4b
--- /dev/null
+++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/values/strings.xml
@@ -0,0 +1,9 @@
+
+
+ W6-A5-ThreadedDownloads-StartedServices
+ Enter URL:
+ https://d396qusza40orc.cloudfront.net/posa/ka.png
+ Run ThreadPool Messenger
+ Run IntentService Messenger
+ Reset Image
+
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/values/styles.xml b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/values/styles.xml
new file mode 100644
index 000000000..6ce89c7ba
--- /dev/null
+++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/values/styles.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/DownloadActivity.java b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/DownloadActivity.java
new file mode 100644
index 000000000..e00e91170
--- /dev/null
+++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/DownloadActivity.java
@@ -0,0 +1,130 @@
+package edu.vuum.mocca;
+
+import java.lang.ref.WeakReference;
+
+import android.os.Handler;
+import android.os.Message;
+import android.view.View;
+import android.widget.Toast;
+
+/**
+ * This is the main activity that the program uses to start the
+ * ThreadedDownloads application. It allows the user to input the URL
+ * of an image and download that image using one of two different
+ * Android Service implementations. When the service is done
+ * downloading the image, it stores it on the Android file system,
+ * then notifies this activity using the Messenger IPC mechanism
+ * discussed in the class.
+ *
+ * Starting services to run synchronously from the asynchronous UI
+ * Thread is an example of the Half-Sync/Half-Async Pattern. Starting
+ * services using Intents is an example of the Command Processor
+ * Pattern. This activity, the Creator, creates a Command in the form
+ * of an Intent. The Intent is received by the service process, which
+ * plays the role of the Executor.
+ *
+ * Returning a result using Messages and Handlers is an example of the
+ * Active Object Pattern. The Service must invoke a method to update
+ * the UI. However, the service thread is not allowed to interact with
+ * the UI. To decouple the invocation of this method from execution,
+ * the Service encapsulates the request in a Message, which plays the
+ * role of Active Object. The message is then passed to the UI
+ * thread's handler, which eventually executes the request on the UI
+ * Thread.
+ *
+ * Note: all UI functionality has been factored out into
+ * DownloadBase. If you wish to display an image, use
+ * displayBitmap(). If you want to get the URL from the EditText
+ * object, use getUrlString().
+ */
+public class DownloadActivity extends DownloadBase {
+ /**
+ * This is the handler used for handling messages sent by a
+ * Messenger. It receives a message containing a pathname to an
+ * image and displays that image in the ImageView.
+ *
+ * The handler plays several roles in the Active Object pattern,
+ * including Proxy, Future, and Servant.
+ *
+ * Please use displayBitmap() defined in DownloadBase
+ */
+ static class MessengerHandler extends Handler {
+
+ // A weak reference to the enclosing class
+ WeakReference outerClass;
+
+ /**
+ * A constructor that gets a weak reference to the enclosing class.
+ * We do this to avoid memory leaks during Java Garbage Collection.
+ *
+ * @see https://groups.google.com/forum/#!msg/android-developers/1aPZXZG6kWk/lIYDavGYn5UJ
+ */
+ public MessengerHandler(DownloadActivity outer) {
+ outerClass = new WeakReference(outer);
+ }
+
+ // Handle any messages that get sent to this Handler
+ @Override
+ public void handleMessage(Message msg) {
+
+ // Get an actual reference to the DownloadActivity
+ // from the WeakReference.
+ final DownloadActivity activity = outerClass.get();
+
+ // If DownloadActivity hasn't been garbage collected
+ // (closed by user), display the sent image.
+ if (activity != null) {
+ // TODO - You fill in here to display the image
+ // bitmap that's been downloaded and returned to
+ // the DownloadActivity as a pathname who's Bundle
+ // key is defined by DownloadUtils.PATHNAME_KEY
+ }
+ }
+ }
+
+ /**
+ * Instantiate the MessengerHandler, passing in the
+ * DownloadActivity to be stored as a WeakReference
+ */
+ MessengerHandler handler = new MessengerHandler(this);
+
+ /**
+ * This method is called when a user presses a button (see
+ * res/layout/activity_download.xml)
+ *
+ * Start a service using startService() or make a call to a Bound
+ * Service using an AIDL interface. The action performed depends
+ * on the button pressed.
+ *
+ * To get the URL from the EditText, please use getUrlString()
+ * defined in DownloadBase.
+ */
+ public void runService(View view) {
+ String which = "";
+
+ switch (view.getId()) {
+ case R.id.intent_service_button:
+ // TODO - You fill in here to start the
+ // DownloadIntentService with the appropriate Intent
+ // returned from the makeIntent() factory method.
+
+ which = "Starting DownloadIntentService";
+ break;
+
+ case R.id.thread_pool_button:
+ // TODO - You fill in here to start the
+ // ThreadPoolDownloadService with the appropriate Intent
+ // returned from the makeIntent() factory method.
+
+ which = "Starting ThreadPoolDownloadService";
+ break;
+
+ }
+
+ // Display a short pop-up notification telling the user which
+ // service was started.
+ Toast.makeText(this,
+ which,
+ Toast.LENGTH_SHORT).show();
+ }
+}
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/DownloadBase.java b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/DownloadBase.java
new file mode 100644
index 000000000..34cf8e9a5
--- /dev/null
+++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/DownloadBase.java
@@ -0,0 +1,116 @@
+package edu.vuum.mocca;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.BitmapDrawable;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.ImageView;
+
+/**
+ * This class is used as a base for the two ThreadedDownloads
+ * Assignment Activities. It instantiates the UI and handles
+ * displaying images and getting text from the EditText object by
+ * making displayBitmap() and getUrlString() available to subclasses.
+ *
+ * This design creates a separation of concerns, where this class
+ * handles UI functionality, while subclasses handle any
+ * service-related functionality that downloads images.
+ *
+ * DownloadBase, which extends Activity and overrides its primitive
+ * operation onCreate(), is an example of the Template Method
+ * Pattern. More generically, any object that extends Activity and
+ * overrides its primitive methods such as onStart() or onPause() is
+ * also an example of the Template Method Pattern.
+ */
+public class DownloadBase extends Activity {
+ /**
+ * Used for debugging.
+ */
+ private final String TAG = this.getClass().getSimpleName();
+
+ /**
+ * This object is the reference to the text box that allows the user to
+ * input a URL to an image for downloading.
+ */
+ private EditText mEditText;
+
+ /**
+ * This object is a reference to the container of an image in the
+ * UI. When we finish downloading, we update this to display the
+ * image we just stored on the file system.
+ */
+ private ImageView mImageView;
+
+ /**
+ * The original bitmap (used for resetting the image).
+ */
+ private Bitmap mDefaultBitmap;
+
+ /**
+ * Store the current bitmap for testing purposes.
+ */
+ public Bitmap mCurrentBitmap;
+
+ /**
+ * Display the given file in the ImageView. Use
+ * BitmapFactory.decodeFile(). Store the bitmap used to update
+ * the file to make testing easier.
+ */
+ void displayBitmap (String pathname) {
+ mCurrentBitmap = BitmapFactory.decodeFile(pathname);
+
+ mImageView.setImageBitmap(mCurrentBitmap);
+ }
+
+ /**
+ * Gets the URL from the EditText.
+ */
+ String getUrlString () {
+ return mEditText.getText().toString();
+ }
+
+ /**
+ * Resets image to the default image stored with the program and
+ * reset the image default URL.
+ */
+ public void resetImage(View view) {
+ mImageView.setImageBitmap(mDefaultBitmap);
+ mCurrentBitmap = mDefaultBitmap;
+ mEditText.setText(getResources().getString(R.string.default_url));
+ Log.d(TAG, "reset Image");
+ }
+
+ /**
+ * This hook method is called when the Activity is initially
+ * created. It's where we setup the UI for the activity and
+ * initialize any objects that need to exist while the activity
+ * exists.
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ Log.d(getClass().getSimpleName(), "onCreate");
+ super.onCreate(savedInstanceState);
+
+ // Use the Android framework to create a User Interface for
+ // this activity. The interface that should be created is
+ // defined in activity_download.xml in the res/layout folder.
+ setContentView(R.layout.activity_download);
+
+ // Once the UI is created, get a reference to the instantiated
+ // EditText and ImageView objects by providing their ids to
+ // the Android framework.
+ mEditText = (EditText) findViewById(R.id.url);
+ mImageView = (ImageView) findViewById(R.id.imageView1);
+
+ // Store whatever image is originally displayed in the
+ // ImageView as a local Bitmap object so that we can quickly
+ // reset the image when a button is pressed.
+ mCurrentBitmap =
+ ((BitmapDrawable)(mImageView.getDrawable())).getBitmap();
+ mDefaultBitmap = mCurrentBitmap;
+ }
+}
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/DownloadIntentService.java b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/DownloadIntentService.java
new file mode 100644
index 000000000..5923c87e7
--- /dev/null
+++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/DownloadIntentService.java
@@ -0,0 +1,90 @@
+package edu.vuum.mocca;
+
+import android.app.IntentService;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Messenger;
+
+/**
+ * @class DownloadIntentService
+ *
+ * @brief This class extends the IntentService, which provides a
+ * framework that simplifies programming and processing Android
+ * Started Services concurrently.
+ *
+ * DownloadIntentService receives an Intent containing a URL
+ * (which is a type of URI) and a Messenger (which is an IPC
+ * mechanism). It downloads the file at the URL, stores it on
+ * the file system, then returns the path name to the caller
+ * using the supplied Messenger.
+ *
+ * The IntentService class implements the CommandProcessor
+ * pattern and the Template Method Pattern. The Messenger is
+ * used as part of the Active Object pattern.
+ */
+public class DownloadIntentService extends IntentService {
+ /**
+ * The default constructor for this service. Simply forwards
+ * construction to IntentService, passing in a name for the Thread
+ * that the service runs in.
+ */
+ public DownloadIntentService() {
+ super("IntentService Worker Thread");
+ }
+
+ /**
+ * Optionally allow the instantiator to specify the name of the
+ * thread this service runs in.
+ */
+ public DownloadIntentService(String name) {
+ super(name);
+ }
+
+ /**
+ * Make an intent that will start this service if supplied to
+ * startService() as a parameter.
+ *
+ * @param context The context of the calling component.
+ * @param handler The handler that the service should
+ * use to respond with a result
+ * @param uri The web URL of a file to download
+ *
+ * This method utilizes the Factory Method makeMessengerIntent()
+ * from the DownloadUtils class. The returned intent is a Command
+ * in the Command Processor Pattern. The intent contains a
+ * messenger, which plays the role of Proxy in the Active Object
+ * Pattern.
+ */
+ public static Intent makeIntent(Context context,
+ Handler handler,
+ String uri) {
+ // TODO - You fill in here to replace null with a call to the
+ // factory method in DownloadUtils that makes a Messenger
+ // Intent with the appropriate parameters.
+
+ return null;
+ }
+
+ /**
+ * Hook method called when a component calls startService() with
+ * the proper intent. This method serves as the Executor in the
+ * Command Processor Pattern. It receives an Intent, which serves
+ * as the Command, and executes some action based on that intent
+ * in the context of this service.
+ *
+ * This method is also a Hook Method in the Template Method
+ * Pattern. The Template class has an overall design goal and
+ * strategy, but it allows subclasses to how some steps in the
+ * strategy are implemented. For example, IntentService handles
+ * the creation and lifecycle of a started service, but allows a
+ * user to define what happens when an Intent is actually handled.
+ */
+ @Override
+ protected void onHandleIntent (Intent intent) {
+ // TODO - You fill in here with a call the appropriate helper
+ // method from the DownloadUtils class that downloads the uri
+ // in the intent and returns the file's pathname using a
+ // Messenger who's Bundle key is defined by DownloadUtils.MESSENGER_KEY
+ }
+}
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/DownloadUtils.java b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/DownloadUtils.java
new file mode 100644
index 000000000..84a300fa6
--- /dev/null
+++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/DownloadUtils.java
@@ -0,0 +1,259 @@
+package edu.vuum.mocca;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+
+import edu.vuum.mocca.R;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Base64;
+import android.util.Log;
+
+/**
+ * @class DownloadUtils
+ *
+ * @brief This class encapsulates several static methods so that all
+ * Services can access them without redefining them in each
+ * Service.
+ */
+public class DownloadUtils {
+ /**
+ * Used for debugging.
+ */
+ static final String TAG = "DownloadActivity";
+
+ /**
+ * The key used to store/retrieve a Messenger extra from a Bundle.
+ */
+ public static final String MESSENGER_KEY = "MESSENGER";
+
+ /**
+ * The key used to store/retrieve a file's pathname from a Bundle.
+ */
+ public static final String PATHNAME_KEY = "PATHNAME";
+
+ /**
+ * If you have access to a stable Internet connection for testing
+ * purposes, feel free to change this variable to false so it
+ * actually downloads the image from a remote server.
+ */
+ // TODO - You can change this to the appropriate setting for your
+ // environment.
+ static final boolean DOWNLOAD_OFFLINE = true;
+
+ /**
+ * Make an Intent which will start a service if provided as a
+ * parameter to startService().
+ *
+ * @param context The context of the calling component
+ * @param service The class of the service to be
+ * started. (For example, ThreadPoolDownloadService.class)
+ * @param handler The handler that the service should
+ * use to return a result.
+ * @param uri The web URL that the service should download
+ *
+ * This method is an example of the Factory Method Pattern,
+ * because it creates and returns a different Intent depending on
+ * the parameters provided to it.
+ *
+ * The Intent is used as the Command Request in the Command
+ * Processor Pattern when it is passed to the
+ * ThreadPoolDownloadService using startService().
+ *
+ * The Handler is used as the Proxy, Future, and Servant in the
+ * Active Object Pattern when it is passed a message, which serves
+ * as the Active Object, and acts depending on what the message
+ * contains.
+ *
+ * The handler *must* be wrapped in a Messenger to allow it to
+ * operate across process boundaries.
+ */
+ public static Intent makeMessengerIntent(Context context,
+ Class> service,
+ Handler handler,
+ String uri) {
+ Messenger messenger = new Messenger(handler);
+
+ Intent intent = new Intent(context,
+ service);
+ intent.putExtra(MESSENGER_KEY,
+ messenger);
+ intent.setData(Uri.parse(uri));
+
+ return intent;
+ }
+
+ /**
+ * Use the provided Messenger to send a Message to a Handler in
+ * another process.
+ *
+ * The provided string, outputPath, should be put into a Bundle
+ * and included with the sent Message.
+ */
+ public static void sendPath (String outputPath,
+ Messenger messenger) {
+ Message msg = Message.obtain();
+ Bundle data = new Bundle();
+ data.putString(PATHNAME_KEY,
+ outputPath);
+
+ // Make the Bundle the "data" of the Message.
+ msg.setData(data);
+
+ try {
+ // Send the Message back to the client Activity.
+ messenger.send(msg);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Download a file to the Android file system, then respond with
+ * the file location using the provided Messenger.
+ */
+ public static void downloadAndRespond(Context context,
+ Uri uri,
+ Messenger messenger) {
+ sendPath(DownloadUtils.downloadFile(context,
+ uri),
+ messenger);
+ }
+
+ /**
+ * The resource that we write to the file system in offline
+ * mode. Note that this must be the same image that the testing
+ * project expects. (found in res/drawable-nodpi and Options.java)
+ */
+ static final int OFFLINE_TEST_IMAGE = R.raw.dougs;
+
+ /**
+ * The file name that we should use to store the image in offline mode
+ */
+ static final String OFFLINE_FILENAME = "dougs.jpg";
+
+ /**
+ * Download the file located at the provided internet url using
+ * the URL class, store it on the android file system using
+ * openFileOutput(), and return the path to the file on disk.
+ *
+ * @param context the context in which to write the file
+ * @param uri the web url
+ *
+ * @return the path to the downloaded file on the file system
+ */
+ public static String downloadFile (Context context,
+ Uri uri) {
+
+ try {
+
+ // If we're offline, write the image in our resources to
+ // disk, then return that pathname.
+ if (DOWNLOAD_OFFLINE) {
+
+ // Store the image on the file system. We can store it
+ // as private since the test project runs in the same
+ // process as the target project
+ FileOutputStream out =
+ context.openFileOutput(OFFLINE_FILENAME, 0);
+
+ // Get a stream from the image resource
+ InputStream in =
+ context.getResources().openRawResource(OFFLINE_TEST_IMAGE);
+
+ // Write the resource to disk.
+ copy(in, out);
+ in.close();
+ out.close();
+
+ return context.getFilesDir().toString() + File.separator + OFFLINE_FILENAME;
+ }
+
+ // Otherwise, go ahead and download the file
+ else {
+ // Create a temp file.
+ final File file = getTemporaryFile(context,
+ uri.toString());
+ Log.d(TAG, " downloading to " + file);
+
+ // Download the contents at the URL, which should
+ // reference an image.
+ final InputStream in = (InputStream)
+ new URL(uri.toString()).getContent();
+ final OutputStream os =
+ new FileOutputStream(file);
+
+ // Copy the contents of the downloaded image to the
+ // temp file.
+ copy(in, os);
+ in.close();
+ os.close();
+
+ // Return the pathname of the temp file.
+ return file.getAbsolutePath();
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Exception while downloading. Returning null.");
+ Log.e(TAG, e.toString());
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ /**
+ * Create a temp file to store the result of a download.
+ *
+ * @param context
+ * @param url
+ * @return
+ * @throws IOException
+ */
+ static private File getTemporaryFile(final Context context,
+ final String url) throws IOException {
+
+ // This is what you'd normally call to get a unique temporary
+ // file, but for testing purposes we always name the file the
+ // same to avoid filling up student phones with numerous
+ // files!
+ // return context.getFileStreamPath(Base64.encodeToString(url.getBytes(),
+ // Base64.NO_WRAP)
+ // + System.currentTimeMillis());
+
+ return context.getFileStreamPath(Base64.encodeToString(url.getBytes(),
+ Base64.NO_WRAP));
+ }
+
+ /**
+ * Copy the contents of an InputStream into an OutputStream.
+ *
+ * @param in
+ * @param out
+ * @return
+ * @throws IOException
+ */
+ static public int copy(final InputStream in,
+ final OutputStream out) throws IOException {
+ final int BUFFER_LENGTH = 1024;
+ final byte[] buffer = new byte[BUFFER_LENGTH];
+ int totalRead = 0;
+ int read = 0;
+
+ while ((read = in.read(buffer)) != -1) {
+ out.write(buffer, 0, read);
+ totalRead += read;
+ }
+
+ return totalRead;
+ }
+}
diff --git a/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/ThreadPoolDownloadService.java b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/ThreadPoolDownloadService.java
new file mode 100644
index 000000000..aad90586c
--- /dev/null
+++ b/assignments/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/ThreadPoolDownloadService.java
@@ -0,0 +1,128 @@
+package edu.vuum.mocca;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Messenger;
+
+/**
+ * @class ThreadPoolDownloadService
+ *
+ * @brief This Service handles downloading several files concurrently
+ * within a pool of Threads. When it is created, it creates a
+ * ThreadPoolExecutor using the newFixedThreadPool() method of
+ * the Executors class.
+ *
+ * When this Service is started, it should be supplied with a
+ * URI for download and a Messenger. It downloads the URI
+ * supplied, stores it on the Android file system, then returns
+ * the pathname of the downloaded file using the supplied
+ * Messenger.
+ *
+ * This class implements the Synchronous Service layer of the
+ * Half-Sync/Half-Async pattern. It also implements a variant
+ * of the Factory Method pattern.
+ */
+public class ThreadPoolDownloadService extends Service {
+ /**
+ * A class constant that determines the maximum number of threads
+ * used to service download requests.
+ */
+ static final int MAX_THREADS = 4;
+
+ /**
+ * The ExecutorService that references a ThreadPool.
+ */
+ ExecutorService mExecutor;
+
+ /**
+ * Hook method called when the Service is created.
+ */
+ @Override
+ public void onCreate() {
+ // TODO - You fill in here to replace null with a new
+ // FixedThreadPool Executor that's configured to use
+ // MAX_THREADS. Use a factory method in the Executors class.
+
+ mExecutor = null;
+ }
+
+ /**
+ * Make an intent that will start this service if supplied to
+ * startService() as a parameter.
+ *
+ * @param context The context of the calling component.
+ * @param handler The handler that the service should
+ * use to respond with a result
+ * @param uri The web URL of a file to download
+ *
+ * This method utilizes the Factory Method makeMessengerIntent()
+ * from the DownloadUtils class. The returned intent is a Command
+ * in the Command Processor Pattern. The intent contains a
+ * messenger, which plays the role of Proxy in the Active Object
+ * Pattern.
+ */
+ public static Intent makeIntent(Context context,
+ Handler handler,
+ String uri) {
+ // TODO - You fill in here, by replacing null with an
+ // invocation of the appropriate factory method in
+ // DownloadUtils that makes a MessengerIntent.
+
+ return null;
+ }
+
+ /**
+ * Hook method called when a component calls startService() with
+ * the proper Intent.
+ */
+ @Override
+ public int onStartCommand(final Intent intent,
+ int flags,
+ int startId) {
+ // TODO - You fill in here to replace null with a new Runnable
+ // that the ThreadPoolExecutor will execute to download the
+ // image and respond to the client. The Runnable's run()
+ // method implementation should forward to the appropriate
+ // helper method from the DownloadUtils class that downloads
+ // the uri in the intent and returns the file's pathname using
+ // a Messenger who's Bundle key is defined by DownloadUtils.MESSENGER_KEY.
+
+ Runnable downloadRunnable = null;
+
+ mExecutor.execute(downloadRunnable);
+
+ // Tell the Android framework how to behave if this service is
+ // interrupted. In our case, we want to restart the service
+ // then re-deliver the intent so that all files are eventually
+ // downloaded.
+ return START_REDELIVER_INTENT;
+ }
+
+ /**
+ * Called when the service is destroyed, which is the last call
+ * the Service receives informing it to clean up any resources it
+ * holds.
+ */
+ @Override
+ public void onDestroy() {
+ // Ensure that the threads used by the ThreadPoolExecutor
+ // complete and are reclaimed by the system.
+
+ mExecutor.shutdown();
+ }
+
+ /**
+ * Return null since this class does not implement a Bound
+ * Service.
+ */
+ @Override
+ public IBinder onBind (Intent intent) {
+ return null;
+ }
+}
diff --git a/assignments/week-7-assignment-6/Assignment-Description.txt b/assignments/week-7-assignment-6/Assignment-Description.txt
new file mode 100644
index 000000000..35965a950
--- /dev/null
+++ b/assignments/week-7-assignment-6/Assignment-Description.txt
@@ -0,0 +1,174 @@
+Week 7: Programming Assignment 6
+
+Released Monday, June 23th, 2014
+Due Monday, July 7th, 2014
+
+In this assignment, you will review an Android application that
+contains potential security vulnerabilities and/or code abstractions
+that do not exhibit secure abstraction design best practices. You
+will identify these issues and complete a multiple choice auto-graded
+quiz on the coursera website. You will then be asked to fix a few of
+these problems in the code.
+
+Security is a continuing evolving field that requires diligance on the
+part of developers. As part of this assignment, you will need to read
+about 2 security flaws in the Android Firefox app and Facebook SDK.
+
+http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-1484
+
+http://blog.parse.com/2012/04/10/discovering-a-major-security-hole-in-facebooks-android-sdk/
+
+When evaluating the code, you should ensure that any data that is
+being taken as input from a permission-protected resource is not
+written to a location that could allow another app without that
+permission to access it.
+
+INSTRUCTIONS FOR PART 1
+
+For Part 1 there are three lines that create vulnerabilties that can
+leak sensitive data or permissions and two lines that are examples of
+poor security abstraction design that make it easy to do things
+insecurely. Please follow the directions below to setup the code base
+that you will be performing a security review on. When you have
+identified the flaws described below, submit your answers through the
+auto-graded quiz for Part 1:
+
+https://class.coursera.org/posa-002/quiz/attempt?quiz_id=193
+
+For Part 2, described below, you will need to fix a subset of these
+flaws and submit your fixes for peer grading.
+
+Note: One of the vulnerabilities in this assignment is patched in
+Android SDK Versions 4.1 and up. If you think that means it doesn't
+matter, you should know that only 14% of Android devices are using an
+SDK above 4.0! We therefore recommend that you create an AVD that is
+using Android 4.0 (api 14) for this assignment. Also, please make
+sure your AVD has SD card memory since this application relies heavily
+on external storage.
+
+Be warned that some AVD's using below SDK 4.0 have reported some unexpected
+behavior.
+
+The application you will be inspecting is called iRemember, which we
+have adapted from Prof. Porter's MOOC on "Programming Mobile
+Applications with Android Handheld Systems". This application allows
+users to create a "story" that consists of a time, date, title, image,
+audio recording, body text, tags, and location. The app stores this
+data in a database and allows yourself and other users to view them at
+a later time. Normally, this application would use a ContentProvider
+to facilitate online database interactions, but for simplicity we have
+re-written part of the application to use the SQLiteDatabase that is
+hosted by your device, so you don't need an internet connection to run
+the application.
+
+In this directory you'll find the source code for the iRemember
+application, as well as an apk called VulnCheck.apk. Once you install
+the iRemember app, you may also install VulnCheck.apk, which is a
+"malware" program that demonstrates how a malicious application might
+exploit the vulnerabilites present in iRemember. If you do not know
+how to install an apk on your (emulated) device, there are
+instructions at the bottom of this file. There are also instructions
+on how to emulate GPS location information in an emulated device.
+
+The main goal of this project is to get a taste of auditing real-world
+code for security vulnerabilities. To accurately identify the
+problems in iRemember, we recommend you watch the Part 2, Module 2
+Videos, which cover the types of mistakes you should be looking out
+for, as well as certain security patterns that should be used for best
+practice.
+
+These videos are available at
+
+https://class.coursera.org/posa-002/lecture
+
+To Import the Project into Eclipse:
+
+1. Do a "git pull" to download the new assignment into your local git repo, or
+ go to:
+
+https://github.com/douglascraigschmidt/POSA-14
+
+ And click "Download Zip" in the bottom right hand corner to download the latest
+ assignments. Unzip them wherever you like.
+
+2. Open Eclipse, and do File->Import->Existing Android Code->Browse
+
+3. Browse to where you downloaded this assignment's code and select ok.
+
+To Run the iRemember Application, right click on the
+'W7-A6-iRememberSecurity' project and select [Run As] -> [Android
+Application]
+
+To install VulnCheck.apk:
+
+1. Start your emulated device.
+
+2. Open a command line or terminal. If you don't know how to do this,
+ Google is your friend. It depends very much on which operating
+ system you're using.
+
+3. Try executing adb by typing "adb" and pressing Enter. If adb
+ executes, it should show a help menu, and you can skip Step 4. If
+ your terminal says something like "Executable not found," then
+ you'll need to manually locate adb in Step 4.
+
+4. The adb executable is located in the [android-sdks]/platform-tools
+ directory. You should open a terminal in this directory in order to
+ execute the adb executable. Again, if you don't know how to do
+ this, Google is your friend. On any platform, the "cd" (change
+ directory) command is usually useful.
+
+5. Make sure adb recognizes your device. Type "adb devices -l" and
+ press Enter. adb should list all the devices it is able to interact
+ with, and you should see your AVD. If not, make sure that you
+ actually started your AVD in step 1.
+
+6. Use adb's install command to install the apk. Type "adb install
+ $PATH_TO_APK", where $PATH_TO_APK is replaced by the path to
+ VulnCheck.apk on your system. If everything goes well, adb should
+ say something like "Success".
+
+7. Start the application. On your AVD, go into the applications
+ menu. You should see the application installed as
+ "W7-A6-VulnCheck".
+
+If you do not have any experience with the Android Debugger Bridge
+(adb), we highly recommend reading up on it here:
+
+http://www.vogella.com/tutorials/AndroidCommandLine/article.html
+http://developer.android.com/tools/help/adb.html
+
+To send GPS information to the AVD (from eclipse):
+
+1. Start your emulated device
+
+2. In eclipse, open the DDMS Perspective. (Window->Open Perspective->DDMS)
+
+3. In the devices tab (which should be on the left), select your device by clicking on it.
+
+4. Click on the emulator tab (which should be on the right). If the emulator tab is not there,
+ open it by doing Window->Show View->Other->Android->Emulator Control
+
+5. At the bottom of the emulator control tab, there should be a section called "Location Controls"
+ Enter the location information you would like to send to the device, then click "Send"
+
+If you are not using Eclipse (or you want to be really cool), you can
+use the emulator console, documented here:
+
+http://developer.android.com/tools/devices/emulator.html#console
+
+The key command is "geo fix".
+
+INSTRUCTIONS FOR PART 2
+
+In the second part of the assignment, you will need to fix two of the five
+security issues. Those two issues are found in the following files:
+
+. AndroidManifest.xml
+. LoginActivity.java
+
+Please modify these two files to fix the security issues in them.
+When you have correctly modified the files to fix these issues, the
+security vulnerability checker will no longer be able to record sound
+or get login information from iRemember. You should leave comments
+describing what you fixed and why.
diff --git a/assignments/week-7-assignment-6/W7-A6-VulnCheck.apk b/assignments/week-7-assignment-6/W7-A6-VulnCheck.apk
new file mode 100644
index 000000000..7c26274ae
Binary files /dev/null and b/assignments/week-7-assignment-6/W7-A6-VulnCheck.apk differ
diff --git a/assignments/week-7-assignment-6/iRemember/.classpath b/assignments/week-7-assignment-6/iRemember/.classpath
new file mode 100644
index 000000000..0461652ec
--- /dev/null
+++ b/assignments/week-7-assignment-6/iRemember/.classpath
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/assignments/week-7-assignment-6/iRemember/.gitignore b/assignments/week-7-assignment-6/iRemember/.gitignore
new file mode 100644
index 000000000..2cc713a52
--- /dev/null
+++ b/assignments/week-7-assignment-6/iRemember/.gitignore
@@ -0,0 +1,3 @@
+bin/
+gen/
+*.*~
diff --git a/assignments/week-7-assignment-6/iRemember/.project b/assignments/week-7-assignment-6/iRemember/.project
new file mode 100644
index 000000000..8498084ac
--- /dev/null
+++ b/assignments/week-7-assignment-6/iRemember/.project
@@ -0,0 +1,39 @@
+
+
+ W7-A6-iRememberSecurity
+
+
+
+
+
+ com.android.ide.eclipse.adt.ResourceManagerBuilder
+
+
+
+
+ com.android.ide.eclipse.adt.PreCompilerBuilder
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+ com.android.ide.eclipse.adt.ApkBuilder
+
+
+
+
+
+ com.android.ide.eclipse.adt.AndroidNature
+ org.eclipse.m2e.core.maven2Nature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/assignments/week-7-assignment-6/iRemember/AndroidManifest.xml b/assignments/week-7-assignment-6/iRemember/AndroidManifest.xml
new file mode 100644
index 000000000..628dc6d57
--- /dev/null
+++ b/assignments/week-7-assignment-6/iRemember/AndroidManifest.xml
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/assignments/week-7-assignment-6/iRemember/Copyright and Licensing Information.txt b/assignments/week-7-assignment-6/iRemember/Copyright and Licensing Information.txt
new file mode 100644
index 000000000..72fdbd2bd
--- /dev/null
+++ b/assignments/week-7-assignment-6/iRemember/Copyright and Licensing Information.txt
@@ -0,0 +1,45 @@
+The iRemember source code (henceforth referred to as "iRemember") is
+copyrighted by Mike Walker, Adam Porter, Doug Schmidt, and Jules White
+at Vanderbilt University and the University of Maryland, Copyright (c)
+2014, all rights reserved. Since iRemember is open-source, freely
+available software, you are free to use, modify, copy, and
+distribute--perpetually and irrevocably--the source code and object code
+produced from the source, as well as copy and distribute modified
+versions of this software. You must, however, include this copyright
+statement along with any code built using iRemember that you release. No
+copyright statement needs to be provided if you just ship binary
+executables of your software products.
+
+You can use iRemember software in commercial and/or binary software
+releases and are under no obligation to redistribute any of your source
+code that is built using the software. Note, however, that you may not
+misappropriate the iRemember code, such as copyrighting it yourself or
+claiming authorship of the iRemember software code, in a way that will
+prevent the software from being distributed freely using an open-source
+development model. You needn't inform anyone that you're using iRemember
+software in your software, though we encourage you to let us know so we
+can promote your project in our success stories.
+
+iRemember is provided as is with no warranties of any kind, including
+the warranties of design, merchantability, and fitness for a particular
+purpose, noninfringement, or arising from a course of dealing, usage or
+trade practice. Vanderbilt University and University of Maryland, their
+employees, and students shall have no liability with respect to the
+infringement of copyrights, trade secrets or any patents by DOC software
+or any part thereof. Moreover, in no event will Vanderbilt University,
+University of Maryland, their employees, or students be liable for any
+lost revenue or profits or other special, indirect and consequential
+damages.
+
+iRemember is provided with no support and without any obligation on the
+part of Vanderbilt University and University of Maryland, their
+employees, or students to assist in its use, correction, modification,
+or enhancement.
+
+The names Vanderbilt University and University of Maryland may not be
+used to endorse or promote products or services derived from this source
+without express written permission from Vanderbilt University or
+University of Maryland. This license grants no permission to call
+products or services derived from the iRemember source, nor does it
+grant permission for the name Washington Vanderbilt University or
+University of Maryland to appear in their names.
diff --git a/assignments/week-7-assignment-6/iRemember/ic_launcher-web.png b/assignments/week-7-assignment-6/iRemember/ic_launcher-web.png
new file mode 100644
index 000000000..35ffdc2e4
Binary files /dev/null and b/assignments/week-7-assignment-6/iRemember/ic_launcher-web.png differ
diff --git a/assignments/week-7-assignment-6/iRemember/libs/android-support-v4.jar b/assignments/week-7-assignment-6/iRemember/libs/android-support-v4.jar
new file mode 100644
index 000000000..187bdf48b
Binary files /dev/null and b/assignments/week-7-assignment-6/iRemember/libs/android-support-v4.jar differ
diff --git a/assignments/week-7-assignment-6/iRemember/libs/slf4j-api-1.7.5.jar b/assignments/week-7-assignment-6/iRemember/libs/slf4j-api-1.7.5.jar
new file mode 100644
index 000000000..8766455d8
Binary files /dev/null and b/assignments/week-7-assignment-6/iRemember/libs/slf4j-api-1.7.5.jar differ
diff --git a/assignments/week-7-assignment-6/iRemember/lint.xml b/assignments/week-7-assignment-6/iRemember/lint.xml
new file mode 100644
index 000000000..ee0eead5b
--- /dev/null
+++ b/assignments/week-7-assignment-6/iRemember/lint.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/assignments/week-7-assignment-6/iRemember/proguard-project.txt b/assignments/week-7-assignment-6/iRemember/proguard-project.txt
new file mode 100644
index 000000000..f2fe1559a
--- /dev/null
+++ b/assignments/week-7-assignment-6/iRemember/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/assignments/week-7-assignment-6/iRemember/project.properties b/assignments/week-7-assignment-6/iRemember/project.properties
new file mode 100644
index 000000000..a3ee5ab64
--- /dev/null
+++ b/assignments/week-7-assignment-6/iRemember/project.properties
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-17
diff --git a/ex/ThreadedDownload/res/drawable-hdpi/ic_action_search.png b/assignments/week-7-assignment-6/iRemember/res/drawable-hdpi/ic_action_search.png
similarity index 100%
rename from ex/ThreadedDownload/res/drawable-hdpi/ic_action_search.png
rename to assignments/week-7-assignment-6/iRemember/res/drawable-hdpi/ic_action_search.png
diff --git a/assignments/week-7-assignment-6/iRemember/res/drawable-hdpi/ic_launcher.png b/assignments/week-7-assignment-6/iRemember/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 000000000..567a28842
Binary files /dev/null and b/assignments/week-7-assignment-6/iRemember/res/drawable-hdpi/ic_launcher.png differ
diff --git a/assignments/week-7-assignment-6/iRemember/res/drawable-ldpi/ic_launcher.png b/assignments/week-7-assignment-6/iRemember/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 000000000..ff354ce13
Binary files /dev/null and b/assignments/week-7-assignment-6/iRemember/res/drawable-ldpi/ic_launcher.png differ
diff --git a/assignments/week-7-assignment-6/iRemember/res/drawable-mdpi/ic_launcher.png b/assignments/week-7-assignment-6/iRemember/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 000000000..5790fdf25
Binary files /dev/null and b/assignments/week-7-assignment-6/iRemember/res/drawable-mdpi/ic_launcher.png differ
diff --git a/assignments/week-7-assignment-6/iRemember/res/drawable-xhdpi/ic_launcher.png b/assignments/week-7-assignment-6/iRemember/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..1aade49c5
Binary files /dev/null and b/assignments/week-7-assignment-6/iRemember/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/assignments/week-7-assignment-6/iRemember/res/layout-sw600dp/main.xml b/assignments/week-7-assignment-6/iRemember/res/layout-sw600dp/main.xml
new file mode 100644
index 000000000..8b4bde08c
--- /dev/null
+++ b/assignments/week-7-assignment-6/iRemember/res/layout-sw600dp/main.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/assignments/week-7-assignment-6/iRemember/res/layout/create_story_activity.xml b/assignments/week-7-assignment-6/iRemember/res/layout/create_story_activity.xml
new file mode 100644
index 000000000..3d0d00e5a
--- /dev/null
+++ b/assignments/week-7-assignment-6/iRemember/res/layout/create_story_activity.xml
@@ -0,0 +1,414 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/assignments/week-7-assignment-6/iRemember/res/layout/edit_story_activity.xml b/assignments/week-7-assignment-6/iRemember/res/layout/edit_story_activity.xml
new file mode 100644
index 000000000..f759e5571
--- /dev/null
+++ b/assignments/week-7-assignment-6/iRemember/res/layout/edit_story_activity.xml
@@ -0,0 +1,380 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/assignments/week-7-assignment-6/iRemember/res/layout/list_story_activity.xml b/assignments/week-7-assignment-6/iRemember/res/layout/list_story_activity.xml
new file mode 100644
index 000000000..1a4e46d7e
--- /dev/null
+++ b/assignments/week-7-assignment-6/iRemember/res/layout/list_story_activity.xml
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/assignments/week-7-assignment-6/iRemember/res/layout/login_activity.xml b/assignments/week-7-assignment-6/iRemember/res/layout/login_activity.xml
new file mode 100644
index 000000000..ce83cc53a
--- /dev/null
+++ b/assignments/week-7-assignment-6/iRemember/res/layout/login_activity.xml
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/assignments/week-7-assignment-6/iRemember/res/layout/story_listview_custom_row.xml b/assignments/week-7-assignment-6/iRemember/res/layout/story_listview_custom_row.xml
new file mode 100644
index 000000000..a0944cbd0
--- /dev/null
+++ b/assignments/week-7-assignment-6/iRemember/res/layout/story_listview_custom_row.xml
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/assignments/week-7-assignment-6/iRemember/res/layout/view_story_activity.xml b/assignments/week-7-assignment-6/iRemember/res/layout/view_story_activity.xml
new file mode 100644
index 000000000..87aefeb16
--- /dev/null
+++ b/assignments/week-7-assignment-6/iRemember/res/layout/view_story_activity.xml
@@ -0,0 +1,395 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/assignments/week-5-assignment-4/res/menu/ping_pong.xml b/assignments/week-7-assignment-6/iRemember/res/menu/main.xml
similarity index 100%
rename from assignments/week-5-assignment-4/res/menu/ping_pong.xml
rename to assignments/week-7-assignment-6/iRemember/res/menu/main.xml
diff --git a/assignments/week-7-assignment-6/iRemember/res/menu/sound_record.xml b/assignments/week-7-assignment-6/iRemember/res/menu/sound_record.xml
new file mode 100644
index 000000000..c00202823
--- /dev/null
+++ b/assignments/week-7-assignment-6/iRemember/res/menu/sound_record.xml
@@ -0,0 +1,9 @@
+
diff --git a/assignments/week-7-assignment-6/iRemember/res/values-sw600dp/account_preferences.xml b/assignments/week-7-assignment-6/iRemember/res/values-sw600dp/account_preferences.xml
new file mode 100644
index 000000000..f93ad54c1
--- /dev/null
+++ b/assignments/week-7-assignment-6/iRemember/res/values-sw600dp/account_preferences.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+ false
+
+
diff --git a/assignments/week-5-assignment-4/res/values-sw600dp/dimens.xml b/assignments/week-7-assignment-6/iRemember/res/values-sw600dp/dimens.xml
similarity index 100%
rename from assignments/week-5-assignment-4/res/values-sw600dp/dimens.xml
rename to assignments/week-7-assignment-6/iRemember/res/values-sw600dp/dimens.xml
diff --git a/assignments/week-5-assignment-4/res/values-sw720dp-land/dimens.xml b/assignments/week-7-assignment-6/iRemember/res/values-sw720dp-land/dimens.xml
similarity index 100%
rename from assignments/week-5-assignment-4/res/values-sw720dp-land/dimens.xml
rename to assignments/week-7-assignment-6/iRemember/res/values-sw720dp-land/dimens.xml
diff --git a/assignments/week-7-assignment-6/iRemember/res/values/dimens.xml b/assignments/week-7-assignment-6/iRemember/res/values/dimens.xml
new file mode 100644
index 000000000..56e256b6f
--- /dev/null
+++ b/assignments/week-7-assignment-6/iRemember/res/values/dimens.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+ 16dp
+ 16dp
+
+
diff --git a/assignments/week-7-assignment-6/iRemember/res/values/strings.xml b/assignments/week-7-assignment-6/iRemember/res/values/strings.xml
new file mode 100644
index 000000000..a4652bf52
--- /dev/null
+++ b/assignments/week-7-assignment-6/iRemember/res/values/strings.xml
@@ -0,0 +1,299 @@
+
+
+
+
+
+
+ iRemember
+ Settings
+ iRemember
+
+ edu.vuum.mocca.provider.MoocProvider
+
+
+
+ #00ffff
+ #808080
+ #add8e6
+
+
+
+
+ Delete
+ Edit
+ Closing Activity
+ Are you sure you want to Delete this Entry?
+ Yes
+ No
+
+
+ LoginId
+ StoryId
+ Title
+ Body
+ AudioLink
+ VideoLink
+ ImageName
+ ImageData
+ Tags
+ CreationTime
+ StoryTime
+ Latitude
+ Longitude
+
+
+
+
+ LoginId
+ StoryId
+ Title
+ Body
+ AudioLink
+ VideoLink
+ ImageName
+ ImageData
+ Tags
+ Story Recording Date
+ Story Occurrence Date
+ Latitude
+ Longitude
+
+
+ Delete
+ Edit
+ Closing Activity
+ Are you sure you want to Delete this Entry?
+ Yes
+ No
+
+
+ LoginId
+ StoryId
+ Tag
+
+
+
+
+ LoginId
+ StoryId
+ Tag
+
+
+
+
+
+
+
+ ADD
+ REFRESH
+ Tag Filter...
+
+
+ ADD
+ REFRESH
+
+
+
+
+
+
+
+ loginId:
+ storyId:
+ title:
+ body:
+ audioLink:
+ videoLink:
+ imageName:
+ imageMetaData:
+ tags:
+ creationTime:
+ storyTime:
+ latitude:
+ longitude:
+
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+ 0
+ 0
+ 0.0
+ 0.0
+
+
+ Clear
+ Cancel
+ Create
+ Record
+ Save
+ Reset
+ Cancel
+
+
+ loginId:
+ storyId:
+ tag:
+
+
+
+
+ 0
+ 0
+
+
+
+ Clear
+ Cancel
+ Create
+ Save
+ Reset
+ Cancel
+
+
+
+
+
+
+
+ LoginId
+ StoryId
+ Title
+ Body
+ AudioLink
+ VideoLink
+ ImageName
+ Image Name
+ Tags
+ CreationTime
+ StoryTime
+ Latitude
+ Longitude
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Login (Please note that this is an example app for the Coursera mooc.
+ The app should function correctly no matter what login is input)
+ Login
+ Password:
+ Login ID:
+
+
+ Save
+ Reset
+ Cancel
+
+
+
+ LoginId
+ StoryId
+ Tag
+
+
+
+
+
+
+
+
+
+
+
+
+ Save
+ Reset
+ Cancel
+
+
+
+
+
+ edu.vanderbilt.SyncProviderDemo.account
+ Server Address::
+ Username:
+ username Hint!
+ Password:
+ password Hint!
+ Login
+ Authenticating
+ Settings
+ Hello world!
+ Record
+ SoundRecordActivity
+ Login (Please note that this is an example app for the Coursera mooc.
+ The app should function correctly no matter what login is input)
+
+
+
+
diff --git a/assignments/week-7-assignment-6/iRemember/res/values/styles.xml b/assignments/week-7-assignment-6/iRemember/res/values/styles.xml
new file mode 100644
index 000000000..93fc04a1f
--- /dev/null
+++ b/assignments/week-7-assignment-6/iRemember/res/values/styles.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/assignments/week-7-assignment-6/iRemember/src/edu/vuum/mocca/provider/MoocSchema.java b/assignments/week-7-assignment-6/iRemember/src/edu/vuum/mocca/provider/MoocSchema.java
new file mode 100644
index 000000000..0107c5125
--- /dev/null
+++ b/assignments/week-7-assignment-6/iRemember/src/edu/vuum/mocca/provider/MoocSchema.java
@@ -0,0 +1,182 @@
+/*
+The iRemember source code (henceforth referred to as "iRemember") is
+copyrighted by Mike Walker, Adam Porter, Doug Schmidt, and Jules White
+at Vanderbilt University and the University of Maryland, Copyright (c)
+2014, all rights reserved. Since iRemember is open-source, freely
+available software, you are free to use, modify, copy, and
+distribute--perpetually and irrevocably--the source code and object code
+produced from the source, as well as copy and distribute modified
+versions of this software. You must, however, include this copyright
+statement along with any code built using iRemember that you release. No
+copyright statement needs to be provided if you just ship binary
+executables of your software products.
+
+You can use iRemember software in commercial and/or binary software
+releases and are under no obligation to redistribute any of your source
+code that is built using the software. Note, however, that you may not
+misappropriate the iRemember code, such as copyrighting it yourself or
+claiming authorship of the iRemember software code, in a way that will
+prevent the software from being distributed freely using an open-source
+development model. You needn't inform anyone that you're using iRemember
+software in your software, though we encourage you to let us know so we
+can promote your project in our success stories.
+
+iRemember is provided as is with no warranties of any kind, including
+the warranties of design, merchantability, and fitness for a particular
+purpose, noninfringement, or arising from a course of dealing, usage or
+trade practice. Vanderbilt University and University of Maryland, their
+employees, and students shall have no liability with respect to the
+infringement of copyrights, trade secrets or any patents by DOC software
+or any part thereof. Moreover, in no event will Vanderbilt University,
+University of Maryland, their employees, or students be liable for any
+lost revenue or profits or other special, indirect and consequential
+damages.
+
+iRemember is provided with no support and without any obligation on the
+part of Vanderbilt University and University of Maryland, their
+employees, or students to assist in its use, correction, modification,
+or enhancement.
+
+The names Vanderbilt University and University of Maryland may not be
+used to endorse or promote products or services derived from this source
+without express written permission from Vanderbilt University or
+University of Maryland. This license grants no permission to call
+products or services derived from the iRemember source, nor does it
+grant permission for the name Vanderbilt University or
+University of Maryland to appear in their names.
+ */
+
+package edu.vuum.mocca.provider;
+
+import android.content.ContentValues;
+import android.net.Uri;
+import android.provider.BaseColumns;
+
+/**
+ * A schema is defined as an overall method of organization for some set of data.
+ *
+ * This class contains many class constants that help organize data storage
+ * and database access.
+ */
+public class MoocSchema {
+
+ /**
+ * Project Related Constants
+ */
+
+ public static final String ORGANIZATIONAL_NAME = "edu.vanderbilt";
+ public static final String PROJECT_NAME = "mooc";
+
+ /**
+ * ConentProvider Related Constants
+ */
+ public static final String AUTHORITY = ORGANIZATIONAL_NAME + "."
+ + PROJECT_NAME + ".moocprovider";
+ private static final Uri BASE_URI = Uri.parse("content://" + AUTHORITY);
+
+ // Define a static class that represents description of stored content
+ // entity.
+ public static class Story {
+ // define a URI paths to access entity
+ // BASE_URI/story - for list of story(s)
+ // BASE_URI/story/* - retrieve specific story by id
+ public static final String PATH = "story";
+ public static final int PATH_TOKEN = 110;
+
+ public static final String PATH_FOR_ID = "story/*";
+ public static final int PATH_FOR_ID_TOKEN = 120;
+
+ // URI for all content stored as story entity
+ public static final Uri CONTENT_URI = BASE_URI.buildUpon()
+ .appendPath(PATH).build();
+
+ private final static String MIME_TYPE_END = "story";
+
+ // define the MIME type of data in the content provider
+ public static final String CONTENT_TYPE_DIR = ORGANIZATIONAL_NAME
+ + ".cursor.dir/" + ORGANIZATIONAL_NAME + "." + MIME_TYPE_END;
+ public static final String CONTENT_ITEM_TYPE = ORGANIZATIONAL_NAME
+ + ".cursor.item/" + ORGANIZATIONAL_NAME + "." + MIME_TYPE_END;
+
+ // the names and order of ALL columns, including internal use ones
+ public static final String[] ALL_COLUMN_NAMES = { Cols.ID,
+ Cols.LOGIN_ID, Cols.STORY_ID, Cols.TITLE, Cols.BODY,
+ Cols.AUDIO_LINK, Cols.VIDEO_LINK, Cols.IMAGE_NAME,
+ Cols.IMAGE_LINK, Cols.TAGS, Cols.CREATION_TIME,
+ Cols.STORY_TIME, Cols.LATITUDE, Cols.LONGITUDE };
+
+ // the names and order of ALL column types, including internal use ones (for use with SQLite)
+ public static final String[] ALL_COLUMN_TYPES = { "integer",
+ "integer", "integer", "text", "text",
+ "text", "text", "text",
+ "text", "text", "integer",
+ "integer", "integer", "integer" };
+
+ public static ContentValues initializeWithDefault(
+ final ContentValues assignedValues) {
+ // final Long now = Long.valueOf(System.currentTimeMillis());
+ final ContentValues setValues = (assignedValues == null) ? new ContentValues()
+ : assignedValues;
+ if (!setValues.containsKey(Cols.LOGIN_ID)) {
+ setValues.put(Cols.LOGIN_ID, 0);
+ }
+ if (!setValues.containsKey(Cols.STORY_ID)) {
+ setValues.put(Cols.STORY_ID, 0);
+ }
+ if (!setValues.containsKey(Cols.TITLE)) {
+ setValues.put(Cols.TITLE, "");
+ }
+ if (!setValues.containsKey(Cols.BODY)) {
+ setValues.put(Cols.BODY, "");
+ }
+ if (!setValues.containsKey(Cols.AUDIO_LINK)) {
+ setValues.put(Cols.AUDIO_LINK, "");
+ }
+ if (!setValues.containsKey(Cols.VIDEO_LINK)) {
+ setValues.put(Cols.VIDEO_LINK, "");
+ }
+ if (!setValues.containsKey(Cols.IMAGE_NAME)) {
+ setValues.put(Cols.IMAGE_NAME, "");
+ }
+ if (!setValues.containsKey(Cols.IMAGE_LINK)) {
+ setValues.put(Cols.IMAGE_LINK, "");
+ }
+ if (!setValues.containsKey(Cols.TAGS)) {
+ setValues.put(Cols.TAGS, "");
+ }
+ if (!setValues.containsKey(Cols.CREATION_TIME)) {
+ setValues.put(Cols.CREATION_TIME, 0);
+ }
+ if (!setValues.containsKey(Cols.STORY_TIME)) {
+ setValues.put(Cols.STORY_TIME, 0);
+ }
+ if (!setValues.containsKey(Cols.LATITUDE)) {
+ setValues.put(Cols.LATITUDE, 0);
+ }
+ if (!setValues.containsKey(Cols.LONGITUDE)) {
+ setValues.put(Cols.LONGITUDE, 0);
+ }
+ return setValues;
+ }
+
+ // a static class to store columns in entity
+ public static class Cols {
+ public static final String ID = BaseColumns._ID; // convention
+ // The name and column index of each column in your database
+ public static final String LOGIN_ID = "LOGIN_ID";
+ public static final String STORY_ID = "STORY_ID";
+ public static final String TITLE = "TITLE";
+ public static final String BODY = "BODY";
+ public static final String AUDIO_LINK = "AUDIO_LINK";
+ public static final String VIDEO_LINK = "VIDEO_LINK";
+ public static final String IMAGE_NAME = "IMAGE_NAME";
+ public static final String IMAGE_LINK = "IMAGE_LINK";
+ public static final String TAGS = "TAGS";
+ public static final String CREATION_TIME = "CREATION_TIME";
+ public static final String STORY_TIME = "STORY_TIME";
+ public static final String LATITUDE = "LATITUDE";
+ public static final String LONGITUDE = "LONGITUDE";
+ }
+ }
+
+}
diff --git a/assignments/week-7-assignment-6/iRemember/src/edu/vuum/mocca/storage/MoocResolver.java b/assignments/week-7-assignment-6/iRemember/src/edu/vuum/mocca/storage/MoocResolver.java
new file mode 100644
index 000000000..0df08b4ca
--- /dev/null
+++ b/assignments/week-7-assignment-6/iRemember/src/edu/vuum/mocca/storage/MoocResolver.java
@@ -0,0 +1,298 @@
+/*
+The iRemember source code (henceforth referred to as "iRemember") is
+copyrighted by Mike Walker, Adam Porter, Doug Schmidt, and Jules White
+at Vanderbilt University and the University of Maryland, Copyright (c)
+2014, all rights reserved. Since iRemember is open-source, freely
+available software, you are free to use, modify, copy, and
+distribute--perpetually and irrevocably--the source code and object code
+produced from the source, as well as copy and distribute modified
+versions of this software. You must, however, include this copyright
+statement along with any code built using iRemember that you release. No
+copyright statement needs to be provided if you just ship binary
+executables of your software products.
+
+You can use iRemember software in commercial and/or binary software
+releases and are under no obligation to redistribute any of your source
+code that is built using the software. Note, however, that you may not
+misappropriate the iRemember code, such as copyrighting it yourself or
+claiming authorship of the iRemember software code, in a way that will
+prevent the software from being distributed freely using an open-source
+development model. You needn't inform anyone that you're using iRemember
+software in your software, though we encourage you to let us know so we
+can promote your project in our success stories.
+
+iRemember is provided as is with no warranties of any kind, including
+the warranties of design, merchantability, and fitness for a particular
+purpose, noninfringement, or arising from a course of dealing, usage or
+trade practice. Vanderbilt University and University of Maryland, their
+employees, and students shall have no liability with respect to the
+infringement of copyrights, trade secrets or any patents by DOC software
+or any part thereof. Moreover, in no event will Vanderbilt University,
+University of Maryland, their employees, or students be liable for any
+lost revenue or profits or other special, indirect and consequential
+damages.
+
+iRemember is provided with no support and without any obligation on the
+part of Vanderbilt University and University of Maryland, their
+employees, or students to assist in its use, correction, modification,
+or enhancement.
+
+The names Vanderbilt University and University of Maryland may not be
+used to endorse or promote products or services derived from this source
+without express written permission from Vanderbilt University or
+University of Maryland. This license grants no permission to call
+products or services derived from the iRemember source, nor does it
+grant permission for the name Vanderbilt University or
+University of Maryland to appear in their names.
+ */
+
+package edu.vuum.mocca.storage;
+
+import java.util.ArrayList;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.os.RemoteException;
+import android.util.Log;
+import edu.vuum.mocca.provider.MoocSchema;
+
+/**
+ * This used to be a wrapper for a ContentResolver to a single URI, which would be used to interface
+ * with a ContentProvider which communicates with a server on the web.
+ *
+ * However, to simplify this assignment and remove any dependencies on the database server being online,
+ * we removed the ContentResolver and instead are storing StoryData in the default SQLite Database that
+ * is hosted by the device.
+ *
+ */
+public class MoocResolver {
+
+ // A private instance of our locally defined mSQLiteOpenHelper
+ private mSQLiteOpenHelper helper;
+
+ // An extension of SQLiteOpenHelper which helps us create and manage
+ // an SQLiteDatabase
+ static class mSQLiteOpenHelper extends SQLiteOpenHelper {
+
+ // Simply forward construction to the super class
+ public mSQLiteOpenHelper (Context context) {
+ super(context, "iRememberSecurityDatabase", null, 1);
+ }
+
+ // When the database is created, create a table to store our story data, if it does not exist.
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+
+ StringBuilder createTable = new StringBuilder();
+ createTable.append("create table if not exists " + tableName + " (");
+ createTable.append(MoocSchema.Story.Cols.ID + " integer primary key autoincrement ");
+
+ String [] names = MoocSchema.Story.ALL_COLUMN_NAMES;
+ String [] types = MoocSchema.Story.ALL_COLUMN_TYPES;
+
+ for (int i = 1; i < names.length; ++i) {
+ createTable.append(", " + names[i] + " " +types[i]);
+ }
+
+ createTable.append(");");
+
+ Log.d("MoocResolver", "onCreate() called: " + createTable.toString());
+
+ try {
+ db.execSQL(createTable.toString());
+ }
+ catch (SQLException e) {
+ Log.e("MoocResolver", e.getMessage());
+ }
+ }
+
+ // We won't worry about upgrading our database in this simple version.
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ }
+
+ }
+
+ // The name of the table that will store our story data in the database
+ private static final String tableName = "iRememberTable";
+
+ /**
+ * Constructor
+ *
+ * @param context The context in which to create the database.
+ */
+ public MoocResolver(Context context) {
+ // Create a new instance of mSQLiteOpenHelper to manage the database
+ helper = new mSQLiteOpenHelper(context);
+ }
+
+ /**
+ * When the user of this class is done with it, make sure to close the database.
+ */
+ @Override
+ public void finalize() {
+ if (helper != null)
+ helper.close();
+ }
+
+ /*
+ * Delete for each ORM Data Type
+ */
+ /**
+ * Delete all StoryData(s) from the database that match the
+ * selectionArgs
+ *
+ * @param selection
+ * @param selectionArgs
+ * @return number of StoryData rows deleted
+ * @throws RemoteException
+ */
+ public int deleteStoryData(final String selection,
+ final String[] selectionArgs) throws RemoteException {
+ SQLiteDatabase db = helper.getWritableDatabase();
+ int res = db.delete(tableName, selection, selectionArgs);
+ return res;
+ }
+
+ /**
+ * Insert a new StoryData object into the database
+ *
+ * @param storyObject
+ * object to be inserted
+ * @return row ID of inserted StoryData in the ContentProvider
+ * @throws RemoteException
+ */
+ public long insert(final StoryData storyObject) throws RemoteException {
+ ContentValues tempCV = storyObject.getCV();
+ tempCV.remove(MoocSchema.Story.Cols.ID);
+ SQLiteDatabase db = helper.getWritableDatabase();
+ long res = db.insert(tableName, null, tempCV);
+ db.close();
+ return res;
+ }
+
+ /*
+ * Query for each ORM Data Type
+ */
+
+ /**
+ * Query the database for StoryData conforming to certain specifications.
+ *
+ * @param projection
+ * @param selection
+ * @param selectionArgs
+ * @param sortOrder
+ * @return an ArrayList of StoryData objects
+ * @throws RemoteException
+ */
+ public ArrayList queryStoryData(final String[] projection,
+ final String selection, final String[] selectionArgs,
+ final String sortOrder) throws RemoteException {
+ // query the database
+ SQLiteDatabase db = helper.getReadableDatabase();
+ Cursor result = db.query(tableName, projection, selection,
+ selectionArgs, null, null, sortOrder);
+ // make return object
+ ArrayList rValue = new ArrayList();
+ // convert cursor to reutrn object
+ rValue.addAll(StoryCreator.getStoryDataArrayListFromCursor(result));
+ result.close();
+
+ //close the database
+ db.close();
+
+ // return 'return object'
+ return rValue;
+ }
+
+ /*
+ * Update for each ORM Data Type
+ */
+
+ /**
+ * Update the specified StoryData with new values.
+ *
+ * @param values
+ * @param selection
+ * @param selectionArgs
+ * @return number of rows changed
+ * @throws RemoteException
+ */
+ public int updateStoryData(final StoryData values, final String selection,
+ final String[] selectionArgs) throws RemoteException {
+
+ SQLiteDatabase db = helper.getWritableDatabase();
+ int res = db.update(tableName, values.getCV(), selection, selectionArgs);
+ db.close();
+ return res;
+ }
+
+ /*
+ * Sample extensions of above for customized additional methods for classes
+ * that extend this one
+ */
+
+ /**
+ * Get all the StoryData objects current stored in the Content Provider
+ *
+ * @return an ArrayList containing all the StoryData objects
+ * @throws RemoteException
+ */
+ public ArrayList getAllStoryData() throws RemoteException {
+ return queryStoryData(null, null, null, null);
+ }
+
+ /**
+ * Get a StoryData from the data stored at the given rowID
+ *
+ * @param rowID
+ * @return StoryData at the given rowID
+ * @throws RemoteException
+ */
+ public StoryData getStoryDataViaRowID(final long rowID)
+ throws RemoteException {
+ String[] selectionArgs = { String.valueOf(rowID) };
+ ArrayList results = queryStoryData(null,
+ MoocSchema.Story.Cols.ID + "= ?", selectionArgs, null);
+ if (results.size() > 0) {
+ return results.get(0);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Delete All rows, from AllStory table, that have the given rowID. (Should
+ * only be 1 row, but Content Providers/SQLite3 deletes all rows with
+ * provided rowID)
+ *
+ * @param rowID
+ * @return number of rows deleted
+ * @throws RemoteException
+ */
+ public int deleteAllStoryWithRowID(long rowID) throws RemoteException {
+ String[] args = { String.valueOf(rowID) };
+ return deleteStoryData(MoocSchema.Story.Cols.ID + " = ? ", args);
+ }
+
+ /**
+ * Updates all StoryData stored with the provided StoryData's 'KEY_ID'
+ * (should only be 1 row of data in the content provider, but content
+ * provider implementation will update EVERY row that matches.)
+ *
+ * @param data
+ * @return number of rows altered
+ * @throws RemoteException
+ */
+ public int updateStoryWithID(StoryData data) throws RemoteException {
+ String selection = "_id = ?";
+ String[] selectionArgs = { String.valueOf(data.KEY_ID) };
+ return updateStoryData(data, selection, selectionArgs);
+ }
+
+
+}
\ No newline at end of file
diff --git a/assignments/week-7-assignment-6/iRemember/src/edu/vuum/mocca/storage/StorageUtilities.java b/assignments/week-7-assignment-6/iRemember/src/edu/vuum/mocca/storage/StorageUtilities.java
new file mode 100644
index 000000000..4ecf33dfd
--- /dev/null
+++ b/assignments/week-7-assignment-6/iRemember/src/edu/vuum/mocca/storage/StorageUtilities.java
@@ -0,0 +1,134 @@
+package edu.vuum.mocca.storage;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Environment;
+import android.util.Log;
+import android.widget.Toast;
+
+/**
+ * This utility class provides several options for storing temporary and permanent files on
+ * the file system with varying degrees of security.
+ */
+public class StorageUtilities {
+
+ // Log tag used for debugging with Logcat
+ public static final String LOG_TAG = StorageUtilities.class.getCanonicalName();
+
+ // Constant that denote whether a file should be stored publicly or privately
+ public static final int SECURITY_PUBLIC = 0; // Line 24
+ public static final int SECURITY_PRIVATE = 1;
+
+ // Constant that denotes what media type a file should be stored as.
+ public static final int MEDIA_TYPE_IMAGE = 1;
+ public static final int MEDIA_TYPE_AUDIO = 2;
+ public static final int MEDIA_TYPE_TEXT = 3;
+
+
+ /**
+ * Creates an output file to store some kind of media (images, audio, text).
+ *
+ * The directory the file is created in depends both on the media type and the security level.
+ * If the security is private, we store it in app-specific private memory. If it is public, we
+ * store the file on external storage. Android has different directories in external storage for
+ * each media type, so we choose the directory depending on the media type parameter.
+ *
+ * If the provided filename is null, we generate a filename based on the current time and media type.
+ *
+ * @param context The context of the calling component
+ * @param type The media type that's being stored (determines file location and name
+ * if not specified)
+ * @param security How securely we should store the temporary files.
+ * We can store it on the SD card or in private app memory.
+ * @param name The name of the file to be created. If null, we generate a name based on
+ * the current time and media type.
+ * @return A File reference to a newly created temporary file
+ */
+ public static File getOutputMediaFile(Context context, int type, int security, String name) {
+ Log.d(LOG_TAG, "getOutputMediaFile() type:" + type);
+
+ // Get the current time stamp
+ String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US) // Line 56
+ .format(new Date());
+
+ // The directory where we'll store the file
+ File storageDir = null; // Line 59
+
+ // The name of the file we'll return
+ File outputFile = null;
+
+ // Make sure external storage is mounted.
+ if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ Toast.makeText(context, "External storage not mounted. Can't write/read file.", Toast.LENGTH_LONG).show();
+ return null;
+ }
+
+ // If security is private, store it in the app's private directory.
+ if (security == SECURITY_PRIVATE) {
+ storageDir = context.getFilesDir();
+ }
+ // Otherwise, store the file in a public directory depending on its media type.
+ else { // Line 76
+ switch (type) {
+ case MEDIA_TYPE_IMAGE:
+ storageDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
+ break;
+ case MEDIA_TYPE_AUDIO:
+ storageDir = context.getExternalFilesDir(Environment.DIRECTORY_MUSIC);
+ break;
+ case MEDIA_TYPE_TEXT:
+ storageDir = context.getExternalFilesDir(null);
+ break;
+ }
+ }
+
+ // If a name was specified, use that filename.
+ if (name != null && storageDir != null) {
+ outputFile = new File(storageDir.getPath() + File.separator + name); // Line 92
+ }
+ // Otherwise, determine filename based on media type.
+ else if (storageDir != null){
+ switch (type) {
+ case MEDIA_TYPE_IMAGE:
+ outputFile = new File(storageDir.getPath() + File.separator
+ + "IMG_" + timeStamp);
+ break;
+ case MEDIA_TYPE_AUDIO:
+ outputFile = new File(storageDir.getPath() + File.separator +
+ "SND_" + timeStamp);
+ break;
+ case MEDIA_TYPE_TEXT:
+ outputFile = new File(storageDir.getPath() + File.separator
+ + "TXT_" + timeStamp);
+ break;
+ }
+ }
+
+ return outputFile;
+ }
+
+ /**
+ * A convenience function for getting a URI to an output file instead of a File reference.
+ *
+ * @param context The context of the calling component
+ * @param type The media type that's being stored (determines file name and location)
+ * @param security How securely we should store the temporary files.
+ * We can store it on the SD card or in private app memory.
+ * @param name The name of the file to be created (optional)
+ * @return A Uri to a newly created temporary file
+ */
+ public static Uri getOutputMediaFileUri(Context context, int type, int security, String name){
+ File outFile = getOutputMediaFile(context, type, security, name);
+ if (outFile != null)
+ return Uri.fromFile(outFile); // Line 128
+
+ return null;
+ }
+
+
+}
diff --git a/assignments/week-7-assignment-6/iRemember/src/edu/vuum/mocca/storage/StoryCreator.java b/assignments/week-7-assignment-6/iRemember/src/edu/vuum/mocca/storage/StoryCreator.java
new file mode 100644
index 000000000..ffe6b387a
--- /dev/null
+++ b/assignments/week-7-assignment-6/iRemember/src/edu/vuum/mocca/storage/StoryCreator.java
@@ -0,0 +1,197 @@
+/*
+
+The iRemember source code (henceforth referred to as "iRemember") is
+copyrighted by Mike Walker, Adam Porter, Doug Schmidt, and Jules White
+at Vanderbilt University and the University of Maryland, Copyright (c)
+2014, all rights reserved. Since iRemember is open-source, freely
+available software, you are free to use, modify, copy, and
+distribute--perpetually and irrevocably--the source code and object code
+produced from the source, as well as copy and distribute modified
+versions of this software. You must, however, include this copyright
+statement along with any code built using iRemember that you release. No
+copyright statement needs to be provided if you just ship binary
+executables of your software products.
+
+You can use iRemember software in commercial and/or binary software
+releases and are under no obligation to redistribute any of your source
+code that is built using the software. Note, however, that you may not
+misappropriate the iRemember code, such as copyrighting it yourself or
+claiming authorship of the iRemember software code, in a way that will
+prevent the software from being distributed freely using an open-source
+development model. You needn't inform anyone that you're using iRemember
+software in your software, though we encourage you to let us know so we
+can promote your project in our success stories.
+
+iRemember is provided as is with no warranties of any kind, including
+the warranties of design, merchantability, and fitness for a particular
+purpose, noninfringement, or arising from a course of dealing, usage or
+trade practice. Vanderbilt University and University of Maryland, their
+employees, and students shall have no liability with respect to the
+infringement of copyrights, trade secrets or any patents by DOC software
+or any part thereof. Moreover, in no event will Vanderbilt University,
+University of Maryland, their employees, or students be liable for any
+lost revenue or profits or other special, indirect and consequential
+damages.
+
+iRemember is provided with no support and without any obligation on the
+part of Vanderbilt University and University of Maryland, their
+employees, or students to assist in its use, correction, modification,
+or enhancement.
+
+The names Vanderbilt University and University of Maryland may not be
+used to endorse or promote products or services derived from this source
+without express written permission from Vanderbilt University or
+University of Maryland. This license grants no permission to call
+products or services derived from the iRemember source, nor does it
+grant permission for the name Vanderbilt University or
+University of Maryland to appear in their names.
+ */
+
+package edu.vuum.mocca.storage;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Locale;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.text.format.DateFormat;
+import edu.vuum.mocca.provider.MoocSchema;
+
+/**
+ * StoryCreator is a helper class that does convenience functions for converting
+ * between the Custom ORM objects (such as StoryData), ContentValues, and Cursors.
+ *
+ * @author Michael A. Walker
+ *
+ */
+public class StoryCreator {
+
+ /**
+ * Create a ContentValues from a provided StoryData.
+ *
+ * @param data
+ * StoryData to be converted.
+ * @return ContentValues that is created from the StoryData object
+ */
+ public static ContentValues getCVfromStory(final StoryData data) {
+ ContentValues rValue = new ContentValues();
+ rValue.put(MoocSchema.Story.Cols.LOGIN_ID, data.loginId);
+ rValue.put(MoocSchema.Story.Cols.STORY_ID, data.storyId);
+ rValue.put(MoocSchema.Story.Cols.TITLE, data.title);
+ rValue.put(MoocSchema.Story.Cols.BODY, data.body);
+ rValue.put(MoocSchema.Story.Cols.AUDIO_LINK, data.audioLink);
+ rValue.put(MoocSchema.Story.Cols.VIDEO_LINK, data.videoLink);
+ rValue.put(MoocSchema.Story.Cols.IMAGE_NAME, data.imageName);
+ rValue.put(MoocSchema.Story.Cols.IMAGE_LINK, data.imageLink);
+ rValue.put(MoocSchema.Story.Cols.TAGS, data.tags);
+ rValue.put(MoocSchema.Story.Cols.CREATION_TIME, data.creationTime);
+ rValue.put(MoocSchema.Story.Cols.STORY_TIME, data.storyTime);
+ rValue.put(MoocSchema.Story.Cols.LATITUDE, data.latitude);
+ rValue.put(MoocSchema.Story.Cols.LONGITUDE, data.longitude);
+ return rValue;
+ }
+
+ /**
+ * Get all of the StoryData from the passed in cursor.
+ *
+ * @param cursor
+ * passed in cursor to get StoryData(s) of.
+ * @return ArrayList The set of StoryData
+ */
+ public static ArrayList getStoryDataArrayListFromCursor(
+ Cursor cursor) {
+ ArrayList rValue = new ArrayList();
+ if (cursor != null) {
+ if (cursor.moveToFirst()) {
+ do {
+ rValue.add(getStoryDataFromCursor(cursor));
+ } while (cursor.moveToNext() == true);
+ }
+ }
+ return rValue;
+ }
+
+ /**
+ * Get the first StoryData from the passed in cursor.
+ *
+ * @param cursor
+ * passed in cursor
+ * @return StoryData object
+ */
+ public static StoryData getStoryDataFromCursor(Cursor cursor) {
+
+ long rowID = cursor.getLong(cursor
+ .getColumnIndex(MoocSchema.Story.Cols.ID));
+ long loginId = cursor.getLong(cursor
+ .getColumnIndex(MoocSchema.Story.Cols.LOGIN_ID));
+ long storyId = cursor.getLong(cursor
+ .getColumnIndex(MoocSchema.Story.Cols.STORY_ID));
+ String title = cursor.getString(cursor
+ .getColumnIndex(MoocSchema.Story.Cols.TITLE));
+ String body = cursor.getString(cursor
+ .getColumnIndex(MoocSchema.Story.Cols.BODY));
+ String audioLink = cursor.getString(cursor
+ .getColumnIndex(MoocSchema.Story.Cols.AUDIO_LINK));
+ String videoLink = cursor.getString(cursor
+ .getColumnIndex(MoocSchema.Story.Cols.VIDEO_LINK));
+ String imageName = cursor.getString(cursor
+ .getColumnIndex(MoocSchema.Story.Cols.IMAGE_NAME));
+ String imageMetaData = cursor.getString(cursor
+ .getColumnIndex(MoocSchema.Story.Cols.IMAGE_LINK));
+ String tags = cursor.getString(cursor
+ .getColumnIndex(MoocSchema.Story.Cols.TAGS));
+ long creationTime = cursor.getLong(cursor
+ .getColumnIndex(MoocSchema.Story.Cols.CREATION_TIME));
+ long storyTime = cursor.getLong(cursor
+ .getColumnIndex(MoocSchema.Story.Cols.STORY_TIME));
+ double latitude = cursor.getDouble(cursor
+ .getColumnIndex(MoocSchema.Story.Cols.LATITUDE));
+ double longitude = cursor.getDouble(cursor
+ .getColumnIndex(MoocSchema.Story.Cols.LONGITUDE));
+
+ // construct the returned object
+ StoryData rValue = new StoryData(rowID, loginId, storyId, title, body,
+ audioLink, videoLink, imageName, imageMetaData, tags,
+ creationTime, storyTime, latitude, longitude);
+
+ return rValue;
+ }
+
+ /**
+ * A convenience function for converting a date expressed in minutes, days, and hours into
+ * a millisecond time stamp.
+ * @param year
+ * @param month
+ * @param day
+ * @param hour
+ * @param minute
+ * @return
+ */
+ public static long componentTimeToTimestamp(int year, int month, int day, int hour,
+ int minute) {
+
+ Calendar c = Calendar.getInstance();
+ c.set(Calendar.YEAR, year);
+ c.set(Calendar.MONTH, month);
+ c.set(Calendar.DAY_OF_MONTH, day);
+ c.set(Calendar.HOUR, hour);
+ c.set(Calendar.MINUTE, minute);
+ c.set(Calendar.SECOND, 0);
+ c.set(Calendar.MILLISECOND, 0);
+
+ return c.getTimeInMillis();
+ }
+
+ /**
+ * A convenience function for converting a millisecond time stamp into a String format
+ * @param timestamp
+ * @return
+ */
+ public static String getStringDate(long timestamp) {
+ Calendar cal = Calendar.getInstance(Locale.ENGLISH);
+ cal.setTimeInMillis(timestamp);
+ String date = DateFormat.format("dd-MM-yyyy", cal).toString();
+ return date;
+ }
+}
diff --git a/assignments/week-7-assignment-6/iRemember/src/edu/vuum/mocca/storage/StoryData.java b/assignments/week-7-assignment-6/iRemember/src/edu/vuum/mocca/storage/StoryData.java
new file mode 100644
index 000000000..73e419dc4
--- /dev/null
+++ b/assignments/week-7-assignment-6/iRemember/src/edu/vuum/mocca/storage/StoryData.java
@@ -0,0 +1,379 @@
+/*
+The iRemember source code (henceforth referred to as "iRemember") is
+copyrighted by Mike Walker, Adam Porter, Doug Schmidt, and Jules White
+at Vanderbilt University and the University of Maryland, Copyright (c)
+2014, all rights reserved. Since iRemember is open-source, freely
+available software, you are free to use, modify, copy, and
+distribute--perpetually and irrevocably--the source code and object code
+produced from the source, as well as copy and distribute modified
+versions of this software. You must, however, include this copyright
+statement along with any code built using iRemember that you release. No
+copyright statement needs to be provided if you just ship binary
+executables of your software products.
+
+You can use iRemember software in commercial and/or binary software
+releases and are under no obligation to redistribute any of your source
+code that is built using the software. Note, however, that you may not
+misappropriate the iRemember code, such as copyrighting it yourself or
+claiming authorship of the iRemember software code, in a way that will
+prevent the software from being distributed freely using an open-source
+development model. You needn't inform anyone that you're using iRemember
+software in your software, though we encourage you to let us know so we
+can promote your project in our success stories.
+
+iRemember is provided as is with no warranties of any kind, including
+the warranties of design, merchantability, and fitness for a particular
+purpose, noninfringement, or arising from a course of dealing, usage or
+trade practice. Vanderbilt University and University of Maryland, their
+employees, and students shall have no liability with respect to the
+infringement of copyrights, trade secrets or any patents by DOC software
+or any part thereof. Moreover, in no event will Vanderbilt University,
+University of Maryland, their employees, or students be liable for any
+lost revenue or profits or other special, indirect and consequential
+damages.
+
+iRemember is provided with no support and without any obligation on the
+part of Vanderbilt University and University of Maryland, their
+employees, or students to assist in its use, correction, modification,
+or enhancement.
+
+The names Vanderbilt University and University of Maryland may not be
+used to endorse or promote products or services derived from this source
+without express written permission from Vanderbilt University or
+University of Maryland. This license grants no permission to call
+products or services derived from the iRemember source, nor does it
+grant permission for the name Vanderbilt University or
+University of Maryland to appear in their names.
+ */
+
+package edu.vuum.mocca.storage;
+
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.message.BasicNameValuePair;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.content.ContentValues;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Custom ORM container class, for Story Data.
+ *
+ * This class is meant as a helper class for those working with the
+ * ContentProvider and SQLiteDatabase. The use of this class is completely optional.
+ *
diff --git a/ex/ThreadedDownload/project.properties b/ex/DownloadApplication/project.properties
similarity index 100%
rename from ex/ThreadedDownload/project.properties
rename to ex/DownloadApplication/project.properties
diff --git a/ex/ThreadedDownload/res/drawable-hdpi/default_image.png b/ex/DownloadApplication/res/drawable-hdpi/default_image.png
similarity index 100%
rename from ex/ThreadedDownload/res/drawable-hdpi/default_image.png
rename to ex/DownloadApplication/res/drawable-hdpi/default_image.png
diff --git a/ex/DownloadApplication/res/drawable-hdpi/ic_action_search.png b/ex/DownloadApplication/res/drawable-hdpi/ic_action_search.png
new file mode 100644
index 000000000..67de12dec
Binary files /dev/null and b/ex/DownloadApplication/res/drawable-hdpi/ic_action_search.png differ
diff --git a/ex/ThreadedDownload/res/drawable-hdpi/ic_launcher.png b/ex/DownloadApplication/res/drawable-hdpi/ic_launcher.png
similarity index 100%
rename from ex/ThreadedDownload/res/drawable-hdpi/ic_launcher.png
rename to ex/DownloadApplication/res/drawable-hdpi/ic_launcher.png
diff --git a/ex/ThreadedDownload/res/drawable-ldpi/ic_icon.png b/ex/DownloadApplication/res/drawable-ldpi/ic_icon.png
similarity index 100%
rename from ex/ThreadedDownload/res/drawable-ldpi/ic_icon.png
rename to ex/DownloadApplication/res/drawable-ldpi/ic_icon.png
diff --git a/ex/ThreadedDownload/res/drawable-ldpi/ic_launcher.png b/ex/DownloadApplication/res/drawable-ldpi/ic_launcher.png
similarity index 100%
rename from ex/ThreadedDownload/res/drawable-ldpi/ic_launcher.png
rename to ex/DownloadApplication/res/drawable-ldpi/ic_launcher.png
diff --git a/ex/ThreadedDownload/res/drawable-mdpi/default_image.png b/ex/DownloadApplication/res/drawable-mdpi/default_image.png
similarity index 100%
rename from ex/ThreadedDownload/res/drawable-mdpi/default_image.png
rename to ex/DownloadApplication/res/drawable-mdpi/default_image.png
diff --git a/ex/ThreadedDownload/res/drawable-mdpi/ic_action_search.png b/ex/DownloadApplication/res/drawable-mdpi/ic_action_search.png
similarity index 100%
rename from ex/ThreadedDownload/res/drawable-mdpi/ic_action_search.png
rename to ex/DownloadApplication/res/drawable-mdpi/ic_action_search.png
diff --git a/ex/ThreadedDownload/res/drawable-mdpi/ic_launcher.png b/ex/DownloadApplication/res/drawable-mdpi/ic_launcher.png
similarity index 100%
rename from ex/ThreadedDownload/res/drawable-mdpi/ic_launcher.png
rename to ex/DownloadApplication/res/drawable-mdpi/ic_launcher.png
diff --git a/ex/ThreadedDownload/res/drawable-xhdpi/default_image.png b/ex/DownloadApplication/res/drawable-xhdpi/default_image.png
similarity index 100%
rename from ex/ThreadedDownload/res/drawable-xhdpi/default_image.png
rename to ex/DownloadApplication/res/drawable-xhdpi/default_image.png
diff --git a/ex/ThreadedDownload/res/drawable-xhdpi/ic_action_search.png b/ex/DownloadApplication/res/drawable-xhdpi/ic_action_search.png
similarity index 100%
rename from ex/ThreadedDownload/res/drawable-xhdpi/ic_action_search.png
rename to ex/DownloadApplication/res/drawable-xhdpi/ic_action_search.png
diff --git a/ex/ThreadedDownload/res/drawable-xhdpi/ic_launcher.png b/ex/DownloadApplication/res/drawable-xhdpi/ic_launcher.png
similarity index 100%
rename from ex/ThreadedDownload/res/drawable-xhdpi/ic_launcher.png
rename to ex/DownloadApplication/res/drawable-xhdpi/ic_launcher.png
diff --git a/ex/ThreadedDownload/res/layout/main.xml b/ex/DownloadApplication/res/layout/main.xml
similarity index 66%
rename from ex/ThreadedDownload/res/layout/main.xml
rename to ex/DownloadApplication/res/layout/main.xml
index 7d10d2724..138295dec 100644
--- a/ex/ThreadedDownload/res/layout/main.xml
+++ b/ex/DownloadApplication/res/layout/main.xml
@@ -18,9 +18,8 @@
@@ -28,33 +27,22 @@
+ android:orientation="horizontal"
+ android:gravity="center_horizontal">
+ android:layout_weight="1"
+ android:onClick="downloadImage"
+ android:text="@string/download" />
-
-
-
-
diff --git a/ex/ThreadedDownload/res/menu/options.xml b/ex/DownloadApplication/res/menu/options.xml
similarity index 100%
rename from ex/ThreadedDownload/res/menu/options.xml
rename to ex/DownloadApplication/res/menu/options.xml
diff --git a/ex/ThreadedDownload/res/values-v11/styles.xml b/ex/DownloadApplication/res/values-v11/styles.xml
similarity index 100%
rename from ex/ThreadedDownload/res/values-v11/styles.xml
rename to ex/DownloadApplication/res/values-v11/styles.xml
diff --git a/ex/ThreadedDownload/res/values-v14/styles.xml b/ex/DownloadApplication/res/values-v14/styles.xml
similarity index 100%
rename from ex/ThreadedDownload/res/values-v14/styles.xml
rename to ex/DownloadApplication/res/values-v14/styles.xml
diff --git a/ex/DownloadApplication/res/values/strings.xml b/ex/DownloadApplication/res/values/strings.xml
new file mode 100644
index 000000000..933bf9bca
--- /dev/null
+++ b/ex/DownloadApplication/res/values/strings.xml
@@ -0,0 +1,11 @@
+
+
+ DownloadApplication
+ Download
+ Download Image
+ Reset Image
+ http://www.dre.vanderbilt.edu/~schmidt/ka.png
+ Enter URL:
+ Help
+ About
+
diff --git a/ex/ThreadedDownload/res/values/styles.xml b/ex/DownloadApplication/res/values/styles.xml
similarity index 100%
rename from ex/ThreadedDownload/res/values/styles.xml
rename to ex/DownloadApplication/res/values/styles.xml
diff --git a/ex/DownloadApplication/src/edu/vuum/mocca/DownloadActivity.java b/ex/DownloadApplication/src/edu/vuum/mocca/DownloadActivity.java
new file mode 100644
index 000000000..eb68cb632
--- /dev/null
+++ b/ex/DownloadApplication/src/edu/vuum/mocca/DownloadActivity.java
@@ -0,0 +1,255 @@
+package edu.vuum.mocca;
+
+import java.lang.ref.WeakReference;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.Menu;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.Toast;
+
+/**
+ * @class DownloadActivity
+ *
+ * @brief A class that enables a user to download a bitmap image using
+ * the DownloadService.
+ */
+public class DownloadActivity extends Activity {
+ /**
+ * User's selection of URL to download
+ */
+ private EditText mUrlEditText;
+
+ /**
+ * Image that's been downloaded
+ */
+ private ImageView mImageView;
+
+ /**
+ * Default URL.
+ */
+ private String mDefaultUrl =
+ "http://www.dre.vanderbilt.edu/~schmidt/ka.png";
+
+ /**
+ * Display progress of download
+ */
+ private ProgressDialog mProgressDialog;
+
+ /**
+ * Stores an instance of DownloadHandler that inherits from
+ * Handler and uses its handleMessage() hook method to process
+ * Messages sent to it from the DownloadService.
+ */
+ Handler mDownloadHandler = null;
+
+ /**
+ * Method that initializes the Activity when it is first created.
+ *
+ * @param savedInstanceState
+ * Activity's previously frozen state, if there was one.
+ */
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Sets the content view specified in the main.xml file.
+ setContentView(R.layout.main);
+
+ // Caches references to the EditText and ImageView objects in
+ // data members to optimize subsequent access.
+ mUrlEditText = (EditText) findViewById(R.id.mUrlEditText);
+ mImageView = (ImageView) findViewById(R.id.mImageView);
+
+ // Initialize the downloadHandler.
+ mDownloadHandler = new DownloadHandler(this);
+ }
+
+ /**
+ * Show a toast, notifying a user of an error when retrieving a
+ * bitmap.
+ */
+ void showErrorToast(String errorString) {
+ Toast.makeText(this,
+ errorString,
+ Toast.LENGTH_LONG).show();
+ }
+
+ /**
+ * Display a downloaded bitmap image if it's non-null; otherwise,
+ * it reports an error via a Toast.
+ *
+ * @param image
+ * The bitmap image
+ */
+ void displayImage(Bitmap image)
+ {
+ if (mImageView == null)
+ showErrorToast("Problem with Application,"
+ + " please contact the Developer.");
+ else if (image != null)
+ mImageView.setImageBitmap(image);
+ else
+ showErrorToast("image is corrupted,"
+ + " please check the requested URL.");
+ }
+
+ /**
+ * Called when a user clicks a button to reset an image to
+ * default.
+ *
+ * @param view
+ * The "Reset Image" button
+ */
+ public void resetImage(View view) {
+ mImageView.setImageResource(R.drawable.default_image);
+ }
+
+ /**
+ * Called when a user clicks the Download Image button to download
+ * an image using the DownloadService
+ *
+ * @param view
+ * The "Download Image" button
+ */
+ public void downloadImage(View view) {
+ // Obtain the requested URL from the user input.
+ String url = getUrlString();
+
+ Log.e(DownloadActivity.class.getSimpleName(),
+ "Downloading " + url);
+
+ hideKeyboard();
+
+ // Inform the user that the download is starting.
+ showDialog("downloading via startService()");
+
+ // Create an Intent to download an image in the background via
+ // a Service. The downloaded image is later diplayed in the
+ // UI Thread via the downloadHandler() method defined below.
+ Intent intent =
+ DownloadService.makeIntent(this,
+ Uri.parse(url),
+ mDownloadHandler);
+
+ // Start the DownloadService.
+ startService(intent);
+ }
+
+ /**
+ * @class DownloadHandler
+ *
+ * @brief A nested class that inherits from Handler and uses its
+ * handleMessage() hook method to process Messages sent to
+ * it from the DownloadService.
+ */
+ private static class DownloadHandler extends Handler {
+ /**
+ * Allows Activity to be garbage collected properly.
+ */
+ private WeakReference mActivity;
+
+ /**
+ * Class constructor constructs mActivity as weak reference
+ * to the activity
+ *
+ * @param activity
+ * The corresponding activity
+ */
+ public DownloadHandler(DownloadActivity activity) {
+ mActivity = new WeakReference(activity);
+ }
+
+ /**
+ * This hook method is dispatched in response to receiving the
+ * pathname back from the DownloadService.
+ */
+ public void handleMessage(Message message) {
+ DownloadActivity activity = mActivity.get();
+ // Bail out if the DownloadActivity is gone.
+ if (activity == null)
+ return;
+
+ // Try to extract the pathname from the message.
+ String pathname = DownloadService.getPathname(message);
+
+ // See if the download worked or not.
+ if (pathname == null)
+ activity.showDialog("failed download");
+
+ // Stop displaying the progress dialog.
+ activity.dismissDialog();
+
+ // Display the image in the UI Thread.
+ activity.displayImage(BitmapFactory.decodeFile(pathname));
+ }
+ };
+
+ /**
+ * Display the Dialog to the User.
+ *
+ * @param message
+ * The String to display what download method was used.
+ */
+ public void showDialog(String message) {
+ mProgressDialog =
+ ProgressDialog.show(this,
+ "Download",
+ message,
+ true);
+ }
+
+ /**
+ * Dismiss the Dialog
+ */
+ public void dismissDialog() {
+ if (mProgressDialog != null)
+ mProgressDialog.dismiss();
+ }
+
+ /**
+ * Hide the keyboard after a user has finished typing the url.
+ */
+ private void hideKeyboard() {
+ InputMethodManager mgr =
+ (InputMethodManager) getSystemService
+ (Context.INPUT_METHOD_SERVICE);
+ mgr.hideSoftInputFromWindow(mUrlEditText.getWindowToken(),
+ 0);
+ }
+
+ /**
+ * Show a collection of menu items for the ThreadedDownloads
+ * activity.
+ *
+ * @return true
+ */
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.options, menu);
+ return true;
+ }
+
+ /**
+ * Read the URL EditText and return the String it contains.
+ *
+ * @return String value in mUrlEditText
+ */
+ String getUrlString() {
+ String s = mUrlEditText.getText().toString();
+ if (s.equals(""))
+ s = mDefaultUrl;
+ return s;
+ }
+
+}
diff --git a/ex/DownloadApplication/src/edu/vuum/mocca/DownloadService.java b/ex/DownloadApplication/src/edu/vuum/mocca/DownloadService.java
new file mode 100644
index 000000000..93d526560
--- /dev/null
+++ b/ex/DownloadApplication/src/edu/vuum/mocca/DownloadService.java
@@ -0,0 +1,337 @@
+package edu.vuum.mocca;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+
+import android.app.Activity;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Base64;
+import android.util.Log;
+
+/**
+ * @class DownloadService
+ *
+ * @brief Downloads & stores a bitmap image on behalf of the
+ * DownloadActivity. DownloadService receives an Intent
+ * containing a URL (which is a type of URI) and a
+ * Messenger. It downloads the file at the URL, stores it on
+ * the file system, then returns the path name to the caller
+ * using the supplied Messenger.
+ *
+ * The DownloadService class implements the CommandProcessor
+ * pattern and The Messenger is used as part of the Active
+ * Object pattern.
+ */
+public class DownloadService extends Service
+{
+ /**
+ * String constant used to extract the Messenger "extra" from an
+ * intent.
+ */
+ private static final String MESSENGER = "MESSENGER";
+
+ /**
+ * String constant used to extract the pathname "extra" from an
+ * intent.
+ */
+ private static final String PATHNAME = "PATHNAME";
+
+ /**
+ * Looper associated with the HandlerThread.
+ */
+ private volatile Looper mServiceLooper;
+
+ /**
+ * Processes Messages sent to it from onStartCommnand() that
+ * indicate which images to download from a remote server.
+ */
+ private volatile ServiceHandler mServiceHandler;
+
+ /**
+ * Hook method called when DownloadService is first launched by
+ * the Android ActivityManager.
+ */
+ public void onCreate() {
+ super.onCreate();
+
+ // Create and start a background HandlerThread since by
+ // default a Service runs in the UI Thread, which we don't
+ // want to block.
+ HandlerThread thread =
+ new HandlerThread("DownloadService");
+ thread.start();
+
+ // Get the HandlerThread's Looper and use it for our Handler.
+ mServiceLooper = thread.getLooper();
+ mServiceHandler =
+ new ServiceHandler(mServiceLooper);
+ }
+
+ /**
+ * Hook method called each time a Started Service is sent an
+ * Intent via startService().
+ */
+ public int onStartCommand(Intent intent,
+ int flags,
+ int startId) {
+ // Create a Message that will be sent to ServiceHandler to
+ // retrieve animagebased on the URI in the Intent.
+ Message message =
+ mServiceHandler.makeDownloadMessage(intent,
+ startId);
+
+ // Send the Message to ServiceHandler to retrieve an image
+ // based on contents of the Intent.
+ mServiceHandler.sendMessage(message);
+
+ // Don't restart the DownloadService automatically if its
+ // process is killed while it's running.
+ return Service.START_NOT_STICKY;
+ }
+
+ /**
+ * Helper method that returns pathname if download succeeded.
+ */
+ public static String getPathname(Message message) {
+ // Extract the data from Message, which is in the form
+ // of a Bundle that can be passed across processes.
+ Bundle data = message.getData();
+
+ // Extract the pathname from the Bundle.
+ String pathname = data.getString(PATHNAME);
+
+ // Check to see if the download succeeded.
+ if (message.arg1 != Activity.RESULT_OK
+ || pathname == null)
+ return null;
+ else
+ return pathname;
+ }
+
+ /**
+ * Factory method to make the desired Intent.
+ */
+ public static Intent makeIntent(Context context,
+ Uri url,
+ Handler downloadHandler) {
+ // Create the Intent that's associated to the DownloadService
+ // class.
+ Intent intent = new Intent(context,
+ DownloadService.class);
+
+ // Set the URI as data in the Intent.
+ intent.setData(url);
+
+ // Create and pass a Messenger as an "extra" so the
+ // DownloadService can send back the pathname.
+ intent.putExtra(MESSENGER,
+ new Messenger(downloadHandler));
+ return intent;
+ }
+
+ /**
+ * @class ServiceHandler
+ *
+ * @brief An inner class that inherits from Handler and uses its
+ * handleMessage() hook method to process Messages sent to
+ * it from onStartCommnand() that indicate which images to
+ * download.
+ */
+ private final class ServiceHandler extends Handler {
+ /**
+ * Class constructor initializes the Looper.
+ *
+ * @param Looper
+ * The Looper that we borrow from HandlerThread.
+ */
+ public ServiceHandler(Looper looper) {
+ super(looper);
+ }
+
+ /**
+ * A factory method that creates a Message that contains
+ * information on the image to download and how to stop the
+ * Service.
+ */
+ private Message makeDownloadMessage(Intent intent,
+ int startId){
+ Message message = Message.obtain();
+ // Include Intent & startId in Message to indicate which URI
+ // to retrieve and which request is being stopped when
+ // download completes.
+ message.obj = intent;
+ message.arg1 = startId;
+ return message;
+ }
+
+ /**
+ * Retrieves the designated image and reply to the
+ * DownloadActivity via the Messenger sent with the Intent.
+ */
+ private void downloadImageAndReply(Intent intent) {
+ // Download the requested image.
+ String pathname = downloadImage(DownloadService.this,
+ intent.getData().toString());
+
+ // Extract the Messenger.
+ Messenger messenger = (Messenger)
+ intent.getExtras().get(MESSENGER);
+
+ // Send the pathname via the messenger.
+ sendPath(messenger, pathname);
+ }
+
+ /**
+ * A factory method that creates a Message to return to the
+ * DownloadActivity with the pathname of the downloaded image.
+ */
+ private Message makeReplyMessage(String pathname){
+ Message message = Message.obtain();
+ // Return the result to indicate whether the download
+ // succeeded or failed.
+ message.arg1 = pathname == null
+ ? Activity.RESULT_CANCELED
+ : Activity.RESULT_OK;
+
+ Bundle data = new Bundle();
+
+ // Pathname for the downloaded image.
+ data.putString(PATHNAME,
+ pathname);
+ message.setData(data);
+ return message;
+ }
+
+ /**
+ * Send the pathname back to the DownloadActivity via the
+ * messenger.
+ */
+ private void sendPath(Messenger messenger,
+ String pathname) {
+ // Call factory method to create Message.
+ Message message = makeReplyMessage(pathname);
+
+ try {
+ // Send pathname to back to the DownloadActivity.
+ messenger.send(message);
+ } catch (RemoteException e) {
+ Log.e(getClass().getName(),
+ "Exception while sending.",
+ e);
+ }
+ }
+
+ /**
+ * Create a file to store the result of a download.
+ *
+ * @param context
+ * @param url
+ * @return
+ * @throws IOException
+ */
+ private File getTemporaryFile(final Context context,
+ final String url) throws IOException {
+ return context.getFileStreamPath(Base64.encodeToString(url.getBytes(),
+ Base64.NO_WRAP)
+ + System.currentTimeMillis());
+ }
+
+ /**
+ * Copy the contents of an InputStream into an OutputStream.
+ *
+ * @param in
+ * @param out
+ * @return
+ * @throws IOException
+ */
+ private int copy(final InputStream in,
+ final OutputStream out) throws IOException {
+ final int BUFFER_LENGTH = 1024;
+ final byte[] buffer = new byte[BUFFER_LENGTH];
+ int totalRead = 0;
+ int read = 0;
+
+ while ((read = in.read(buffer)) != -1) {
+ out.write(buffer, 0, read);
+ totalRead += read;
+ }
+
+ return totalRead;
+ }
+
+ /**
+ * Download the requested image and return the local file path.
+ *
+ * @param context
+ * @param url
+ * @return
+ */
+ public String downloadImage(final Context context,
+ final String url) {
+ try {
+ final File file = getTemporaryFile(context, url);
+ Log.d(getClass().getName(), " downloading to " + file);
+
+ final InputStream in = (InputStream)
+ new URL(url).getContent();
+ final OutputStream out =
+ new FileOutputStream(file);
+
+ copy(in, out);
+ in.close();
+ out.close();
+ return file.getAbsolutePath();
+ } catch (Exception e) {
+ Log.e(getClass().getName(),
+ "Exception while downloading. Returning null.");
+ Log.e(getClass().getName(),
+ e.toString());
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ /**
+ * Hook method that retrieves an image from a remote server.
+ */
+ public void handleMessage(Message message) {
+ // Download the designated image and reply to the
+ // DownloadActivity via the Messenger sent with the
+ // Intent.
+ downloadImageAndReply((Intent) message.obj);
+
+ // Stop the Service using the startId, so it doesn't stop
+ // in the middle of handling another download request.
+ stopSelf(message.arg1);
+ }
+ }
+
+ /**
+ * Hook method called back to shutdown the Looper.
+ */
+ public void onDestroy() {
+ mServiceLooper.quit();
+ }
+
+ /**
+ * This hook method is a no-op since we're a Start Service.
+ */
+ public IBinder onBind(Intent arg0) {
+ return null;
+ }
+}
diff --git a/ex/GeonamesApplication/.classpath b/ex/GeonamesApplication/.classpath
new file mode 100644
index 000000000..c06dfcb8e
--- /dev/null
+++ b/ex/GeonamesApplication/.classpath
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/ex/GeonamesApplication/.project b/ex/GeonamesApplication/.project
new file mode 100644
index 000000000..5a138f014
--- /dev/null
+++ b/ex/GeonamesApplication/.project
@@ -0,0 +1,33 @@
+
+
+ GeoNamesApplication
+
+
+
+
+
+ com.android.ide.eclipse.adt.ResourceManagerBuilder
+
+
+
+
+ com.android.ide.eclipse.adt.PreCompilerBuilder
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ com.android.ide.eclipse.adt.ApkBuilder
+
+
+
+
+
+ com.android.ide.eclipse.adt.AndroidNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/ex/GeonamesApplication/AndroidManifest.xml b/ex/GeonamesApplication/AndroidManifest.xml
new file mode 100644
index 000000000..3f93dadcc
--- /dev/null
+++ b/ex/GeonamesApplication/AndroidManifest.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ex/GeonamesApplication/project.properties b/ex/GeonamesApplication/project.properties
new file mode 100644
index 000000000..a3ee5ab64
--- /dev/null
+++ b/ex/GeonamesApplication/project.properties
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-17
diff --git a/ex/GeonamesApplication/res/drawable-hdpi/ic_launcher.png b/ex/GeonamesApplication/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 000000000..96a442e5b
Binary files /dev/null and b/ex/GeonamesApplication/res/drawable-hdpi/ic_launcher.png differ
diff --git a/ex/GeonamesApplication/res/drawable-mdpi/ic_launcher.png b/ex/GeonamesApplication/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 000000000..359047dfa
Binary files /dev/null and b/ex/GeonamesApplication/res/drawable-mdpi/ic_launcher.png differ
diff --git a/ex/GeonamesApplication/res/drawable-xhdpi/ic_launcher.png b/ex/GeonamesApplication/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..71c6d760f
Binary files /dev/null and b/ex/GeonamesApplication/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/ex/GeonamesApplication/res/layout/geo_names_activity.xml b/ex/GeonamesApplication/res/layout/geo_names_activity.xml
new file mode 100644
index 000000000..1f32e5b71
--- /dev/null
+++ b/ex/GeonamesApplication/res/layout/geo_names_activity.xml
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ex/GeonamesApplication/res/values-v11/styles.xml b/ex/GeonamesApplication/res/values-v11/styles.xml
new file mode 100644
index 000000000..3c02242ad
--- /dev/null
+++ b/ex/GeonamesApplication/res/values-v11/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/ex/GeonamesApplication/res/values-v14/styles.xml b/ex/GeonamesApplication/res/values-v14/styles.xml
new file mode 100644
index 000000000..a91fd0372
--- /dev/null
+++ b/ex/GeonamesApplication/res/values-v14/styles.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
diff --git a/ex/GeonamesApplication/res/values/strings.xml b/ex/GeonamesApplication/res/values/strings.xml
new file mode 100644
index 000000000..94604b54a
--- /dev/null
+++ b/ex/GeonamesApplication/res/values/strings.xml
@@ -0,0 +1,9 @@
+
+
+ GeoNamesApplication
+ Enter URL:
+ "http://api.geonames.org/earthquakesJSON?north=44.1%26south=-9.9%26east=-22.4%26west=55.2%26username=demo"
+ Call GeoNames Service Sync
+ Call GeoNames Service Async
+ Reset Output
+
diff --git a/ex/GeonamesApplication/res/values/styles.xml b/ex/GeonamesApplication/res/values/styles.xml
new file mode 100644
index 000000000..6ce89c7ba
--- /dev/null
+++ b/ex/GeonamesApplication/res/values/styles.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
diff --git a/ex/GeonamesApplication/src/edu/vuum/mocca/GeoNamesActivity.java b/ex/GeonamesApplication/src/edu/vuum/mocca/GeoNamesActivity.java
new file mode 100644
index 000000000..0a3799b67
--- /dev/null
+++ b/ex/GeonamesApplication/src/edu/vuum/mocca/GeoNamesActivity.java
@@ -0,0 +1,243 @@
+package edu.vuum.mocca;
+
+import android.content.ComponentName;
+import android.content.ServiceConnection;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.View;
+import android.widget.Toast;
+
+/**
+ * This is the main activity that the program uses to start the
+ * GeoNamesApplication, which provides access to the GeoNames
+ * geographical database via its RESTful Web services API, as
+ * described at http://en.wikipedia.org/wiki/GeoNames. It allows the
+ * user to input a URL containing a request for one of its various
+ * services, such as direct and reverse geocoding, finding places
+ * through postal codes, finding places next to a given place, and
+ * finding Wikipedia articles about neighbouring places.
+ *
+ * The GeoNamesActivity uses Bound Services to perform the actual
+ * interactions with the GeoNames Web services in background Threads
+ * running in a separate process. There are two types of Bound
+ * Services shown in this example: synchronous and asynchronous. The
+ * Activity starts the desired Service using bindService(). After the
+ * Service is started, its onBind() hook method returns an
+ * implementation of an AIDL interface to the Activity by
+ * asynchronously calling the onServiceConnected() hook method in the
+ * Activity. The AIDL interface object that's returned can then be
+ * used to interact with the Service either synchronously or
+ * asynchronously, depending on the type of AIDL interface requested.
+ *
+ * Starting Bound Services to run synchronously in background Threads
+ * from the asynchronous UI Thread is an example of the
+ * Half-Sync/Half-Async Pattern. Starting Bound Services using
+ * Intents is an example of the Activator and Command Processor
+ * patterns. The GeoNamesActivity plays the role of the Creator and
+ * creates a Command in the form of an Intent. The Intent is received
+ * by the service process, which plays the role of the Executor.
+ *
+ * The use of AIDL interfaces to pass information between two
+ * different processes is an example of the Broker Pattern, in which
+ * all communication-related functionality is encapsulated in the AIDL
+ * interface and the underlying Android Binder framework, shielding
+ * applications from tedious and error-prone aspects of inter-process
+ * communication.
+ */
+public class GeoNamesActivity extends GeoNamesBase {
+ /**
+ * Used for debugging.
+ */
+ private final String TAG = this.getClass().getSimpleName();
+
+ /**
+ * The AIDL Interface that's used to make twoway calls to the
+ * GeoNamesServiceSync Service. This object plays the role of
+ * Requestor in the Broker Pattern. If it's null then there's no
+ * connection to the Service.
+ */
+ GeoNamesCall mGeoNamesCall = null;
+
+ /**
+ * The AIDL Interface that we will use to make oneway calls to the
+ * GeoNamesServiceAsync Service. This plays the role of Requestor
+ * in the Broker Pattern. If it's null then there's no connection
+ * to the Service.
+ */
+ GeoNamesRequest mGeoNamesRequest = null;
+
+ /**
+ * This ServiceConnection is used to receive the GeoNamesCall
+ * proxy after binding to the GeoNamesServiceSync Service using
+ * bindService().
+ */
+ ServiceConnection mServiceConnectionSync = new ServiceConnection() {
+ /**
+ * Cast the returned IBinder object to the GeoNamesCall
+ * AIDL Interface and store it for later use in
+ * mGeoNamesCall.
+ */
+ @Override
+ public void onServiceConnected(ComponentName name,
+ IBinder service) {
+ Log.d(TAG, "ComponentName: " + name);
+ mGeoNamesCall = GeoNamesCall.Stub.asInterface(service);
+ }
+
+ /**
+ * Called if the remote service crashes and is no longer
+ * available. The ServiceConnection will remain bound,
+ * but the service will not respond to any requests.
+ */
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mGeoNamesCall = null;
+ }
+
+ };
+
+ /**
+ * This ServiceConnection is used to receive the GeoNamesRequest
+ * proxy after binding to the GeoNamesServiceAsync Service using
+ * bindService().
+ */
+ ServiceConnection mServiceConnectionAsync = new ServiceConnection() {
+ /**
+ * Called after the KeyGeneratorService is connected to
+ * convey the result returned from onBind().
+ */
+ @Override
+ public void onServiceConnected(ComponentName name,
+ IBinder service) {
+ Log.d(TAG, "ComponentName: " + name);
+ // Cast the returned IBinder object to the
+ // GeoNamesRequest AIDL Interface and store it for
+ // later use in mGeoNamesRequest.
+ mGeoNamesRequest = GeoNamesRequest.Stub.asInterface(service);
+ }
+
+ /**
+ * Called if the Service crashes and is no longer
+ * available. The ServiceConnection will remain bound,
+ * but the service will not respond to any requests.
+ */
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mGeoNamesRequest = null;
+ }
+
+ };
+
+ /**
+ * The implementation of the GeoNamesCallback AIDL
+ * Interface. Should be passed to the GeoNamesServiceAsync
+ * Service using the GeoNamesRequest.callWebService() method.
+ *
+ * This implementation of GeoNamesCallback.Stub plays the role of
+ * Invoker in the Broker Pattern.
+ */
+ GeoNamesCallback.Stub mGeoNamesCallback = new GeoNamesCallback.Stub() {
+ /**
+ * Called when the GeoNamesServiceAsync finishes obtaining
+ * the results from the GeoNames Web service. Use the
+ * provided String to display the results in a TextView.
+ */
+ @Override
+ public void sendResults(final String results) throws RemoteException {
+ Log.d(TAG, "Receiving oneway sendResults() callback");
+
+ // Since this method is not run in the Main UI Thread,
+ // but in a thread allocated from the Binder thread
+ // pool, we need to call displayResults() to post a
+ // Runnable on the UI Thread via runOnUiThread().
+ runOnUiThread(new Runnable () {
+ public void run () {
+ displayResults(results);
+ }
+ });
+ }
+ };
+
+ /**
+ * This method is called when a user presses a button (see
+ * res/layout/GeoNamesActivity.xml)
+ */
+ public void runService(View view) {
+ Uri uri = Uri.parse(getUrlString());
+
+ hideKeyboard();
+
+ switch (view.getId()) {
+ case R.id.bound_sync_button:
+ if (mGeoNamesCall != null)
+ try {
+ Log.d(TAG,
+ "Calling twoway GeoNamesServiceSync.getWebServiceResults()");
+
+ String results =
+ // Invoke the twoway call, which blocks until
+ // it gets a reply.
+ mGeoNamesCall.getWebServiceResults(uri);
+ displayResults(results);
+ } catch (RemoteException e1) {
+ e1.printStackTrace();
+ }
+
+ break;
+
+ case R.id.bound_async_button:
+ if (mGeoNamesRequest != null)
+ try {
+ Log.d(TAG, "Calling oneway GeoNamesServiceAsync.callWebService()");
+
+ // Invoke the oneway call, which doesn't block.
+ mGeoNamesRequest.callWebService(uri,
+ mGeoNamesCallback);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ break;
+ }
+ }
+
+ /**
+ * Hook method called when the GeoNamesActivity becomes visible to
+ * bind the Activity to the Services.
+ */
+ @Override
+ public void onStart () {
+ super.onStart();
+
+ // Launch the designated Bound Service if they aren't already
+ // running via a call to bindService() Bind this activity to
+ // the GeoNamesService* Services if they aren't already bound.
+
+ if (mGeoNamesCall == null)
+ bindService(GeoNamesServiceSync.makeIntent(this),
+ mServiceConnectionSync,
+ BIND_AUTO_CREATE);
+
+ if (mGeoNamesRequest == null)
+ bindService(GeoNamesServiceAsync.makeIntent(this),
+ mServiceConnectionAsync,
+ BIND_AUTO_CREATE);
+ }
+
+ /**
+ * Hook method called when the GeoNamesActivity becomes completely
+ * hidden to unbind the Activity from the Services.
+ */
+ @Override
+ public void onStop () {
+ super.onStop();
+
+ // Unbind the Sync/Async Services if they are connected.
+ if (mGeoNamesCall != null)
+ unbindService(mServiceConnectionSync);
+
+ if (mGeoNamesRequest != null)
+ unbindService(mServiceConnectionAsync);
+ }
+}
diff --git a/ex/GeonamesApplication/src/edu/vuum/mocca/GeoNamesBase.java b/ex/GeonamesApplication/src/edu/vuum/mocca/GeoNamesBase.java
new file mode 100644
index 000000000..2aeeb8a7b
--- /dev/null
+++ b/ex/GeonamesApplication/src/edu/vuum/mocca/GeoNamesBase.java
@@ -0,0 +1,120 @@
+package edu.vuum.mocca;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.StrictMode;
+import android.util.Log;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+import android.widget.TextView;
+
+/**
+ * This class is used the base class for the GeoNamesActivity. It
+ * instantiates the UI and handles displaying images and getting text
+ * from the EditText object by making displayBitmap() and
+ * getUrlString() available to subclasses. This design separates
+ * concerns by having GeoNamesBase handle UI functionality while
+ * subclasses (such as GeoNamesActivity) handle any Service-related
+ * communication with the GeoNames Web service.
+ *
+ * GeoNamesBase is an example of the Template Method pattern since it
+ * extends Activity and overrides its onCreate() hook method. More
+ * generally, any object that extends Activity and overrides its hook
+ * methods, such as onStart() or onPause(), is also an example of the
+ * Template Method pattern.
+ */
+public class GeoNamesBase extends Activity {
+ /**
+ * Used for debugging.
+ */
+ private final String TAG = this.getClass().getSimpleName();
+
+ /**
+ * This is the reference to the text box that allows the user to
+ * input a URL to a Web service request.
+ */
+ private EditText mUrlEditText;
+
+ /**
+ * This is a reference to the container of text in the UI. When
+ * we finish getting the results of the Web service call, we
+ * update this to display the results.
+ */
+ private TextView mTextView = null;
+
+ /**
+ * Display the given result string in the TextView.
+ */
+ protected void displayResults(String result) {
+ mTextView.setText(result);
+ }
+
+ /**
+ * Gets the URL from the EditText.
+ */
+ protected String getUrlString () {
+ return mUrlEditText.getText().toString();
+ }
+
+ /**
+ * Hide the keyboard after a user has finished typing the url.
+ */
+ protected void hideKeyboard() {
+ InputMethodManager mgr =
+ (InputMethodManager) getSystemService
+ (Context.INPUT_METHOD_SERVICE);
+ mgr.hideSoftInputFromWindow(mUrlEditText.getWindowToken(),
+ 0);
+ }
+
+ /**
+ * Resets the textView to be "".
+ */
+ public void resetOutput(View view) {
+ mTextView.setText("");
+ Log.d(TAG, "reset Image");
+ }
+
+ /**
+ * This is called when the Activity is initially created. This is
+ * where we setup the UI for the activity and initialize any
+ * objects that need to exist while the activity exists.
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ Log.d(TAG, "onCreate");
+ super.onCreate(savedInstanceState);
+
+ // Use the Android framework to create a User Interface for
+ // this activity. The interface that should be created is
+ // defined in GeoNamesActivity.xml in the res/layout folder.
+ setContentView(R.layout.geo_names_activity);
+
+ // Once the UI is created, get a reference to the instantiated
+ // EditText and ImageView objects by providing their ids to
+ // the Android framework.
+ mUrlEditText = (EditText) findViewById(R.id.url);
+ mTextView = (TextView) findViewById(R.id.textView1);
+
+ // Turn off strict mode.
+ //
+ // Normally, if you try to do any networking from the main UI
+ // thread, the Android framework will throw an exception and
+ // stop working. However, part of this application uses a
+ // synchronous AIDL interface to demonstrate how to execute
+ // functions in services synchronously. For this purpose, we
+ // turn off strict mode so that the Android framework will
+ // work without complaining.
+ //
+ // Please note that this is for demonstration purposes ONLY,
+ // and you should NEVER, EVER turn off strict mode in
+ // production code. You should also not do networking
+ // operations on the main thread, because it might cause your
+ // application to crash.
+ StrictMode.ThreadPolicy policy =
+ new StrictMode.ThreadPolicy.Builder().permitAll().build();
+ StrictMode.setThreadPolicy(policy);
+ }
+}
diff --git a/ex/GeonamesApplication/src/edu/vuum/mocca/GeoNamesCall.aidl b/ex/GeonamesApplication/src/edu/vuum/mocca/GeoNamesCall.aidl
new file mode 100644
index 000000000..56a326c28
--- /dev/null
+++ b/ex/GeonamesApplication/src/edu/vuum/mocca/GeoNamesCall.aidl
@@ -0,0 +1,19 @@
+package edu.vuum.mocca;
+
+/**
+ * @class GeoNamesCall
+ *
+ * @brief An AIDL interface used to get the results of a web service
+ * call in another process. Any invocations of
+ * getWebServiceResultsPathname() will block until the method
+ * completes and returns from the other process.
+ *
+ * This file generates a Java interface in the gen folder.
+ */
+interface GeoNamesCall {
+ /**
+ * Invoke a call to the GeoNames Web service at the provided
+ * Internet uri and returns results as a string.
+ */
+ String getWebServiceResults (in Uri uri);
+}
diff --git a/ex/GeonamesApplication/src/edu/vuum/mocca/GeoNamesCallback.aidl b/ex/GeonamesApplication/src/edu/vuum/mocca/GeoNamesCallback.aidl
new file mode 100644
index 000000000..7b7b2c6ac
--- /dev/null
+++ b/ex/GeonamesApplication/src/edu/vuum/mocca/GeoNamesCallback.aidl
@@ -0,0 +1,17 @@
+package edu.vuum.mocca;
+
+/**
+ * @class
+ *
+ * @brief An AIDL Interface used to receive results from a prior call
+ * to GeoNamesRequest.callWebService().
+ *
+ * This file generates a Java interface in the gen folder.
+ */
+interface GeoNamesCallback {
+ /**
+ * Send a string containing the results from the call to the Web
+ * service back to the client.
+ */
+ oneway void sendResults (in String results);
+}
diff --git a/ex/GeonamesApplication/src/edu/vuum/mocca/GeoNamesRequest.aidl b/ex/GeonamesApplication/src/edu/vuum/mocca/GeoNamesRequest.aidl
new file mode 100644
index 000000000..7d7bc99c3
--- /dev/null
+++ b/ex/GeonamesApplication/src/edu/vuum/mocca/GeoNamesRequest.aidl
@@ -0,0 +1,23 @@
+package edu.vuum.mocca;
+
+import edu.vuum.mocca.GeoNamesCallback;
+
+/**
+ * @class GeoNamesRequest
+ *
+ * @brief An AIDL interface used to get the results of a web service
+ * call in another process. The caller provides a
+ * GeoNamesCallback object so that the Service process can
+ * return a result across process boundaries asynchronously.
+ *
+ * This file generates a Java interface in the gen folder.
+ */
+interface GeoNamesRequest {
+ /**
+ * Invoke a call to the GeoNames webservice at the provided
+ * Internet uri. The Service uses the GeoNamesCallback parameter
+ * to return a string containing the results back to the Activity.
+ */
+ oneway void callWebService (in Uri uri,
+ in GeoNamesCallback callback);
+}
diff --git a/ex/GeonamesApplication/src/edu/vuum/mocca/GeoNamesServiceAsync.java b/ex/GeonamesApplication/src/edu/vuum/mocca/GeoNamesServiceAsync.java
new file mode 100644
index 000000000..b85608a3f
--- /dev/null
+++ b/ex/GeonamesApplication/src/edu/vuum/mocca/GeoNamesServiceAsync.java
@@ -0,0 +1,89 @@
+package edu.vuum.mocca;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * @class GeoNamesServiceAsync
+ *
+ * @brief This Service performs calls to the GeoNames Web service
+ * using asynchronous AIDL interactions. The component (which
+ * in this example application is GeoNamesActivity) that binds
+ * to this service should receive an IBinder Proxy that
+ * references an instance of GeoNamesRequest, which extends
+ * IBinder. The component can then interact with this Service
+ * by invoking oneway method calls via the GeoNamesRequest
+ * proxy. Specifically, the component can ask this Service to
+ * call the GeoNames Web service, passing in a GeoNamesCallback
+ * object. Once the Web service call is finished, this Service
+ * will send the results back to the calling component by
+ * invoking sendResults() on the GeoNamesCallback object.
+ *
+ * AIDL and the Binder framework are an example of the Broker
+ * Pattern, in which all interprocess communication details are
+ * hidden behind the AIDL interfaces.
+ */
+public class GeoNamesServiceAsync extends Service{
+ /**
+ * Used for debugging.
+ */
+ private final String TAG = this.getClass().getSimpleName();
+
+ /**
+ * The concrete implementation of the AIDL Interface
+ * GeoNamesRequest. We extend the Stub class, which implements
+ * the GeoNamesRequest interface, so that Android can properly
+ * handle calls across process boundaries.
+ *
+ * This implementation plays the role of Invoker in the Broker
+ * Pattern.
+ */
+ GeoNamesRequest.Stub mGeoNamesRequestImpl = new GeoNamesRequest.Stub() {
+ /**
+ * Invoke a call to the GeoNames webservice at the
+ * provided Internet uri. The Service uses the
+ * GeoNamesCallback parameter to return a string
+ * containing the results back to the Activity.
+ *
+ * Use the method defined in GeoNamesUtils for code
+ * brevity.
+ */
+ @Override
+ public void callWebService(Uri uri,
+ GeoNamesCallback callback)
+ throws RemoteException {
+ Log.d(TAG, "Calling getWebServiceResults()");
+ // Invoke a oneway method to return the results to the
+ // client, which doesn't block synchronously.
+ callback.sendResults(GeoNamesUtils.getWebServiceResults(uri));
+ }
+
+ };
+
+ /**
+ * Called when a component calls bindService() with the proper
+ * intent. Return the concrete implementation of GeoNamesRequest
+ * cast as an IBinder.
+ */
+ @Override
+ public IBinder onBind(Intent intent) {
+ Log.d(TAG, "calling onBind()");
+ return mGeoNamesRequestImpl;
+ }
+
+ /**
+ * Make an Intent that will start this service when passed to
+ * bindService().
+ *
+ * @param context The context of the calling component.
+ */
+ public static Intent makeIntent(Context context) {
+ return new Intent(context,
+ GeoNamesServiceAsync.class);
+ }
+}
diff --git a/ex/GeonamesApplication/src/edu/vuum/mocca/GeoNamesServiceSync.java b/ex/GeonamesApplication/src/edu/vuum/mocca/GeoNamesServiceSync.java
new file mode 100644
index 000000000..552232e57
--- /dev/null
+++ b/ex/GeonamesApplication/src/edu/vuum/mocca/GeoNamesServiceSync.java
@@ -0,0 +1,88 @@
+package edu.vuum.mocca;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * @class GeoNamesServiceSync
+ *
+ * @brief This class handles downloads using synchronous AIDL
+ * interactions. The component that binds to this Service
+ * (which in this example application is GeoNamesActivity)
+ * should receive an IBinder proxy that references an instance
+ * of GeoNamesCall, which extends IBinder. The component can
+ * then interact with this service by making twoway method
+ * calls on the GeoNamesCall proxy. Specifically, the component
+ * can ask this Service to call the GeoNames Web service, which
+ * will run synchronously until it finishes getting the results
+ * from the the web service and returning them as a String.
+ *
+ * AIDL and the Binder framework are an example of the Broker
+ * Pattern, in which all interprocess communication details are
+ * hidden behind the AIDL interfaces.
+ */
+public class GeoNamesServiceSync extends Service {
+ /**
+ * Used for debugging.
+ */
+ private final String TAG = this.getClass().getSimpleName();
+
+ /**
+ * The concrete implementation of the AIDL Interface GeoNamesCall.
+ * We extend the Stub class, which implements the GeoNamesCall
+ * interface, so that Android can properly handle calls across
+ * process boundaries.
+ *
+ * This implementation plays the role of Invoker in the Broker
+ * Pattern.
+ */
+ GeoNamesCall.Stub mGeoNamesCallImpl = new GeoNamesCall.Stub() {
+ /**
+ * Invoke a call to the GeoNames webservice at the
+ * provided Internet uri. The Service uses the
+ * GeoNamesCallback parameter to return a string
+ * containing the results back to the Activity.
+ *
+ * Use the method defined in GeoNamesUtils for code
+ * brevity.
+
+ * GeoNames the image at the given Uri and return a
+ * pathname to the file on the Android file system.
+ */
+ @Override
+ public String getWebServiceResults(Uri uri)
+ throws RemoteException {
+ Log.d(TAG, "Calling getWebServiceResults()");
+ // Return the results to the client, which blocks
+ // synchronously.
+ return GeoNamesUtils.getWebServiceResults(uri);
+ }
+ };
+
+ /**
+ * Called when a component calls bindService() with the proper
+ * intent. Return the concrete implementation of GeoNamesCall
+ * cast as an IBinder.
+ */
+ @Override
+ public IBinder onBind(Intent intent) {
+ Log.d(TAG, "calling onBind()");
+ return mGeoNamesCallImpl;
+ }
+
+ /**
+ * Make an Intent that will start this service when passed to
+ * bindService().
+ *
+ * @param context The context of the calling component.
+ */
+ public static Intent makeIntent(Context context) {
+ return new Intent(context,
+ GeoNamesServiceSync.class);
+ }
+}
diff --git a/ex/GeonamesApplication/src/edu/vuum/mocca/GeoNamesUtils.java b/ex/GeonamesApplication/src/edu/vuum/mocca/GeoNamesUtils.java
new file mode 100644
index 000000000..594d75ae9
--- /dev/null
+++ b/ex/GeonamesApplication/src/edu/vuum/mocca/GeoNamesUtils.java
@@ -0,0 +1,100 @@
+package edu.vuum.mocca;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import android.net.Uri;
+import android.util.Log;
+
+/**
+ * @class GeoNamesUtils
+ *
+ * @brief This class encapsulates a static helper method that both
+ * GeoNamesService* implementations can access without
+ * redefining them in each Service.
+ */
+public class GeoNamesUtils {
+ private final static String TAG = "GeoNamesUtils";
+
+ /**
+ * Invoke a call to the GeoNames webservice at the provided
+ * Internet url and return the results as a string.
+ *
+ * @param uri the web url
+ *
+ * @return the results obtained from the Web Service call.
+ */
+ public static String getWebServiceResults(Uri uri) {
+ String url = uri.toString();
+ String regex = "%26(?!\\d)";
+ // Replace any "%26" with the "&" so the GeoNames webservice
+ // will work properly.
+ url = url.replaceAll(regex, "&");
+
+ Log.d(TAG,
+ "calling getWebServiceResults() with URL "
+ + url);
+ try {
+ // Send a request to the GeoNames Web service.
+ HttpURLConnection conn =
+ (HttpURLConnection) new URL(url).openConnection();
+
+ // Return the results of the Web service as a String.
+ return copy(conn.getInputStream());
+ } catch (MalformedURLException e1) {
+ Log.d(TAG,
+ "MalformedURLException while downloading. Returning null.");
+ e1.printStackTrace();
+ } catch (Exception e) {
+ Log.d(TAG,
+ "Exception while downloading. Returning null.");
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * Copy the contents of an InputStream into a String.
+ *
+ * @param is InputStream
+ * @return string
+ */
+ private static String copy(final InputStream is) {
+ StringBuffer data = new StringBuffer();
+ BufferedReader br = new BufferedReader(new InputStreamReader(is));
+
+ try {
+ // Copy all the results from the GeoNames Web service into
+ // a String and return it to the caller.
+ for (String rawData;
+ (rawData = br.readLine()) != null;
+ )
+ data.append(rawData);
+ } catch (IOException e) {
+ Log.d(TAG,
+ "IOException while downloading. Returning null.");
+ e.printStackTrace();
+ return null;
+ } finally {
+ if (is != null)
+ try {
+ is.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ if (br != null)
+ try {
+ br.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ return data.toString();
+ }
+}
diff --git a/ex/PingPong/console/src/edu/vuum/mocca/build.sh b/ex/PingPong/console/src/edu/vuum/mocca/build.sh
deleted file mode 100644
index 87c6dc140..000000000
--- a/ex/PingPong/console/src/edu/vuum/mocca/build.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#/bin/sh -f
-javac ConsolePlatformStrategy.java Main.java Options.java PlayPingPong.java PlatformStrategy.java PlatformStrategyFactory.java
-
-
-
diff --git a/ex/PingPong/wrong/PingPongWrong.java b/ex/PingPong/wrong/PingPongWrong.java
deleted file mode 100644
index 49ffb78b4..000000000
--- a/ex/PingPong/wrong/PingPongWrong.java
+++ /dev/null
@@ -1,77 +0,0 @@
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.CountDownLatch;
-
-/**
- * @class PingPongWrong
- *
- * @brief This class implements a Java program that creates two
- * threads that attempt to alternately print "Ping" and "Pong",
- * respectively, on the display. This implementation behaves
- * incorrectly due to lack of synchronization between the
- * threads. It's also hard-coded to run as a Java console
- * application and thus can't work in Android without major
- * changes.
- */
-public class PingPongWrong
-{
- /**
- * Number of iterations to play ping/pong.
- */
- public static int mMaxIterations = 10;
-
- /**
- * @brief PlayPingPongThread
- *
- * @class This class implements the incorrect non-synchronized
- * version of the ping/pong application.
- */
- public static class PlayPingPongThread extends Thread
- {
- public PlayPingPongThread (String stringToPrint)
- {
- this.mStringToPrint = stringToPrint;
- }
-
- /**
- * Main event loop that runs in a separate thread of control.
- */
- public void run ()
- {
- for (int loopsDone = 1;
- loopsDone <= mMaxIterations;
- ++loopsDone)
- // Print out the iteration.
- System.out.println(mStringToPrint + "(" + loopsDone + ")");
-
- // Exit the thread when the loop is done.
- }
-
- // The string to print for each ping and pong operation.
- private String mStringToPrint;
- }
-
- public static void main(String[] args) {
- try {
- System.out.println("Ready...Set...Go!");
-
- // Create the ping and pong threads.
- PlayPingPongThread ping =
- new PlayPingPongThread("Ping!");
- PlayPingPongThread pong =
- new PlayPingPongThread("Pong!");
-
- // Start ping and pong threads, which calls their run()
- // methods.
- ping.start();
- pong.start();
-
- // Wait for both threads to exit before exiting main().
- ping.join();
- pong.join();
-
- System.out.println("Done!");
- }
- catch (java.lang.InterruptedException e)
- {}
- }
-}
diff --git a/ex/ThreadedDownload/res/values/strings.xml b/ex/ThreadedDownload/res/values/strings.xml
deleted file mode 100644
index 44b88bb9e..000000000
--- a/ex/ThreadedDownload/res/values/strings.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
- ThreadedDownload
- Settings
- ThreadedDownload
- Run\nRunnable
- Run\nMessages
- Run\nAsync
- Reset\nImage
- http://www.dre.vanderbilt.edu/~schmidt/ka.png
- Enter URL:
- Help
- About
-
-
diff --git a/ex/ThreadedDownload/src/edu/vuum/mocca/ThreadedDownloads.java b/ex/ThreadedDownload/src/edu/vuum/mocca/ThreadedDownloads.java
deleted file mode 100644
index 7f676f4b2..000000000
--- a/ex/ThreadedDownload/src/edu/vuum/mocca/ThreadedDownloads.java
+++ /dev/null
@@ -1,544 +0,0 @@
-package edu.vuum.mocca;
-
-import java.io.InputStream;
-import java.lang.ref.WeakReference;
-import java.net.URL;
-
-import android.app.Activity;
-import android.app.ProgressDialog;
-import android.content.Context;
-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.util.Log;
-import android.view.Menu;
-import android.view.View;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.EditText;
-import android.widget.ImageView;
-import android.widget.Toast;
-
-/**
- * @class ThreadedDownloads
- *
- * @brief A class that allows a user to download a bitmap image using
- * any of the following concurrency models from the
- * . HaMeR framework
- * . Handlers and Runnables
- * . Handlers and Messages
- * . AsyncTask framework
- */
-public class ThreadedDownloads extends Activity {
- /**
- * Default URL to download
- */
- private final static String mDefaultURL =
- "http://www.dre.vanderbilt.edu/~schmidt/ka.png";
-
- /**
- * User's selection of URL to download
- */
- private EditText mUrlEditText;
-
- /**
- * Image that's been downloaded
- */
- private ImageView mImageView;
-
- /**
- * Display progress of download
- */
- private ProgressDialog mProgressDialog;
-
- /**
- * Debug Tag for logging debug output to LogCat
- */
- private final static String TAG =
- ThreadedDownloads.class.getSimpleName();
-
- /**
- * Method that initializes the Activity when it is first created.
- *
- * @param savedInstanceState
- * Activity's previously frozen state, if there was one.
- */
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- /**
- * Sets the content view specified in the main.xml file.
- */
- setContentView(R.layout.main);
-
- /**
- * Caches references to the EditText and ImageView objects in
- * data members to optimize subsequent access.
- */
- mUrlEditText = (EditText) findViewById(R.id.mUrlEditText);
- mImageView = (ImageView) findViewById(R.id.mImageView);
- }
-
- /**
- * Show a toast, notifying a user of an error when retrieving a
- * bitmap.
- */
- void showErrorToast(String errorString) {
- Toast.makeText(this,
- errorString,
- Toast.LENGTH_LONG).show();
- }
-
- /**
- * Display a downloaded bitmap image if it's non-null; otherwise,
- * it reports an error via a Toast.
- *
- * @param image
- * The bitmap image
- */
- void displayImage(Bitmap image)
- {
- if (mImageView == null)
- showErrorToast("Problem with Application,"
- + " please contact the Developer.");
- else if (image != null)
- mImageView.setImageBitmap(image);
- else
- showErrorToast("image is corrupted,"
- + " please check the requested URL.");
- }
-
- /**
- * Download a bitmap image from the URL provided by the user.
- *
- * @param url
- * The url where a bitmap image is located
- * @return the image bitmap or null if there was an error
- */
- private Bitmap downloadImage(String url) {
- /**
- * Use the default URL if the user doesn't supply one.
- */
- if (url.equals(""))
- url = mDefaultURL;
-
- try {
- /**
- * Connect to a remote server, download the contents of
- * the image, and provide access to it via an Input
- * Stream. */
- InputStream is =
- (InputStream) new URL(url).getContent();
-
- /**
- * Decode an InputStream into a Bitmap.
- */
- Bitmap image = BitmapFactory.decodeStream(is);
- return image;
- } catch (Exception e) {
- /**
- * Post error reports to the UI Thread.
- */
- this.runOnUiThread(new Runnable() {
- public void run() {
- /**
- * Use a Toast to inform user that something
- * has gone wrong.
- */
- showErrorToast("Error downloading image,"
- + " please check the requested URL.");
- }
- });
- Log.e(TAG, "Error downloading image");
- e.printStackTrace();
- return null;
- }
- }
-
- /**
- * Called when a user clicks a button to download an image with
- * Runnables and Handlers.
- *
- * @param view
- * The "Run Runnable" button
- */
- public void runRunnable(View view) {
- /**
- * Obtain the requested URL from the user input.
- */
- String url = getUrlString();
-
- hideKeyboard();
-
- /**
- * Inform the user that the download is starting.
- */
-
- showDialog("downloading via Runnables and Handlers");
-
- /**
- * Create and start a new Thread to download an image in the
- * background via a Runnable. The downloaded image is then
- * diplayed in the UI Thread by posting another Runnable via
- * the Activity's runOnUiThread() method, which uses an
- * internal Handler.
- */
- new Thread(new RunnableWithHandlers(url)).start();
- }
-
- /**
- * @class RunnablesWithHandler
- *
- * @brief This class downloads a bitmap image in the background
- * using Runnables and Handlers.
- */
- private class RunnableWithHandlers implements Runnable {
- /*
- * The URL to download.
- */
- String mUrl;
-
- /**
- * Class constructor caches the url of the bitmap image.
- *
- * @param url
- * The bitmap image url
- */
- RunnableWithHandlers(String url) {
- mUrl = url;
- }
-
- /**
- * Download a bitmap image in the background. It also sets the
- * image to an image view and dismisses the progress dialog.
- */
- public void run() {
- final Bitmap image = downloadImage(mUrl);
-
- ThreadedDownloads.this.runOnUiThread(new Runnable() {
- public void run() {
- /**
- * Dismiss the progress dialog.
- */
- mProgressDialog.dismiss();
-
- /**
- * Display the downloaded image to the user.
- */
- displayImage(image);
- }
- });
- }
- }
-
- /**
- * Called when a user clicks a button to download an image with
- * Handlers and Messages.
- *
- * @param view
- * The "Run Messages" button
- */
- public void runMessages(View view) {
- /**
- * Obtain the requested URL from the user input.
- */
- String url = getUrlString();
-
- hideKeyboard();
-
- /**
- * Create and start a new Thread to download an image in the
- * background and then use Messages and MessageHandler to
- * cause it to be displayed in the UI Thread.
- */
- new Thread(new RunnableWithMessages(url)).start();
- }
-
- /**
- * Display the Dialog to the User.
- *
- * @param message
- * The String to display what download method was used.
- */
- public void showDialog(String message) {
- mProgressDialog =
- ProgressDialog.show(this,"Download",message);
- }
-
- /**
- * Dismiss the Dialog
- */
- public void dismissDialog() {
- if (mProgressDialog != null)
- mProgressDialog.dismiss();
- }
-
- /**
- * @class MyHandler
- *
- * @brief A static inner class that inherits from Handler and uses
- * its handleMessage() hook method to process Messages sent
- * to it from a background Thread. Since it's static its
- * instances do not hold implicit references to their outer
- * classes.
- */
- private static class MessageHandler extends Handler {
- /**
- * Types of Messages that can be passed from a background
- * Thread to the UI Thread to specify which processing to
- * perform.
- */
- static final int SHOW_DIALOG = 1;
- static final int DISMISS_DIALOG = 2;
- static final int DISPLAY_IMAGE = 3;
-
- /**
- * Allows Activity to be garbage collected properly.
- */
- WeakReference mActivity;
-
- /**
- * Class constructor constructs mActivity as weak reference
- * to the activity
- *
- * @param activity
- * The corresponding activity
- */
- MessageHandler(ThreadedDownloads activity) {
- mActivity = new WeakReference(activity);
- }
-
- /**
- * Process the specified Messages passed to MessageHandler in
- * the UI Thread. These Messages instruct the Handler to start
- * showing the progress dialog, dismiss it, or display the
- * designated bitmap image via the ImageView.
- */
- public void handleMessage(Message msg) {
-
- /*
- * Check to see if the activity still exists and return if
- * not.
- */
- if (mActivity.get() == null)
- return;
-
- switch (msg.what) {
-
- case SHOW_DIALOG:
- mActivity.get().showDialog("downloading via Handlers and Messages");
- break;
-
- case DISMISS_DIALOG:
- /**
- * Dismiss the progress dialog.
- */
- mActivity.get().dismissDialog();
- break;
-
- case DISPLAY_IMAGE:
- /**
- * Display the downloaded image to the user.
- */
- mActivity.get().displayImage((Bitmap) msg.obj);
- break;
- }
- }
- }
-
- /**
- * Instance of MessageHandler.
- */
- MessageHandler messageHandler = new MessageHandler(this);
-
- /**
- * @class RunnableWithMessages
- *
- * @brief This class downloads a bitmap image in the background
- * using Handlers and Messages.
- */
- private class RunnableWithMessages implements Runnable {
- /*
- * The URL to download.
- */
- String mUrl;
-
- /**
- * Class constructor caches the url of a bitmap image and a
- * handler.
- *
- * @param url
- * The bitmap image url
- * @param h
- * The handler that handles Messages
- */
- RunnableWithMessages(String url) {
- this.mUrl = url;
- }
-
- /**
- * Download a bitmap image in a background Thread. It sends
- * various messages to the mHandler running in the UI Thread.
- */
- public void run() {
- /**
- * Store a copy of the reference to the MessageHandler.
- */
- final MessageHandler mHandler =
- ThreadedDownloads.this.messageHandler;
-
- /**
- * Factory creates a Message that instructs the
- * MessageHandler to begin showing the progress dialog to
- * the user.
- */
- Message msg =
- mHandler.obtainMessage(MessageHandler.SHOW_DIALOG,
- mProgressDialog);
-
- /**
- * Send the Message to initiate the ProgressDialog.
- */
- mHandler.sendMessage(msg);
-
- /**
- * Download the image.
- */
- final Bitmap image = downloadImage(mUrl);
-
- /**
- * Factory creates a Message that instructs the
- * MessageHandler to dismiss the progress dialog.
- */
- msg = mHandler.obtainMessage(MessageHandler.DISMISS_DIALOG,
- mProgressDialog);
- /**
- * Send the Message to dismiss the ProgressDialog.
- */
- mHandler.sendMessage(msg);
-
- /**
- * Factory creates a Message that instructs the
- * MessageHandler to display the image to the user.
- */
- msg = mHandler.obtainMessage(MessageHandler.DISPLAY_IMAGE,
- image);
- /**
- * Send the Message to instruct the UI Thread to display
- * the image.
- */
- mHandler.sendMessage(msg);
- }
- }
-
- /**
- * Called when a user clicks a button to download an image with
- * AsyncTask.
- *
- * @param view
- * The "Run Async" button
- */
- public void runAsyncTask(View view) {
-
- String url = getUrlString();
-
- hideKeyboard();
-
- new DownloadTask().execute(url);
- }
-
- /**
- * @class DownloadTask
- *
- * @brief This class downloads a bitmap image in the background
- * using AsyncTask.
- */
- private class DownloadTask extends AsyncTask {
- /**
- * Called by the AsyncTask framework in the UI Thread to
- * perform initialization actions.
- */
- protected void onPreExecute() {
- /**
- * Show the progress dialog before starting the download
- * in a Background Thread.
- */
- showDialog("downloading via AsyncTask");
- }
-
- /**
- * Downloads bitmap in an AsyncTask background thread.
- *
- * @param params
- * The url of a bitmap image
- */
- protected Bitmap doInBackground(String... urls) {
- return downloadImage(urls[0]);
- }
-
- /**
- * Called after an operation executing in the background is
- * completed. It sets the bitmap image to an image view and
- * dismisses the progress dialog.
- *
- * @param image
- * The bitmap image
- */
- protected void onPostExecute(Bitmap image) {
- /**
- * Dismiss the progress dialog.
- */
- dismissDialog();
-
- /**
- * Display the downloaded image to the user.
- */
- displayImage(image);
- }
- }
-
- /**
- * Called when a user clicks a button to reset an image to
- * default.
- *
- * @param view
- * The "Reset Image" button
- */
- public void resetImage(View view) {
- mImageView.setImageResource(R.drawable.default_image);
- }
-
- /**
- * Hide the keyboard after a user has finished typing the url.
- */
- private void hideKeyboard() {
- InputMethodManager mgr =
- (InputMethodManager) getSystemService
- (Context.INPUT_METHOD_SERVICE);
- mgr.hideSoftInputFromWindow(mUrlEditText.getWindowToken(),
- 0);
- }
-
- /**
- * Show a collection of menu items for the ThreadedDownloads
- * activity.
- *
- * @return true
- */
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.options, menu);
- return true;
- }
-
- /**
- * Read the URL EditText and return the String it contains.
- *
- * @return String value in mUrlEditText
- */
- String getUrlString() {
- return mUrlEditText.getText().toString();
- }
-
-}
diff --git a/ex/UniqueIDGeneratorApplication/.classpath b/ex/UniqueIDGeneratorApplication/.classpath
new file mode 100644
index 000000000..51769745b
--- /dev/null
+++ b/ex/UniqueIDGeneratorApplication/.classpath
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/ex/UniqueIDGeneratorApplication/.project b/ex/UniqueIDGeneratorApplication/.project
new file mode 100644
index 000000000..fe90b7f80
--- /dev/null
+++ b/ex/UniqueIDGeneratorApplication/.project
@@ -0,0 +1,33 @@
+
+
+ UniqueIDGeneratorApplication
+
+
+
+
+
+ com.android.ide.eclipse.adt.ResourceManagerBuilder
+
+
+
+
+ com.android.ide.eclipse.adt.PreCompilerBuilder
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ com.android.ide.eclipse.adt.ApkBuilder
+
+
+
+
+
+ com.android.ide.eclipse.adt.AndroidNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/ex/UniqueIDGeneratorApplication/AndroidManifest.xml b/ex/UniqueIDGeneratorApplication/AndroidManifest.xml
new file mode 100644
index 000000000..d43aa30ee
--- /dev/null
+++ b/ex/UniqueIDGeneratorApplication/AndroidManifest.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ex/UniqueIDGeneratorApplication/project.properties b/ex/UniqueIDGeneratorApplication/project.properties
new file mode 100644
index 000000000..258c89923
--- /dev/null
+++ b/ex/UniqueIDGeneratorApplication/project.properties
@@ -0,0 +1,15 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-18
+apk-configurations=
diff --git a/ex/UniqueIDGeneratorApplication/res/drawable/icon.png b/ex/UniqueIDGeneratorApplication/res/drawable/icon.png
new file mode 100644
index 000000000..504314e4a
Binary files /dev/null and b/ex/UniqueIDGeneratorApplication/res/drawable/icon.png differ
diff --git a/ex/UniqueIDGeneratorApplication/res/layout/main.xml b/ex/UniqueIDGeneratorApplication/res/layout/main.xml
new file mode 100644
index 000000000..50b41c351
--- /dev/null
+++ b/ex/UniqueIDGeneratorApplication/res/layout/main.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/ex/UniqueIDGeneratorApplication/res/values/strings.xml b/ex/UniqueIDGeneratorApplication/res/values/strings.xml
new file mode 100644
index 000000000..d96b4d00b
--- /dev/null
+++ b/ex/UniqueIDGeneratorApplication/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+
+ UniqueIDGeneratorApplication
+
diff --git a/ex/UniqueIDGeneratorApplication/src/edu/vuum/mocca/UniqueIDGeneratorActivity.java b/ex/UniqueIDGeneratorApplication/src/edu/vuum/mocca/UniqueIDGeneratorActivity.java
new file mode 100644
index 000000000..b6bf57df6
--- /dev/null
+++ b/ex/UniqueIDGeneratorApplication/src/edu/vuum/mocca/UniqueIDGeneratorActivity.java
@@ -0,0 +1,167 @@
+package edu.vuum.mocca;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.View;
+import android.widget.TextView;
+
+/**
+ * @class UniqueIDGeneratorActivity
+ *
+ * @brief A class that interacts with a user to generate a system-wide
+ * Unique ID via the UniqueIDGeneratorService, which is a Bound
+ * Service.
+ */
+public class UniqueIDGeneratorActivity extends Activity {
+ /**
+ * Used for debugging.
+ */
+ private final String TAG = getClass().getName();
+
+ /**
+ * Location where the unique ID is displayed.
+ */
+ private TextView mOutput;
+
+ /**
+ * Reference to the request Messenger that's implemented in the
+ * UniqueIDGeneratorService.
+ */
+ private Messenger mReqMessengerRef = null;
+
+ /**
+ * Reference to the ReplyMessenger that's passed to the
+ * UniqueIDGeneratorService and used to process replies from the
+ * Service.
+ */
+ private Messenger mReplyMessenger =
+ new Messenger(new ReplyHandler());
+
+ /**
+ * @class ReplyHandler
+ *
+ * @brief Receives the reply from the UniqueIDGeneratorService
+ * containing the unique ID and displays it to the user.
+ */
+ class ReplyHandler extends Handler {
+ /**
+ * Callback to handle the reply from the UniqueIDGeneratorService.
+ */
+ public void handleMessage(Message reply) {
+ // Get the unique ID encapsulated in reply Message.
+ String uniqueID =
+ UniqueIDGeneratorService.uniqueID(reply);
+
+ Log.d(TAG, "received unique ID " + uniqueID);
+
+ // Display the unique ID.
+ mOutput.setText(uniqueID);
+ }
+ }
+
+ /**
+ * This ServiceConnection is used to receive a Messenger reference
+ * after binding to the UniqueIDGeneratorService using bindService().
+ */
+ private ServiceConnection mSvcConn = new ServiceConnection() {
+ /**
+ * Called after the UniqueIDGeneratorService is connected to
+ * convey the result returned from onBind().
+ */
+ public void onServiceConnected(ComponentName className,
+ IBinder binder) {
+ Log.d(TAG, "ComponentName:" + className);
+
+ // Create a new Messenger that encapsulates the
+ // returned IBinder object and store it for later use
+ // in mReqMessengerRef.
+ mReqMessengerRef = new Messenger(binder);
+ }
+
+ /**
+ * Called if the Service crashes and is no longer
+ * available. The ServiceConnection will remain bound,
+ * but the Service will not respond to any requests.
+ */
+ public void onServiceDisconnected(ComponentName className) {
+ mReqMessengerRef = null;
+ }
+ };
+
+ /**
+ * Method that initializes the Activity when it is first created.
+ *
+ * @param savedInstanceState
+ * Activity's previously frozen state, if there was one.
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ mOutput = (TextView) findViewById(R.id.output);
+ }
+
+ /**
+ * Called by Android when the user presses the "Generate Unique
+ * ID" button to request a new unique ID from the
+ * UniqueIDGeneratorService.
+ */
+ public void generateUniqueID(View view) {
+ // Create a request Message that indicates the Service should
+ // send the reply back to ReplyHandler encapsulated by the
+ // Messenger.
+ Message request = Message.obtain();
+ request.replyTo = mReplyMessenger;
+ // Log.d(TAG, "mReplyMessenger = " + mReplyMessenger.hashCode());
+ try {
+ if (mReqMessengerRef != null) {
+ Log.d(TAG, "sending message");
+ // Send the request Message to the
+ // UniqueIDGeneratorService.
+ mReqMessengerRef.send(request);
+ }
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Hook method called by Android when this Activity becomes
+ * visible.
+ */
+ @Override
+ protected void onStart() {
+ super.onStart();
+ Log.d(TAG, "onStart()");
+
+ Log.d(TAG, "calling bindService()");
+ if (mReqMessengerRef == null)
+ // Bind to the UniqueIDGeneratorService associated with this
+ // Intent.
+ bindService(UniqueIDGeneratorService.makeIntent(this),
+ mSvcConn,
+ Context.BIND_AUTO_CREATE);
+ }
+
+ /**
+ * Hook method called by Android when this Activity becomes
+ * invisible.
+ */
+ @Override
+ protected void onStop() {
+ // Unbind from the Service.
+ unbindService(mSvcConn);
+ super.onStop();
+ }
+}
diff --git a/ex/UniqueIDGeneratorApplication/src/edu/vuum/mocca/UniqueIDGeneratorService.java b/ex/UniqueIDGeneratorApplication/src/edu/vuum/mocca/UniqueIDGeneratorService.java
new file mode 100644
index 000000000..b884fc1b6
--- /dev/null
+++ b/ex/UniqueIDGeneratorApplication/src/edu/vuum/mocca/UniqueIDGeneratorService.java
@@ -0,0 +1,221 @@
+package edu.vuum.mocca;
+
+import java.util.UUID;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.preference.PreferenceManager;
+
+/**
+ * @class UniqueIDGeneratorService
+ *
+ * @brief This Service generates unique IDs via a Thread pool and
+ * returns the IDs to the UniqueIDGeneratorActivity.
+ *
+ * This class implements the Synchronous Service layer of the
+ * Half-Sync/Half-Async pattern. It also implements a variant
+ * of the Factory Method pattern.
+ */
+public class UniqueIDGeneratorService extends Service {
+ /**
+ * Used for debugging.
+ */
+ private final String TAG = getClass().getName();
+
+ /**
+ * String used as a key for the unique ID stored in the reply
+ * Message.
+ */
+ final static String ID = "ID";
+
+ /**
+ * A RequestHandler that processes Messages from the
+ * UniqueIDGeneratorService within a pool of Threads.
+ */
+ private RequestHandler mRequestHandler = null;
+
+ /**
+ * A Messenger that encapsulates the RequestHandler used to handle
+ * request Messages sent from the UniqueIDGeneratorActivity.
+ */
+ private Messenger mReqMessenger = null;
+
+ /**
+ * Hook method called when the Service is created.
+ */
+ @Override
+ public void onCreate() {
+ // The Messenger encapsulates the RequestHandler
+ // used to handle request Messages sent from
+ // UniqueIDGeneratorActivity.
+ mRequestHandler = new RequestHandler();
+ mReqMessenger = new Messenger(mRequestHandler);
+ }
+
+ /**
+ * Factory method to make the desired Intent.
+ */
+ public static Intent makeIntent(Context context) {
+ // Create the Intent that's associated to the
+ // UniqueIDGeneratorService class.
+ return new Intent(context,
+ UniqueIDGeneratorService.class);
+ }
+
+ /**
+ * Extracts the encapsulated unique ID from the reply Message.
+ */
+ public static String uniqueID(Message replyMessage) {
+ return replyMessage.getData().getString(ID);
+ }
+
+ /**
+ * @class RequestHandler
+ *
+ * @brief This class generates unique IDs via a pool of Threads
+ * and sends them back to UniqueIDGeneratorActivity. When
+ * it's created, it creates a ThreadPoolExecutor using the
+ * newFixedThreadPool() method of the Executors class.
+ */
+ private class RequestHandler extends Handler {
+ /**
+ * A class constant that determines the maximum number of
+ * threads used to service download requests.
+ */
+ private final int MAX_THREADS = 4;
+
+ /**
+ * The ExecutorService implementation that references a
+ * ThreadPool.
+ */
+ private ExecutorService mExecutor;
+
+ /**
+ * A collection of unique IDs implemented internally using a
+ * persistent Java HashMap.
+ */
+ private SharedPreferences uniqueIDs = null;
+
+ /**
+ * Initialize RequestHandler to generate IDs concurrently.
+ */
+ public RequestHandler() {
+ // Get a SharedPreferences instance that points to the
+ // default file used by the preference framework in this
+ // Service.
+ uniqueIDs =
+ PreferenceManager.getDefaultSharedPreferences
+ (UniqueIDGeneratorService.this);
+
+ // Create a FixedThreadPool Executor that's configured to
+ // use MAX_THREADS.
+ mExecutor =
+ Executors.newFixedThreadPool(MAX_THREADS);
+ }
+
+ // Ensure threads used by the ThreadPoolExecutor complete and
+ // are reclaimed by the system.
+ public void shutdown() {
+ mExecutor.shutdown();
+ }
+
+ /**
+ * Return a Message containing an ID that's unique
+ * system-wide.
+ */
+ private Message generateUniqueID() {
+ String uniqueID;
+
+ // Protect critical section to ensure the IDs are unique.
+ synchronized (this) {
+ // This loop keeps generating a random UUID if it's
+ // not unique (i.e., is not currently found in the
+ // persistent collection of SharedPreferences). The
+ // likelihood of a non-unique UUID is low, but we're
+ // being extra paranoid for the sake of this example
+ // ;-)
+ do {
+ uniqueID = UUID.randomUUID().toString();
+ } while (uniqueIDs.getInt(uniqueID, 0) == 1);
+
+ // We found a unique ID, so add it as the "key" to the
+ // persistent collection of SharedPreferences, with a
+ // value of 1 to indicate this ID is already "used".
+ SharedPreferences.Editor editor = uniqueIDs.edit();
+ editor.putInt(uniqueID, 1);
+ editor.commit();
+ }
+
+ // Create a Message that's used to send the unique ID back
+ // to the UniqueIDGeneratorActivity.
+ Message reply = Message.obtain();
+ Bundle data = new Bundle();
+ data.putString(ID, uniqueID);
+ reply.setData(data);
+ return reply;
+ }
+
+ // Hook method called back when a request Message arrives from
+ // the UniqueIDGeneratorActivity. The message it receives
+ // contains the Messenger used to reply to the Activity.
+ public void handleMessage(Message request) {
+
+ // Store the reply Messenger so it doesn't change out from
+ // underneath us.
+ final Messenger replyMessenger = request.replyTo;
+
+ // Log.d(TAG, "replyMessenger = " + replyMessenger.hashCode());
+
+ // Put a runnable that generates a unique ID into the
+ // thread pool for subsequent concurrent processing.
+ mExecutor.execute(new Runnable() {
+ public void run () {
+ Message reply =
+ generateUniqueID();
+
+ try {
+ // Send the reply back to the
+ // UniqueIDGeneratorActivity.
+ // Log.d(TAG, "replyMessenger = " + replyMessenger.hashCode());
+ // try { Thread.sleep (10000); } catch (InterruptedException e) {}
+ replyMessenger.send(reply);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * Called when the service is destroyed, which is the last call
+ * the Service receives informing it to clean up any resources it
+ * holds.
+ */
+ @Override
+ public void onDestroy() {
+ // Ensure threads used by the ThreadPoolExecutor complete and
+ // are reclaimed by the system.
+ mRequestHandler.shutdown();
+ }
+
+ /**
+ * Factory method that returns the underlying IBinder associated
+ * with the Request Messenger.
+ */
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mReqMessenger.getBinder();
+ }
+}
+
diff --git a/grading-drivers/week-1-assignment-0/.classpath b/grading-drivers/week-1-assignment-0/.classpath
new file mode 100644
index 000000000..b10b8d9b2
--- /dev/null
+++ b/grading-drivers/week-1-assignment-0/.classpath
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/grading-drivers/week-1-assignment-0/.project b/grading-drivers/week-1-assignment-0/.project
new file mode 100644
index 000000000..3cc6dcf74
--- /dev/null
+++ b/grading-drivers/week-1-assignment-0/.project
@@ -0,0 +1,17 @@
+
+
+ W1-A0-SynchronizedQueueTest-Grading
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/grading-drivers/week-1-assignment-0/Assessment-Description.txt b/grading-drivers/week-1-assignment-0/Assessment-Description.txt
new file mode 100644
index 000000000..4ad64454b
--- /dev/null
+++ b/grading-drivers/week-1-assignment-0/Assessment-Description.txt
@@ -0,0 +1,56 @@
+Week 1: Programming Assignment 0 Peer Assessment Description
+
+Due Monday, June 2nd, 2014
+
+You will be asked to evaluate five other student's projects as well as
+do a self assessment of your own project. If you do not assess the
+work of others, then you will not pass this assignment.
+
+As you do your evaluation, please keep an open mind and focus on the
+positive. Our goal is not to find every way to deduct points over
+small deviations from the requirements or for legitimate differences
+in implementation styles, etc. Look for ways to give points when it's
+clear that the submitter has given a good faith effort to do the
+project, and when it's likely that they've succeeded.
+
+Review the submitter's source code. Since you did your own project,
+you know where the changes should be and you have a good idea of the
+kind of method calls and work flows that should be in the submitter's
+code. Remember that they may have done things differently than you
+did. That's fine.
+
+Use the grading driver files rooted in the "src" directory and make
+sure it compiles in your environment. This way, we ensure that all
+submissions are tested against the "master" regression tests and
+minimize the amount of code that comes from "the wild". Note that
+these grading drivers are in a different location than the original
+skeletons.
+
+Last, copy the submitter's SynchronizedQueue.java file into your IDE
+(which may have been given a random, odd name by Coursera, so you'll
+need to rename it) and store it in the src/edu/vuum/mocca directory.
+I recommend scanning the submitted code to ensure there are no changes
+other than adding the appropriate code as indicated by the "TODO"
+comments. A quick way to check for this automatically is to run "diff"
+on the original SynchronizedQueue.java file and the submitted
+SynchronizedQueue.java file to make sure all the changes are as
+expected, in the expected places in the code.
+
+Once you've reviewed the code against the rubric described below
+please try to compile and run it, walking through the steps called out
+in the grading video that we'll create in each week's "VIrtual Office
+Hours" (see item #38 in the POSA MOOC FAQ at
+http://www.coursera.org/course/posa for info on Virtual Office
+Hours). I suggest you make a fresh Eclipse workspace when doing this,
+so that the submitter's code is isolated and easily distinguishable
+from yours. One way you can do that is to select File > Open Workspace
+> Browse > New Folder. This will lead to a new, empty Eclipse
+workspace. Then you can copy that submitter's SynchronizedQueue.java
+file into this new workspace. Also, make sure to run the submitted
+code in an Android emulator, rather than on your Android device to
+further isolate it from causing harm.
+
+And again, remember that most everyone is working very hard and
+putting in a serious effort. When in doubt, err on the side of giving
+too many points, rather than giving too few.
+
diff --git a/grading-drivers/week-1-assignment-0/src/edu/vuum/mooca/SynchronizedQueue.java b/grading-drivers/week-1-assignment-0/src/edu/vuum/mooca/SynchronizedQueue.java
new file mode 100644
index 000000000..c4ffb3c18
--- /dev/null
+++ b/grading-drivers/week-1-assignment-0/src/edu/vuum/mooca/SynchronizedQueue.java
@@ -0,0 +1,265 @@
+package edu.vuum.mooca;
+import java.util.concurrent.*;
+
+/**
+ * @class SynchronizedQueue
+ *
+ * @brief This class tests the use of Java Threads and several
+ * implementations of the Java BlockingQueue interface.
+ */
+public class SynchronizedQueue {
+ /**
+ * Keep track of the number of times the producer test iterates.
+ */
+ static volatile int mProducerCounter = 0;
+
+ /**
+ * Keep track of the number of times the consumer test iterates.
+ */
+ static volatile int mConsumerCounter = 0;
+
+ /**
+ * Maximum timeout.
+ */
+ static final int TIMEOUT_SECONDS = 5;
+
+ /**
+ * Error value for a timeout.
+ */
+ static final int TIMEOUT_OCCURRED = -1;
+
+ /**
+ * Error value for a failure.
+ */
+ static final int FAILURE_OCCURRED = -2;
+
+ /**
+ * @class SynchronizedQueueResult
+ *
+ * @brief Enumerated type for return values of testing logic, has
+ * String for easy output.
+ */
+ public enum SynchronizedQueueResult {
+ RAN_PROPERLY("Threads Ran Properly."),
+ JOIN_NEVER_CALLED("Join() never called."),
+ THREADS_NEVER_RAN("Threads never ran."),
+ THREADS_NEVER_INTERUPTED("Threads never interrupted."),
+ THREADS_THREW_EXCEPTION("Thread threw an exception."),
+ THREADS_NEVER_CREATED("Threads never created."),
+ TESTING_LOGIC_THREW_EXCEPTION("Testing Logic threw Exception."),
+ THREADS_TIMEDOUT("Threads Timed-out, Interupt likely not called.");
+
+ /**
+ * String value for the enumerated type.
+ */
+ private String mValue = null;
+
+ /**
+ * Initialize the mValue string.
+ */
+ private SynchronizedQueueResult(String value) {
+ mValue = value;
+ }
+
+ /**
+ * Return the mValue string.
+ */
+ public String getString() {
+ return mValue;
+ }
+ }
+
+ /**
+ * @class QueueAdapter
+ *
+ * @brief Applies a variant of the GoF Adapter pattern that
+ * enables us to test several implementations of the
+ * BlockingQueue interface.
+ */
+ public static class QueueAdapter {
+ /**
+ * Stores the queue that we're adapting.
+ */
+ private BlockingQueue mQueue;
+
+ /**
+ * Store the queue that we're adapting.
+ */
+ public QueueAdapter(BlockingQueue queue) {
+ mQueue = queue;
+ }
+
+ /**
+ * Insert msg at the tail of the queue.
+ *
+ * @throws TimeoutException and InterruptedException
+ */
+ public void put(E msg) throws InterruptedException, TimeoutException {
+ // Keep track of how many times we're called.
+ mProducerCounter++;
+ boolean timeoutValue = mQueue.offer(msg,
+ TIMEOUT_SECONDS,
+ TimeUnit.SECONDS);
+ if (timeoutValue == false)
+ throw new TimeoutException();
+ }
+
+ /**
+ * Remove msg from the head of the queue.
+ *
+ * @throws TimeoutException
+ * , InterruptedException
+ */
+ public E take() throws InterruptedException, TimeoutException {
+ // Keep track of how many times we're called.
+ mConsumerCounter++;
+ E rValue = mQueue.poll(TIMEOUT_SECONDS,
+ TimeUnit.SECONDS);
+
+ if (rValue == null)
+ throw new TimeoutException();
+
+ return rValue;
+ }
+ }
+
+ /**
+ * Adapter object used to test different BlockingQueue
+ * implementations.
+ */
+ private static QueueAdapter mQueue = null;
+
+ /**
+ * This runnable loops for mMaxIterations and calls put() on
+ * mQueue to insert the iteration number into the queue.
+ */
+ static Runnable producerRunnable = new Runnable() {
+ public void run() {
+ for (int i = 0; i < mMaxIterations; i++)
+ try {
+ mQueue.put(i);
+ if (Thread.interrupted())
+ throw new InterruptedException();
+ } catch (InterruptedException e) {
+ System.out.println("Thread properly interrupted by "
+ + e.toString() + " in producerRunnable");
+ // This isn't an error - it just means that
+ // we've been interrupted by the main Thread.
+ return;
+ } catch (TimeoutException e) {
+ System.out.println("Exception " + e.toString()
+ + " occurred in producerRunnable");
+ // Indicate a timeout.
+ mProducerCounter = TIMEOUT_OCCURRED;
+ return;
+ } catch (Exception e) {
+ System.out.println("Exception " + e.toString()
+ + " occurred in producerRunnable");
+ // Indicate a failure.
+ mProducerCounter = FAILURE_OCCURRED;
+ return;
+ }
+ }
+ };
+
+ /**
+ * This runnable loops for mMaxIterations and calls take() on mQueue to
+ * remove the iteration from the queue.
+ */
+ static Runnable consumerRunnable = new Runnable() {
+ public void run() {
+ for (int i = 0; i < mMaxIterations; i++)
+ try {
+ if (Thread.interrupted()) {
+ throw new InterruptedException();
+ }
+ Integer result = (Integer) mQueue.take();
+
+ System.out.println("iteration = " + result);
+ } catch (InterruptedException e) {
+ System.out.println("Thread properly interrupted by "
+ + e.toString() + " in consumerRunnable");
+ // This isn't an error - it just means that
+ // we've been interrupted by the main Thread.
+ return;
+ } catch (TimeoutException e) {
+ System.out.println("Exception " + e.toString()
+ + " occurred in consumerRunnable");
+ // Indicate a timeout.
+ mConsumerCounter = TIMEOUT_OCCURRED;
+ return;
+ } catch (Exception e) {
+ System.out.println("Exception " + e.toString()
+ + " occurred in consumerRunnable");
+ // Indicate a failure.
+ mConsumerCounter = FAILURE_OCCURRED;
+ return;
+ }
+ }
+ };
+
+ /**
+ * Number of iterations to test (the actual test shouldn't run
+ * this many iterations since the Threads ought to be interrupted
+ * long before it gets this far).
+ */
+ public static int mMaxIterations = 1000000;
+
+ /**
+ * Run the test for the queue parameter.
+ */
+ public static SynchronizedQueueResult testQueue(QueueAdapter queue) {
+ try {
+ mQueue = queue;
+
+ // Please make sure to keep all the "TODO" comments in the
+ // code below to make it easy for peer reviewers to find
+ // them.
+
+ // TODO - you fill in here to replace the null
+ // initialization below to create two Java Threads, one
+ // that's passed the producerRunnable and the other that's
+ // passed the consumerRunnable.
+ Thread consumer = null;
+ Thread producer = null;
+
+ // TODO - you fill in here to start the threads. More
+ // interesting results will occur if you start the
+ // consumer first.
+
+ // Give the Threads a chance to run before interrupting
+ // them.
+ Thread.sleep(100);
+
+ // TODO - you fill in here to interrupt the threads.
+
+ // TODO - you fill in here to wait for the threads to
+ // exit.
+
+ // Do some sanity checking to see if the Threads work as
+ // expected.
+ if (consumer == null
+ || producer == null)
+ return SynchronizedQueueResult.THREADS_NEVER_CREATED;
+ else if (consumer.isAlive()
+ || producer.isAlive())
+ return SynchronizedQueueResult.JOIN_NEVER_CALLED;
+ else if (mConsumerCounter == 0
+ || mProducerCounter == 0)
+ return SynchronizedQueueResult.THREADS_NEVER_RAN;
+ else if (mConsumerCounter == mMaxIterations
+ || mProducerCounter == mMaxIterations)
+ return SynchronizedQueueResult.THREADS_NEVER_INTERUPTED;
+ else if (mConsumerCounter == FAILURE_OCCURRED
+ || mProducerCounter == FAILURE_OCCURRED)
+ return SynchronizedQueueResult.THREADS_THREW_EXCEPTION;
+ else if (mConsumerCounter == TIMEOUT_OCCURRED
+ || mProducerCounter == TIMEOUT_OCCURRED)
+ return SynchronizedQueueResult.THREADS_TIMEDOUT;
+ else
+ return SynchronizedQueueResult.RAN_PROPERLY;
+ } catch (Exception e) {
+ return SynchronizedQueueResult.TESTING_LOGIC_THREW_EXCEPTION;
+ }
+ }
+}
diff --git a/grading-drivers/week-1-assignment-0/src/edu/vuum/mooca/SynchronizedQueueTest.java b/grading-drivers/week-1-assignment-0/src/edu/vuum/mooca/SynchronizedQueueTest.java
new file mode 100644
index 000000000..427448946
--- /dev/null
+++ b/grading-drivers/week-1-assignment-0/src/edu/vuum/mooca/SynchronizedQueueTest.java
@@ -0,0 +1,71 @@
+package edu.vuum.mooca;
+import static org.junit.Assert.*;
+
+import java.util.concurrent.ArrayBlockingQueue;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import edu.vuum.mooca.SynchronizedQueue.*;
+
+/**
+ * @class SynchronizedQueueTest
+ *
+ * @brief This class tests queues for proper functionality by
+ * using the testQueue method defined in SynchronizedQueue.java
+ */
+public class SynchronizedQueueTest {
+ /**
+ * Indicates how big the queue should be.
+ */
+ int queueSize;
+
+ /**
+ * Run the test for the queue parameter.
+ *
+ * @return result. If SynchronizedQueue test ran properly, returns
+ * null. If not, returns error message.
+ */
+ static String runQueueTest(String qName, QueueAdapter queue) {
+ System.out.println("Starting " + qName + " test...");
+
+ SynchronizedQueueResult result =
+ SynchronizedQueue.testQueue(queue);
+
+ System.out.println("End " + qName + " test.\n");
+ System.out.println("See JUnit view for results -- \n" +
+ "Green check-marks denote program correctness. \n" +
+ "Blue x-marks indicate a problem with your implementation. \n");
+
+ if (result != SynchronizedQueueResult.RAN_PROPERLY)
+ return result.getString();
+
+ return null;
+ }
+
+ /**
+ * Runs before each test. Sets queueSize.
+ * @throws Exception
+ */
+ @Before
+ public void setUp() throws Exception {
+ // Indicate how big the queue should be, which should be
+ // smaller than the number of iterations to induce blocking
+ // behavior.
+ queueSize = SynchronizedQueue.mMaxIterations / 10;
+ }
+
+ /**
+ * Tests the ArrayBlockingQueue, which should pass without error.
+ */
+ @Test
+ public void arrayBlockingQueueTest() {
+ QueueAdapter queueAdapter =
+ new QueueAdapter(new ArrayBlockingQueue(queueSize));
+ String errors = runQueueTest("ArrayBlockingQueue", queueAdapter);
+
+ assertNull("Error occurred: " +
+ errors,
+ errors);
+ }
+}
diff --git a/grading-drivers/week-2-assignment-1/.classpath b/grading-drivers/week-2-assignment-1/.classpath
new file mode 100644
index 000000000..3e0fb272a
--- /dev/null
+++ b/grading-drivers/week-2-assignment-1/.classpath
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/grading-drivers/week-2-assignment-1/.project b/grading-drivers/week-2-assignment-1/.project
new file mode 100644
index 000000000..72dbe761b
--- /dev/null
+++ b/grading-drivers/week-2-assignment-1/.project
@@ -0,0 +1,17 @@
+
+
+ W2-A1-SimpleAtomicLong-Grading
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/grading-drivers/week-2-assignment-1/Assessment-Description.txt b/grading-drivers/week-2-assignment-1/Assessment-Description.txt
new file mode 100644
index 000000000..724b2df23
--- /dev/null
+++ b/grading-drivers/week-2-assignment-1/Assessment-Description.txt
@@ -0,0 +1,57 @@
+Week 2: Programming Assignment 1 Peer Assessment Description
+
+Due Monday, June 9th, 2014
+
+You will be asked to evaluate five other student's projects as well as
+do a self assessment of your own project. If you do not assess the
+work of others, then you will not pass this assignment.
+
+As you do your evaluation, please keep an open mind and focus on the
+positive. Our goal is not to find every way to deduct points over
+small deviations from the requirements or for legitimate differences
+in implementation styles, etc. Look for ways to give points when it's
+clear that the submitter has given a good faith effort to do the
+project, and when it's likely that they've succeeded.
+
+Review the submitter's source code. Since you did your own project,
+you know where the changes should be and you have a good idea of the
+kind of method calls and work flows that should be in the submitter's
+code. Remember that they may have done things differently than you
+did. That's fine.
+
+Next, get a copy of the grading driver files from
+
+https://github.com/douglascraigschmidt/POSA-14/tree/master/grading-drivers/week-2-assignment-1
+
+and copy the submitter's SimpleAtomicLong.java file into your IDE
+(which may have been given a random, odd name by Coursera, so you'll
+need to rename it) and store it in the src/edu/vuum/mocca/ directory.
+I recommend scanning the submitted code to ensure there are no changes
+other than adding the appropriate code as indicated by the "TODO"
+comments. A quick way to check for this automatically is to run "diff"
+on the original SimpleAtomicLong.java file and the submitted
+SimpleAtomicLong.java file to make sure all the changes are as
+expected, in the expected places in the code. Then make sure
+everything compiles properly in your environment. Note that the
+grading drivers are in a different location than the original
+skeletons to ensure that all submissions are tested against the
+"master" regression tests and minimize the amount of code that comes
+from "the wild".
+
+Once you've reviewed the code against the rubric described below
+please try to run it, walking through the steps called out in the
+grading video that we'll create in each week's "VIrtual Office Hours"
+(see item #38 in the POSA MOOC FAQ at
+http://www.coursera.org/course/posa for info on Virtual Office
+Hours). I suggest you make a fresh Eclipse workspace when doing this,
+so that the submitter's code is isolated and easily distinguishable
+from yours. One way you can do that is to select File > Open Workspace
+> Browse > New Folder. This will lead to a new, empty Eclipse
+workspace. Then you can copy that submitter's SimpleAtomicLong.java
+file into this new workspace. Also, it's recommended to run the
+submitted code a virtual machine sandbox to further isolate it from
+causing harm.
+
+And again, remember that most everyone is working very hard and
+putting in a serious effort. When in doubt, err on the side of giving
+too many points, rather than giving too few.
diff --git a/grading-drivers/week-2-assignment-1/src/edu/vuum/mocca/BuggyLongTest.java b/grading-drivers/week-2-assignment-1/src/edu/vuum/mocca/BuggyLongTest.java
new file mode 100644
index 000000000..fc854689b
--- /dev/null
+++ b/grading-drivers/week-2-assignment-1/src/edu/vuum/mocca/BuggyLongTest.java
@@ -0,0 +1,117 @@
+package edu.vuum.mocca;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+
+/**
+ * @class BuggyLongTest
+ *
+ * @brief This class shows what happens if concurrent access to a long
+ * isn't properly serialized.
+ */
+class BuggyLongTest
+{
+ /**
+ * Number of iterations to run the test.
+ */
+ static final long mMaxIterations = 100000000;
+
+ /**
+ * Barrier synchronizer that controls when the two threads start
+ * running the test.
+ */
+ static CyclicBarrier mStartBarrier = new CyclicBarrier(2);
+
+ /**
+ * Barrier synchronizer that controls when the main thread can
+ * return.
+ */
+ static CountDownLatch mStopLatch = new CountDownLatch(2);
+
+ /**
+ * An instance of long that's not protected by a lock.
+ */
+ static long mCounter = 0;
+
+ /**
+ * Main entry point method that runs the test.
+ */
+ public static void main(String[] args) {
+ try {
+ System.out.println("Starting BuggyLongTest");
+
+ /**
+ * Start a Thread whose Runnable decrements the SimpleAtomicLong
+ * mMaxIterations number of times.
+ */
+ new Thread(new Runnable()
+ { public void run() {
+ try
+ {
+ /**
+ * Wait for both Threads to start running
+ * before beginning the loop.
+ */
+ mStartBarrier.await();
+
+ for (int i = 0; i < mMaxIterations; ++i)
+ mCounter--;
+
+ /**
+ * Inform the main thread that we're done.
+ */
+ mStopLatch.countDown();
+ }
+ catch (Exception e)
+ {
+ System.out.println("problem here");
+ }
+ }
+ }).start();
+
+ /**
+ * Start a Thread whose Runnable increments the SimpleAtomicLong
+ * mMaxIterations number of times.
+ */
+ new Thread(new Runnable()
+ { public void run() {
+ try {
+ /**
+ * Wait for both Threads to start running
+ * before beginning the loop.
+ */
+ mStartBarrier.await();
+
+ for (int i = 0; i < mMaxIterations; ++i)
+ mCounter++;
+
+ /**
+ * Inform the main thread that we're done.
+ */
+ mStopLatch.countDown();
+ }
+ catch (Exception e) { }
+ }
+ }).start();
+
+ /**
+ * Barrier synchronizer that waits for both worker threads
+ * to exit before continuing.
+ */
+ mStopLatch.await();
+
+ long result = mCounter;
+ /**
+ * Check to ensure the test worked, i.e., mCounter's value
+ * should be 0.
+ */
+ if (result == 0)
+ System.out.println("test worked");
+ else
+ System.out.println("test failed: mCounter = " + result);
+
+ System.out.println("Finishing BuggyLongTest");
+ }
+ catch (Exception e) { }
+ }
+}
diff --git a/grading-drivers/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLong.java b/grading-drivers/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLong.java
new file mode 100644
index 000000000..f57064f95
--- /dev/null
+++ b/grading-drivers/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLong.java
@@ -0,0 +1,80 @@
+// Import the necessary Java synchronization and scheduling classes.
+
+package edu.vuum.mocca;
+
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.concurrent.locks.Lock;
+
+/**
+ * @class SimpleAtomicLong
+ *
+ * @brief This class implements a subset of the
+ * java.util.concurrent.atomic.SimpleAtomicLong class using a
+ * ReentrantReadWriteLock to illustrate how they work.
+ */
+class SimpleAtomicLong
+{
+ /**
+ * The value that's manipulated atomically via the methods.
+ */
+ private long mValue;
+
+
+ /**
+ * The ReentrantReadWriteLock used to serialize access to mValue.
+ */
+ // TODO - add the implementation
+
+ /**
+ * Creates a new SimpleAtomicLong with the given initial value.
+ */
+ public SimpleAtomicLong(long initialValue) {
+ // TODO - you fill in here
+ }
+
+ /**
+ * @brief Gets the current value
+ *
+ * @returns The current value
+ */
+ public long get() {
+ // TODO - you fill in here
+ }
+
+ /**
+ * @brief Atomically decrements by one the current value
+ *
+ * @returns the updated value
+ */
+ public long decrementAndGet() {
+ // TODO - you fill in here
+ }
+
+ /**
+ * @brief Atomically increments by one the current value
+ *
+ * @returns the previous value
+ */
+ public long getAndIncrement() {
+ // TODO - you fill in here
+ }
+
+ /**
+ * @brief Atomically decrements by one the current value
+ *
+ * @returns the previous value
+ */
+ public long getAndDecrement() {
+ // TODO - you fill in here
+ }
+
+ /**
+ * @brief Atomically increments by one the current value
+ *
+ * @returns the updated value
+ */
+ public long incrementAndGet() {
+ // TODO - you fill in here
+ }
+}
+
diff --git a/grading-drivers/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLongMultithreadedTest.java b/grading-drivers/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLongMultithreadedTest.java
new file mode 100644
index 000000000..afde62c6f
--- /dev/null
+++ b/grading-drivers/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLongMultithreadedTest.java
@@ -0,0 +1,322 @@
+package edu.vuum.mocca;
+
+import static org.junit.Assert.*;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * @class SimpleAtomicLongMultithreadedTest
+ *
+ * @brief Test the logic and multithreaded implementation of the
+ * SimpleAtomicLong class by having concurrent threads call the
+ * SimpleAtomicLong instance for various methods.
+ */
+public class SimpleAtomicLongMultithreadedTest {
+
+ /**
+ * Our start points.
+ */
+ final static long INITIAL_VALUE = 0;
+
+ /**
+ * Number of iterations to run the commands.
+ */
+ final static long mMaxIterations = 1000000;
+
+ /**
+ * Barrier synchronizer that controls when the threads start
+ * running the test.
+ */
+ static CyclicBarrier mStartBarrier;
+
+ /**
+ * Barrier synchronizer that controls when the main thread can
+ * return.
+ */
+ static CountDownLatch mStopLatch;
+
+ /**
+ * An instance of our implementation of SimpleAtomicLong, which is
+ * defined as "volatile" to ensure proper visibility of its fields
+ * after construction.
+ */
+ static volatile SimpleAtomicLong mCounter;
+
+ /**
+ * Runnable commands that use the mCounter methods
+ * get()
+ * incrementAndGet()
+ * getAndIncrement()
+ * decrementAndGet()
+ * getAndDecrement()
+ */
+ static Runnable getCommand;
+ static Runnable incrementGetCommand;
+ static Runnable getIncrementCommand;
+ static Runnable decrementGetCommand;
+ static Runnable getDecrementCommand;
+
+ /**
+ * The value of mCounter prior to any changes made by testing.
+ */
+ long preTestValue;
+
+ /**
+ * The number of duplicate threads to run when testing each
+ * individual command.
+ */
+ final int numThreads = 5;
+
+ /**
+ * @class RunTest
+ *
+ * @brief This class runs the test by invoking a command each time
+ * through the loop.
+ */
+ static class RunTest implements Runnable
+ {
+ /**
+ * A Command that determines what operation is done within the
+ * loop.
+ */
+ private Runnable mCommand;
+
+ /**
+ * An integer which keeps track of the number of times the
+ * command has been called. Initially equal to zero.
+ */
+ private long iterations = 0;
+
+ /**
+ * Store the command in a data member field.
+ */
+ RunTest(Runnable command) {
+ mCommand = command;
+ }
+
+ /**
+ * Run the command within a loop.
+ */
+ public void run() {
+ try
+ {
+ /**
+ * Wait for all Threads to start running before
+ * beginning the loop.
+ */
+ mStartBarrier.await();
+
+ for (; iterations < mMaxIterations; ++iterations) {
+ mCommand.run();
+ }
+ /**
+ * Inform the main thread that we're done.
+ */
+ mStopLatch.countDown();
+ } catch (Exception e) {
+ fail("Runnable failed.");
+ }
+ }
+
+ /**
+ * Returns the number of times this command has been performed.
+ * @return iterations
+ */
+ public long getIterations() {
+ return iterations;
+ }
+ }
+
+ /**
+ * Runs prior to all tests. Creates a static instance of
+ * SimpleAtomicLong and all runnable commands.
+ */
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ /**
+ * Instance of SimpleAtomicLong class
+ */
+ mCounter = new SimpleAtomicLong(INITIAL_VALUE);
+
+ /**
+ * Runnable commands that execute get(), incrementAndGet(),
+ * getAndIncrement(), decrementAndGet(), getAndDecrement(),
+ * respectively, on the SimpleAtomicLong instance
+ */
+ getCommand = new Runnable() { public void run() { mCounter.get(); } };
+ incrementGetCommand = new Runnable() { public void run() { mCounter.incrementAndGet(); } };
+ getIncrementCommand = new Runnable() { public void run() { mCounter.getAndIncrement(); } };
+ decrementGetCommand = new Runnable() { public void run() { mCounter.decrementAndGet(); } };
+ getDecrementCommand = new Runnable() { public void run() { mCounter.getAndDecrement(); } };
+ }
+
+ /**
+ * Runs prior to each test. Stores the pre-test value of the mCounter.
+ */
+ @Before
+ public void setUp() throws Exception {
+ preTestValue = mCounter.get();
+ }
+
+ /**
+ * Tests for proper concurrency and functionality of {@code get()}.
+ */
+ @Test
+ public void multiGetTest() {
+ /**
+ * run multiple threads calling mCounter.get().
+ */
+ runThreads(getCommand);
+ /**
+ * The expected post-test value is no change in the pre-test
+ * value.
+ */
+ assertEquals(preTestValue,
+ mCounter.get());
+ }
+
+ /**
+ * Tests for proper concurrency and functionality of {@code
+ * incrementAndGet()}.
+ */
+ @Test
+ public void multiIncrementAndGetTest() {
+ runThreads(incrementGetCommand);
+ /**
+ * expected value after threads are run should be the number
+ * of maximum iterations times the number of threads plus the
+ * pre-test value
+ */
+ assertEquals(preTestValue
+ + mMaxIterations*numThreads,
+ mCounter.get());
+ }
+
+ /**
+ * Tests for proper concurrency and functionality of {@code getAndIncrement()}.
+ */
+ @Test
+ public void multiGetAndIncrementTest() {
+ runThreads(getIncrementCommand);
+ assertEquals(preTestValue
+ + mMaxIterations*numThreads,
+ mCounter.get());
+ }
+
+ /**
+ * Tests for proper concurrency and functionality of {@code
+ * decrementAndGet()}.
+ */
+ @Test
+ public void multiDecrementAndGetTest() {
+ runThreads(decrementGetCommand);
+ /**
+ * Expected value of mCounter after threads have completed
+ * running is the pre-test value minus the maximum iterations
+ * times the number of threads that were run.
+ */
+ assertEquals(preTestValue -
+ mMaxIterations*numThreads,
+ mCounter.get());
+ }
+
+ /**
+ * Tests for proper concurrency and functionality of {@code getAndIncrement()}.
+ */
+ @Test
+ public void multiGetAndDecrementTest() {
+ runThreads(getDecrementCommand);
+ /**
+ * expected value of mCounter after threads have completed running
+ * is the pre-test value minus the maximum iterations times the number
+ * of threads that were run
+ */
+ assertEquals(preTestValue -
+ mMaxIterations*numThreads,
+ mCounter.get());
+ }
+
+ /**
+ * Tests concurrent running of threads performing a variety of
+ * operations on mCounter (e.g. {@code get()}, {@code
+ * getAndIncrement()}, {@code getAndDecrement()},{@code
+ * incrementAndGet()}, and {@code decrementAndGet()}).
+ */
+ @Test
+ public void multiThreadedTest() {
+ /**
+ * Run five threads concurrently, each performing a different
+ * method on the SimpleAtomicLong instance
+ */
+ runThreads(null);
+ /**
+ * Check to ensure the pre-test and post-test values are
+ * equal. This indicates the test was successful.
+ */
+ assertEquals(preTestValue,
+ mCounter.get());
+ }
+
+ /**
+ * Runs numThreads concurrent threads executing the same command.
+ * Has a CyclicBarrier and CountDownLatch to facilitate
+ * concurrency.
+ * @param command
+ */
+ private void runThreads(Runnable command) {
+ mStartBarrier = new CyclicBarrier(numThreads);
+ mStopLatch = new CountDownLatch(numThreads);
+ try {
+ /**
+ * Create an array of RunTests whose Runnable commands
+ * execute on the SimpleAtomicLong mMaxIterations number
+ * of times. If given a command, each thread should run
+ * duplicates. If command is null, then run one of each
+ * type of command.
+ */
+ RunTest[] runTests;
+ if(command == null) {
+ runTests = new RunTest[5];
+ runTests[0] = new RunTest(getCommand);
+ runTests[1] = new RunTest(incrementGetCommand);
+ runTests[2] = new RunTest(decrementGetCommand);
+ runTests[3] = new RunTest(getDecrementCommand);
+ runTests[4] = new RunTest(getIncrementCommand);
+ }
+ else {
+ runTests = new RunTest[numThreads];
+ for(int i = 0; i < runTests.length; i++)
+ runTests[i] = new RunTest(command);
+ }
+
+ /**
+ * Start threads whose Runnable commands execute on the
+ * SimpleAtomicLong mMaxIterations number of times.
+ */
+ for(int i = 0; i < runTests.length; i++)
+ new Thread(runTests[i]).start();
+
+ /**
+ * Barrier synchronizer that waits for all worker threads
+ * to exit before continuing.
+ */
+ mStopLatch.await();
+
+ /**
+ * Check to ensure threads have run.
+ */
+ for(int i = 0; i < runTests.length; i++)
+ assertEquals("Threads have not executed.",
+ mMaxIterations,
+ runTests[i].getIterations());
+ } catch (Exception e) {
+ fail("Exception thrown.");
+ }
+ }
+
+}
diff --git a/grading-drivers/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLongSingleThreadedTest.java b/grading-drivers/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLongSingleThreadedTest.java
new file mode 100644
index 000000000..ef28e3e0a
--- /dev/null
+++ b/grading-drivers/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLongSingleThreadedTest.java
@@ -0,0 +1,235 @@
+package edu.vuum.mocca;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @brief SimpleAtomicLongSingleThreadedTest
+ *
+ * @class Evalutes the logic of the SimpleAtomicLong class by testing
+ * every method with each of the values in mTestValues.
+ */
+public class SimpleAtomicLongSingleThreadedTest {
+ /*
+ * Test possible edge cases at 0, and a large negative and
+ * positive.
+ */
+ final static long[] mTestValues = { -100, -1, 0, 1, 100 };
+
+ /**
+ * Test Constructor.
+ */
+ @Test
+ public void constructorTest() {
+ for (long testValue : mTestValues) {
+ SimpleAtomicLong counter =
+ new SimpleAtomicLong(testValue);
+ Assert.assertNotNull(counter);
+ assertEquals(testValue, counter.get());
+ }
+ }
+
+ /**
+ * Test get()
+ */
+ @Test
+ public void getTest() {
+ for (long testValue : mTestValues) {
+ SimpleAtomicLong counter = new SimpleAtomicLong(testValue);
+ assertEquals(testValue,
+ counter.get());
+ }
+ }
+
+ /**
+ * test decrementAndGet()
+ */
+ @Test
+ public void decrementAndGetTest() {
+ for (long testValue : mTestValues) {
+ final SimpleAtomicLong counter =
+ new SimpleAtomicLong(testValue);
+ decrementAndGetTestLogic(counter,
+ testValue,
+ testValue - 1,
+ testValue - 1);
+ }
+ }
+
+ /**
+ * test getAndDecrement()
+ */
+ @Test
+ public void getAndDecrementTest() {
+ for (long testValue : mTestValues) {
+ final SimpleAtomicLong counter =
+ new SimpleAtomicLong(testValue);
+ getAndDecrementTestLogic(counter,
+ testValue,
+ testValue,
+ testValue - 1);
+ }
+ }
+
+ /**
+ * test incrementAndGet()
+ */
+ @Test
+ public void incrementAndGetTestTest() {
+ for (long testValue : mTestValues) {
+ final SimpleAtomicLong counter =
+ new SimpleAtomicLong(testValue);
+ incrementAndGetTestLogic(counter,
+ testValue,
+ testValue + 1,
+ testValue + 1);
+ }
+ }
+
+ /**
+ * test getAndIncrement()
+ */
+ @Test
+ public void getAndIncrementTest() {
+ for (long testValue : mTestValues) {
+ final SimpleAtomicLong counter =
+ new SimpleAtomicLong(testValue);
+ getAndIncrementTestLogic(counter,
+ testValue,
+ testValue,
+ testValue + 1);
+ }
+ }
+
+ /**
+ * Compares the values expected with the values produced by each test logic.
+ *
+ * @param pre
+ * The 'pre' number produced by the test
+ * @param preValue
+ * The 'pre' number expected
+ * @param result
+ * The 'result' number produced by the test
+ * @param resultValue
+ * The 'result' expected
+ * @param post
+ * The 'post' number produced by the test
+ * @param postValue
+ * The 'post' expected
+ */
+ private void compareResults(long pre, long preValue, long result,
+ long resultValue, long post, long postValue) {
+ assertEquals(pre, preValue);
+ assertEquals(result, resultValue);
+ assertEquals(post, postValue);
+ }
+
+ /**
+ * The Logic of testing decrementAndGet
+ *
+ * @param simpleAtomicLong
+ * The SimpleAtomicLong to be tested
+ * @param preValue
+ * The expected 'pre' value
+ * @param resultValue
+ * The expected 'result' value
+ * @param postValue
+ * The expected 'post' value
+ */
+ public void decrementAndGetTestLogic(SimpleAtomicLong simpleAtomicLong,
+ long preValue, long resultValue, long postValue) {
+ long pre = simpleAtomicLong.get();
+ long result = simpleAtomicLong.decrementAndGet();
+ long post = simpleAtomicLong.get();
+ assertEquals(pre - 1, result);
+ assertEquals(result, post);
+ compareResults(pre,
+ preValue,
+ result,
+ resultValue,
+ post,
+ postValue);
+ }
+
+ /**
+ * The Logic of testing getAndDecrement
+ *
+ * @param simpleAtomicLong
+ * The SimpleAtomicLong to be tested
+ * @param preValue
+ * The expected 'pre' value
+ * @param resultValue
+ * The expected 'result' value
+ * @param postValue
+ * The expected 'post' value
+ */
+ public void getAndDecrementTestLogic(SimpleAtomicLong simpleAtomicLong,
+ long preValue, long resultValue, long postValue) {
+ long pre = simpleAtomicLong.get();
+ long result = simpleAtomicLong.getAndDecrement();
+ long post = simpleAtomicLong.get();
+ assertEquals(pre, result);
+ assertEquals(pre - 1, post);
+ compareResults(pre,
+ preValue,
+ result,
+ resultValue,
+ post,
+ postValue);
+ }
+
+ /**
+ * The Logic of testing incrementAndGet
+ *
+ * @param simpleAtomicLong
+ * The SimpleAtomicLong to be tested
+ * @param preValue
+ * The expected 'pre' value
+ * @param resultValue
+ * The expected 'result' value
+ * @param postValue
+ * The expected 'post' value
+ */
+ public void incrementAndGetTestLogic(SimpleAtomicLong simpleAtomicLong,
+ long preValue, long resultValue, long postValue) {
+ long pre = simpleAtomicLong.get();
+ long result = simpleAtomicLong.incrementAndGet();
+ long post = simpleAtomicLong.get();
+ assertEquals(pre + 1, result);
+ assertEquals(result, post);
+ compareResults(pre,
+ preValue,
+ result,
+ resultValue,
+ post,
+ postValue);
+ }
+
+ /**
+ * The Logic of testing getAndIncrement
+ *
+ * @param simpleAtomicLong
+ * The SimpleAtomicLong to be tested
+ * @param preValue
+ * The expected 'pre' value
+ * @param resultValue
+ * The expected 'result' value
+ * @param postValue
+ * The expected 'post' value
+ */
+ public void getAndIncrementTestLogic(SimpleAtomicLong simpleAtomicLong,
+ long preValue, long resultValue, long postValue) {
+ long pre = simpleAtomicLong.get();
+ long result = simpleAtomicLong.getAndIncrement();
+ long post = simpleAtomicLong.get();
+ assertEquals(pre, result);
+ assertEquals(pre + 1, post);
+ compareResults(pre,
+ preValue,
+ result,
+ resultValue,
+ post, postValue);
+ }
+}
diff --git a/grading-drivers/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLongTests.java b/grading-drivers/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLongTests.java
new file mode 100644
index 000000000..990496762
--- /dev/null
+++ b/grading-drivers/week-2-assignment-1/src/edu/vuum/mocca/SimpleAtomicLongTests.java
@@ -0,0 +1,18 @@
+package edu.vuum.mocca;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+
+
+@RunWith(Suite.class)
+@SuiteClasses({ SimpleAtomicLongMultithreadedTest.class,
+ SimpleAtomicLongSingleThreadedTest.class })
+/**
+ * @class SimpleAtomicLongTest
+ *
+ * @brief Entry point for running all the regression tests for the
+ * SimpleAtomicLong implementation.
+ */
+public class SimpleAtomicLongTests {
+}
diff --git a/grading-drivers/week-3-assignment-2/.classpath b/grading-drivers/week-3-assignment-2/.classpath
new file mode 100644
index 000000000..b0e3db7ba
--- /dev/null
+++ b/grading-drivers/week-3-assignment-2/.classpath
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/grading-drivers/week-3-assignment-2/.project b/grading-drivers/week-3-assignment-2/.project
new file mode 100644
index 000000000..80621e3a8
--- /dev/null
+++ b/grading-drivers/week-3-assignment-2/.project
@@ -0,0 +1,17 @@
+
+
+ W3-A2-PalantirManager-Grading
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/grading-drivers/week-3-assignment-2/Assessment-Description.txt b/grading-drivers/week-3-assignment-2/Assessment-Description.txt
new file mode 100644
index 000000000..6cf4b3a2a
--- /dev/null
+++ b/grading-drivers/week-3-assignment-2/Assessment-Description.txt
@@ -0,0 +1,56 @@
+Week 3: Programming Assignment 2 Peer Assessment Description
+
+Due Monday, June 16th, 2014
+
+You will be asked to evaluate five other student's projects as well as
+do a self assessment of your own project. If you do not assess the
+work of others, then you will receive a 20% penalty for your solution.
+
+As you do your evaluation, please keep an open mind and focus on the
+positive. Our goal is not to find every way to deduct points over
+small deviations from the requirements or for legitimate differences
+in implementation styles, etc. Look for ways to give points when it's
+clear that the submitter has given a good faith effort to do the
+project, and when it's likely that they've succeeded.
+
+Review the submitter's source code. Since you did your own project,
+you know where the changes should be and you have a good idea of the
+kind of method calls and work flows that should be in the submitter's
+code. Remember that they may have done things differently than you
+did. That's fine.
+
+Next, get a copy of the grading driver files from
+
+https://github.com/douglascraigschmidt/POSA-14/tree/master/grading-drivers/week-3-assignment-2
+
+and copy the submitter's SimpleAtomicLong.java and
+SimpleSemaphore.java files into your IDE (which may have been given a
+random, odd name by Coursera, so you'll need to rename it) and store
+it in the src/edu/vuum/mocca/ directory. I recommend scanning the
+submitted code to ensure there are no changes other than adding the
+appropriate code as indicated by the "TODO" comments. A quick way to
+check for this automatically is to run "diff" on the original files
+and the submitted files to make sure all the changes are as expected,
+in the expected places in the code. Then make sure everything compiles
+properly in your environment. Note that the grading drivers are in a
+different location than the original skeletons to ensure that all
+submissions are tested against the "master" regression tests and
+minimize the amount of code that comes from "the wild".
+
+Once you've reviewed the code against the rubric described below
+please try to run it, walking through the steps called out in the
+grading video that we'll create in each week's "VIrtual Office Hours"
+(see item #38 in the POSA MOOC FAQ at
+http://www.coursera.org/course/posa for info on Virtual Office
+Hours). I suggest you make a fresh Eclipse workspace when doing this,
+so that the submitter's code is isolated and easily distinguishable
+from yours. One way you can do that is to select File > Open Workspace
+> Browse > New Folder. This will lead to a new, empty Eclipse
+workspace. Then you can copy that submitter's SimpleAtomicLong.java
+and SimpleSemaphore.java files into this new workspace. Also, it's
+recommended to run the submitted code a virtual machine sandbox to
+further isolate it from causing harm.
+
+And again, remember that most everyone is working very hard and
+putting in a serious effort. When in doubt, err on the side of giving
+too many points, rather than giving too few.
diff --git a/grading-drivers/week-3-assignment-2/src/edu/vuum/mocca/SimpleAtomicLong.java b/grading-drivers/week-3-assignment-2/src/edu/vuum/mocca/SimpleAtomicLong.java
new file mode 100644
index 000000000..f57064f95
--- /dev/null
+++ b/grading-drivers/week-3-assignment-2/src/edu/vuum/mocca/SimpleAtomicLong.java
@@ -0,0 +1,80 @@
+// Import the necessary Java synchronization and scheduling classes.
+
+package edu.vuum.mocca;
+
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.concurrent.locks.Lock;
+
+/**
+ * @class SimpleAtomicLong
+ *
+ * @brief This class implements a subset of the
+ * java.util.concurrent.atomic.SimpleAtomicLong class using a
+ * ReentrantReadWriteLock to illustrate how they work.
+ */
+class SimpleAtomicLong
+{
+ /**
+ * The value that's manipulated atomically via the methods.
+ */
+ private long mValue;
+
+
+ /**
+ * The ReentrantReadWriteLock used to serialize access to mValue.
+ */
+ // TODO - add the implementation
+
+ /**
+ * Creates a new SimpleAtomicLong with the given initial value.
+ */
+ public SimpleAtomicLong(long initialValue) {
+ // TODO - you fill in here
+ }
+
+ /**
+ * @brief Gets the current value
+ *
+ * @returns The current value
+ */
+ public long get() {
+ // TODO - you fill in here
+ }
+
+ /**
+ * @brief Atomically decrements by one the current value
+ *
+ * @returns the updated value
+ */
+ public long decrementAndGet() {
+ // TODO - you fill in here
+ }
+
+ /**
+ * @brief Atomically increments by one the current value
+ *
+ * @returns the previous value
+ */
+ public long getAndIncrement() {
+ // TODO - you fill in here
+ }
+
+ /**
+ * @brief Atomically decrements by one the current value
+ *
+ * @returns the previous value
+ */
+ public long getAndDecrement() {
+ // TODO - you fill in here
+ }
+
+ /**
+ * @brief Atomically increments by one the current value
+ *
+ * @returns the updated value
+ */
+ public long incrementAndGet() {
+ // TODO - you fill in here
+ }
+}
+
diff --git a/grading-drivers/week-3-assignment-2/src/edu/vuum/mocca/SimpleSemaphore.java b/grading-drivers/week-3-assignment-2/src/edu/vuum/mocca/SimpleSemaphore.java
new file mode 100644
index 000000000..581f2eb99
--- /dev/null
+++ b/grading-drivers/week-3-assignment-2/src/edu/vuum/mocca/SimpleSemaphore.java
@@ -0,0 +1,69 @@
+package edu.vuum.mocca;
+
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.Condition;
+
+/**
+ * @class SimpleSemaphore
+ *
+ * @brief This class provides a simple counting semaphore implementation using
+ * Java a ReentrantLock and a ConditionObject. It must implement both
+ * "Fair" and "NonFair" semaphore semantics, just liked Java Semaphores.
+ */
+public class SimpleSemaphore {
+ /**
+ * Define a ReentrantLock to protect the critical section.
+ */
+ // TODO - you fill in here
+
+ /**
+ * Define a ConditionObject to wait while the number of
+ * permits is 0.
+ */
+ // TODO - you fill in here
+
+ /**
+ * Define a count of the number of available permits.
+ */
+ // TODO - you fill in here. Make sure that this data member will
+ // ensure its values aren't cached by multiple Threads..
+
+ public SimpleSemaphore(int permits, boolean fair) {
+ // TODO - you fill in here to initialize the SimpleSemaphore,
+ // making sure to allow both fair and non-fair Semaphore
+ // semantics.
+ }
+
+ /**
+ * Acquire one permit from the semaphore in a manner that can be
+ * interrupted.
+ */
+ public void acquire() throws InterruptedException {
+ // TODO - you fill in here.
+ }
+
+ /**
+ * Acquire one permit from the semaphore in a manner that cannot be
+ * interrupted.
+ */
+ public void acquireUninterruptibly() {
+ // TODO - you fill in here.
+ }
+
+ /**
+ * Return one permit to the semaphore.
+ */
+ void release() {
+ // TODO - you fill in here.
+ }
+
+ /**
+ * Return the number of permits available.
+ */
+ public int availablePermits() {
+ // TODO - you fill in here by changing null to the appropriate
+ // return value.
+ return null;
+ }
+}
diff --git a/grading-drivers/week-3-assignment-2/test/edu/vuum/mocca/AllTests.java b/grading-drivers/week-3-assignment-2/test/edu/vuum/mocca/AllTests.java
new file mode 100644
index 000000000..325a23f41
--- /dev/null
+++ b/grading-drivers/week-3-assignment-2/test/edu/vuum/mocca/AllTests.java
@@ -0,0 +1,12 @@
+package edu.vuum.mocca;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+
+@RunWith(Suite.class)
+@SuiteClasses({PalantirManagerUnitTest.class,
+ SimpleAtomicLongUnitTest.class,
+ SimpleSemaphoreUnitTest.class})
+public class AllTests {
+}
diff --git a/grading-drivers/week-3-assignment-2/test/edu/vuum/mocca/FairnessChecker.java b/grading-drivers/week-3-assignment-2/test/edu/vuum/mocca/FairnessChecker.java
new file mode 100644
index 000000000..2d8e1ff6a
--- /dev/null
+++ b/grading-drivers/week-3-assignment-2/test/edu/vuum/mocca/FairnessChecker.java
@@ -0,0 +1,47 @@
+package edu.vuum.mocca;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * @class FairnessChecker
+ *
+ * @brief Class that attempts to check whether the SimpleSemaphore
+ * implementation is "fair". It has inherent limitations, but
+ * to fix these limitations would require obtrusive
+ * instrumentation within the SimpleSemaphore implementation
+ * itself.
+ */
+public class FairnessChecker {
+ /**
+ * List of the waiting Threads, which are stored in FIFO order to
+ * see if the SimpleSemaphore implementation is "fair".
+ */
+ private List mEntryList;
+
+ /**
+ * Initialize the FairnessChecker
+ */
+ public FairnessChecker(final int totalEntries) {
+ mEntryList = new ArrayList(totalEntries);
+ }
+
+ /**
+ * Add the name of a Thread that's about to acquire the @code
+ * SimpleSemaphore. Assumes that Thread name are unique.
+ */
+ public synchronized void addNewThread(final String entry) {
+ mEntryList.add(entry);
+ }
+
+ /**
+ * Returns true if the calling Thread's name is the same as the
+ * first item in the list, else false.
+ */
+ public synchronized boolean checkOrder(final String entry) {
+ String currentEntry = mEntryList.remove(0);
+
+ return currentEntry.equals(entry);
+ }
+}
+
diff --git a/grading-drivers/week-3-assignment-2/test/edu/vuum/mocca/Palantir.java b/grading-drivers/week-3-assignment-2/test/edu/vuum/mocca/Palantir.java
new file mode 100644
index 000000000..1ecd8d2bd
--- /dev/null
+++ b/grading-drivers/week-3-assignment-2/test/edu/vuum/mocca/Palantir.java
@@ -0,0 +1,21 @@
+package edu.vuum.mocca;
+/**
+ * @class Palantir
+ *
+ * @brief Provides an interface for gazing into a Palantir.
+ * Essentially plays the role of a "command" in the Command
+ * pattern.
+ */
+
+interface Palantir {
+ /**
+ * Gaze into the Palantir (and go into a tranc ;-)).
+ */
+ public void gaze();
+
+ /**
+ * Return the name of the Palantir.
+ */
+ public String name();
+}
+
diff --git a/grading-drivers/week-3-assignment-2/test/edu/vuum/mocca/PalantirManager.java b/grading-drivers/week-3-assignment-2/test/edu/vuum/mocca/PalantirManager.java
new file mode 100644
index 000000000..dedf2454a
--- /dev/null
+++ b/grading-drivers/week-3-assignment-2/test/edu/vuum/mocca/PalantirManager.java
@@ -0,0 +1,100 @@
+package edu.vuum.mocca;
+
+import java.util.List;
+
+/**
+ * @class PalantirManager
+ *
+ * @brief Uses a "fair" Semaphore to control access to the
+ * available Palantiri. Implements the "Pooling" pattern
+ * in POSA3.
+ */
+public class PalantirManager {
+ /**
+ * Max number of Palantiri available.
+ */
+ private int mMaxPalantiri = 0;
+
+ /**
+ * Simple implementation of a Semaphore that can be configured
+ * to use the "fair" policy.
+ */
+ private SimpleSemaphore mAvailable = null;
+
+ /**
+ * List of the available Palantiri.
+ */
+ protected List mPalantiri = null;
+
+ /**
+ * Keeps track of the Palantiri that are available for use.
+ * The indices in this array mirror the list of mPanatiri.
+ */
+ protected boolean[] used = null;
+
+ /**
+ * Create a resource manager for the palantiri passed as a
+ * parameter.
+ */
+ PalantirManager(final List palantiri) {
+ mMaxPalantiri = palantiri.size();
+ mPalantiri = palantiri;
+ used = new boolean[palantiri.size()];
+
+ /**
+ * Use the "fair" policy.
+ */
+ mAvailable = new SimpleSemaphore(mMaxPalantiri, true);
+ }
+
+ /**
+ * Get the next available Palantir from the resource pool,
+ * blocking until one is available.
+ */
+ public Palantir acquirePalantir() {
+ mAvailable.acquireUninterruptibly();
+ return getNextAvailablePalantir();
+ }
+
+ /**
+ * Returns the designated @code palantir so that it's
+ * available for others to use.
+ */
+ public void releasePalantir(final Palantir palantir) {
+ if (markAsUnused(palantir))
+ mAvailable.release();
+ }
+
+ /**
+ * Get the next available Palantir from the resource pool.
+ */
+ protected synchronized Palantir getNextAvailablePalantir() {
+ // Linear search is fine for this simple demo.
+ for (int i = 0; i < mMaxPalantiri; ++i) {
+ if (!used[i]) {
+ used[i] = true;
+ return mPalantiri.get(i);
+ }
+ }
+ // Not reached unless something really weird happens..
+ return null;
+ }
+
+ /**
+ * Return the @code palantir back to the resource pool.
+ */
+ protected synchronized boolean markAsUnused(final Palantir palantir) {
+ // Linear search is fine for this simple demo.
+ for (int i = 0; i < mMaxPalantiri; ++i) {
+ if (palantir == mPalantiri.get(i)) {
+ if (used[i]) {
+ used[i] = false;
+ return true;
+ } else
+ return false;
+ }
+ }
+ return false;
+ }
+}
+
diff --git a/grading-drivers/week-3-assignment-2/test/edu/vuum/mocca/PalantirManagerUnitTest.java b/grading-drivers/week-3-assignment-2/test/edu/vuum/mocca/PalantirManagerUnitTest.java
new file mode 100644
index 000000000..90edc858f
--- /dev/null
+++ b/grading-drivers/week-3-assignment-2/test/edu/vuum/mocca/PalantirManagerUnitTest.java
@@ -0,0 +1,271 @@
+package edu.vuum.mocca;
+
+import static org.junit.Assert.fail;
+import static org.junit.Assert.assertFalse;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.junit.Test;
+
+/**
+ * @class PalantirManagerUnitTest
+ *
+ * @brief This program tests that student implementations of
+ * SimpleAtomicLong and SimpleSemaphore correctly implement a
+ * resource manager that limits the number of Beings from
+ * Middle-Earth who can concurrently gaze into a Palantir (see
+ * http://en.wikipedia.org/wiki/Palantir for more information
+ * on Palantirs if you're not a Lord of the Ring's fan yet
+ * ;-)).
+ */
+public class PalantirManagerUnitTest {
+ /**
+ * If this is set to true in then lots of debugging output will be
+ * generated.
+ */
+ public static boolean diagnosticsEnabled = false;
+
+ /**
+ * Total number of times each Palantir user gets to gaze into a
+ * Palantir.
+ */
+ final static int mMaxPalantirSessions = 5;
+
+ /**
+ * Total number of active Threads.
+ */
+ static volatile long mMaxActiveThreads = 0;
+
+ /**
+ * Keep track of whether a runtime exception occurs.
+ */
+ boolean mFailed = false;
+
+ /**
+ * Count of the number of Active Threads.
+ */
+ static SimpleAtomicLong mActiveThreads = new SimpleAtomicLong(0);
+
+ /**
+ * Resource Manager that controls access to the available
+ * Palantiri.
+ */
+ static PalantirManager mPalantirManager = null;
+
+ /**
+ * Object that attempts to check whether the Semaphore
+ * implementation is "fair".
+ */
+ static FairnessChecker mFairnessChecker = null;
+
+ /**
+ * Runnable passed to each Thread that uses a Palantir.
+ */
+ static Runnable usePalantir = new Runnable() {
+ /**
+ * This is the main loop run by each Being of Middle-Earth
+ * who wants to gaze into a Palantir.
+ */
+ public void run() {
+ // Bound the total number of times that each user can
+ // gaze into a Palantir.
+ for (int i = 0; i < mMaxPalantirSessions; ++i) {
+ if (diagnosticsEnabled)
+ System.out.println(Thread.currentThread().getName()
+ + " is acquiring the palantir");
+
+ // Used to check for Semaphore fairness.
+ mFairnessChecker.addNewThread(Thread.currentThread().getName());
+
+ // Get access to a Palantir, which will block if
+ // all the available Palantiri are in use.
+ Palantir palantir = mPalantirManager.acquirePalantir();
+
+ // There's a race condition here since it's
+ // possible for one thread to call
+ // mFairnessChecker.addNewThread() and then yield
+ // to another thread which again calls
+ // mFairnessChecker.addNewThread() and then goes
+ // on without interruption to call
+ // mPalantirManager.acquirePalantir(), which will
+ // fool the fairness checker into wrongly thinking
+ // the acquisition wasn't fair. we'll just give a
+ // warning (rather than an error) if it looks like
+ // the semaphore acquire() method isn't "fair".
+ if (!mFairnessChecker.checkOrder(Thread.currentThread()
+ .getName())) {
+ if (diagnosticsEnabled)
+ System.out.println(Thread.currentThread().getName()
+ + ": warning, semaphore acquire may not be fair");
+ }
+
+ // Ensure that the Semaphore implementation is
+ // correctly limiting the number of Palantir
+ // gazers.
+ long activeThreads = mActiveThreads.getAndIncrement();
+ if (mMaxActiveThreads < activeThreads) {
+ System.out.println("too many threads = "
+ + activeThreads);
+ throw new RuntimeException();
+ }
+ if (diagnosticsEnabled)
+ System.out.println(Thread.currentThread().getName()
+ + " is starting to gaze at the "
+ + palantir.name()
+ + " palantir");
+
+ // Gaze at the Palantir for the time alloted in
+ // the command.
+ palantir.gaze();
+
+ // Indicate this Being is no longer using the
+ // Palantir.
+ mActiveThreads.decrementAndGet();
+
+ if (diagnosticsEnabled)
+ System.out.println(Thread.currentThread().getName()
+ + " is finished gazing at the "
+ + palantir.name()
+ + " palantir");
+
+ // Return the Palantir back to the shared pool so
+ // other Beings can gaze at it.
+ mPalantirManager.releasePalantir(palantir);
+
+ if (diagnosticsEnabled)
+ System.out.println(Thread.currentThread().getName()
+ + " has released the "
+ + palantir.name()
+ + " palantir");
+ }
+
+ }
+ };
+
+ /**
+ * This factory creates a list of Palantiri.
+ */
+ static List makePalantiri() {
+ List palantiri = new ArrayList();
+
+ // MinasTirith Palantir
+ palantiri.add(new Palantir() {
+ public void gaze() {
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException e) {
+ }
+ }
+ public String name() {
+ return "MinasTirith";
+ }
+ });
+ // Orthanc Palantir
+ palantiri.add(new Palantir() {
+ public void gaze() {
+ try {
+ Thread.sleep(150);
+ } catch (InterruptedException e) {
+ }
+ }
+
+ public String name() {
+ return "Orthanc";
+ }
+ });
+ // Barad-dur Palantir
+ palantiri.add(new Palantir() {
+ public void gaze() {
+ try {
+ // The unblinking eye gazes for a long time
+ // ;-)
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ }
+ }
+ public String name() {
+ return "Barad-dur";
+ }
+ });
+
+ return palantiri;
+ }
+
+ @Test
+ public void testPalantirManager() {
+ try {
+ if (diagnosticsEnabled)
+ System.out.println("Starting PalantirManagerTest");
+
+ // Get the list of available Palantiri.
+ List palantiri =
+ PalantirManagerUnitTest.makePalantiri();
+
+ // Limit the number of users (threads) that can gaze into
+ // the available Palantiri.
+ mMaxActiveThreads = palantiri.size();
+
+ // Create a resource manager that control access to the
+ // available Palantiri.
+ mPalantirManager = new PalantirManager(palantiri);
+
+ // Create a list of Middle-Earth Beings who want to use
+ // the Palantir.
+ List palantirUsers = new ArrayList();
+ palantirUsers.add(new Thread(usePalantir, "Pippen"));
+ palantirUsers.add(new Thread(usePalantir, "Aragorn"));
+ palantirUsers.add(new Thread(usePalantir, "Denathor"));
+ palantirUsers.add(new Thread(usePalantir, "Sauron"));
+ palantirUsers.add(new Thread(usePalantir, "Saruman"));
+
+ // Create an object that attempts to check whether the
+ // Semaphore implementation is "fair".
+ mFairnessChecker = new FairnessChecker(palantirUsers.size());
+
+ // Start all the Threads that Middle-Earth Beings use to
+ // gaze into the Palantir.
+ for (ListIterator iterator = palantirUsers.listIterator();
+ iterator.hasNext();
+ ) {
+ Thread t = iterator.next();
+ // Catch runtime exceptions and induce a JUnit test
+ // failure.
+ t.setUncaughtExceptionHandler
+ (new Thread.UncaughtExceptionHandler() {
+ public void uncaughtException(Thread t,
+ Throwable e) {
+ System.out.println(t
+ + " throws exception: "
+ + e);
+ mFailed = true;
+ }
+ });
+ t.start();
+ }
+
+ // Barrier synchronization that waits for all the Threads
+ // to exit.
+ for (ListIterator iterator = palantirUsers.listIterator();
+ iterator.hasNext();
+ )
+ iterator.next().join();
+
+ // Make sure we haven't failed.
+ assertFalse(mFailed);
+
+ if (diagnosticsEnabled)
+ System.out.println("Finishing PalantirManagerTest");
+ } catch (Exception e) {
+ if (diagnosticsEnabled)
+ System.out.println("A "
+ + e.getMessage()
+ + " Exception was thrown");
+ fail("A "
+ + e.getMessage()
+ + " Exception was thrown");
+ }
+ }
+
+}
diff --git a/grading-drivers/week-3-assignment-2/test/edu/vuum/mocca/SimpleAtomicLongUnitTest.java b/grading-drivers/week-3-assignment-2/test/edu/vuum/mocca/SimpleAtomicLongUnitTest.java
new file mode 100644
index 000000000..69895b8ac
--- /dev/null
+++ b/grading-drivers/week-3-assignment-2/test/edu/vuum/mocca/SimpleAtomicLongUnitTest.java
@@ -0,0 +1,93 @@
+package edu.vuum.mocca;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Test;
+
+/**
+ * @class SimpleAtomicLongUnitTest
+ *
+ * @brief Simple unit test for the SimpleAtomicLong clas that ensures
+ * the version submitted for this assignment works correctly.
+ */
+public class SimpleAtomicLongUnitTest {
+ @Test
+ public void testSimpleAtomicLong() {
+ SimpleAtomicLong testLong = new SimpleAtomicLong(0);
+ assertNotNull(testLong);
+ }
+
+ @Test
+ public void testGet() {
+ SimpleAtomicLong testLong = new SimpleAtomicLong(0);
+ assertEquals(testLong.get(), 0);
+
+ SimpleAtomicLong testLong2 = new SimpleAtomicLong(100);
+ assertEquals(testLong2.get(), 100);
+
+ SimpleAtomicLong testLong3 = new SimpleAtomicLong(-100);
+ assertEquals(testLong3.get(), -100);
+ }
+
+ @Test
+ public void testDecrementAndGet() {
+ SimpleAtomicLong testLong = new SimpleAtomicLong(0);
+ assertEquals(testLong.decrementAndGet(), -1);
+ assertEquals(testLong.get(), -1);
+
+ SimpleAtomicLong testLong2 = new SimpleAtomicLong(100);
+ assertEquals(testLong2.decrementAndGet(), 99);
+ assertEquals(testLong2.get(), 99);
+
+ SimpleAtomicLong testLong3 = new SimpleAtomicLong(-100);
+ assertEquals(testLong3.decrementAndGet(), -101);
+ assertEquals(testLong3.get(), -101);
+ }
+
+ @Test
+ public void testIncrementAndGet() {
+ SimpleAtomicLong testLong = new SimpleAtomicLong(0);
+ assertEquals(testLong.incrementAndGet(), 1);
+ assertEquals(testLong.get(), 1);
+
+ SimpleAtomicLong testLong2 = new SimpleAtomicLong(100);
+ assertEquals(testLong2.incrementAndGet(), 101);
+ assertEquals(testLong2.get(), 101);
+
+ SimpleAtomicLong testLong3 = new SimpleAtomicLong(-100);
+ assertEquals(testLong3.incrementAndGet(), -99);
+ assertEquals(testLong3.get(), -99);
+ }
+
+ @Test
+ public void testGetAndIncrement() {
+ SimpleAtomicLong testLong = new SimpleAtomicLong(0);
+ assertEquals(testLong.getAndIncrement(), 0);
+ assertEquals(testLong.get(), 1);
+
+ SimpleAtomicLong testLong2 = new SimpleAtomicLong(100);
+ assertEquals(testLong2.getAndIncrement(), 100);
+ assertEquals(testLong2.get(), 101);
+
+ SimpleAtomicLong testLong3 = new SimpleAtomicLong(-100);
+ assertEquals(testLong3.getAndIncrement(), -100);
+ assertEquals(testLong3.get(), -99);
+ }
+
+ @Test
+ public void testGetAndDecrement() {
+ SimpleAtomicLong testLong = new SimpleAtomicLong(0);
+ assertEquals(testLong.getAndDecrement(), 0);
+ assertEquals(testLong.get(), -1);
+
+ SimpleAtomicLong testLong2 = new SimpleAtomicLong(100);
+ assertEquals(testLong2.getAndDecrement(), 100);
+ assertEquals(testLong2.get(), 99);
+
+ SimpleAtomicLong testLong3 = new SimpleAtomicLong(-100);
+ assertEquals(testLong3.getAndDecrement(), -100);
+ assertEquals(testLong3.get(), -101);
+ }
+
+}
diff --git a/grading-drivers/week-3-assignment-2/test/edu/vuum/mocca/SimpleSemaphoreUnitTest.java b/grading-drivers/week-3-assignment-2/test/edu/vuum/mocca/SimpleSemaphoreUnitTest.java
new file mode 100644
index 000000000..8cdd59390
--- /dev/null
+++ b/grading-drivers/week-3-assignment-2/test/edu/vuum/mocca/SimpleSemaphoreUnitTest.java
@@ -0,0 +1,68 @@
+package edu.vuum.mocca;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.concurrent.Semaphore;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * @class SimpleSemaphoreUnitTest
+ *
+ * @brief Simple unit test for the SimpleSemaphore that just tests
+ * single-threaded logic.
+ */
+public class SimpleSemaphoreUnitTest {
+ @Test
+ public void testSimpleSemaphore() {
+ SimpleSemaphore simpleSemaphore = new SimpleSemaphore(2, true);
+ assertNotNull(simpleSemaphore);
+ }
+
+ @Test
+ public void testAcquire() throws InterruptedException {
+ SimpleSemaphore simpleSemaphore = new SimpleSemaphore(2, true);
+ assertEquals(simpleSemaphore.availablePermits(), 2);
+ simpleSemaphore.acquire();
+ assertEquals(simpleSemaphore.availablePermits(), 1);
+ simpleSemaphore.acquire();
+ assertEquals(simpleSemaphore.availablePermits(), 0);
+ }
+
+ @Test
+ public void testAcquireUninterruptibly() throws InterruptedException {
+ SimpleSemaphore simpleSemaphore = new SimpleSemaphore(2, true);
+ assertEquals(simpleSemaphore.availablePermits(), 2);
+ simpleSemaphore.acquireUninterruptibly();
+ assertEquals(simpleSemaphore.availablePermits(), 1);
+ simpleSemaphore.acquireUninterruptibly();
+ assertEquals(simpleSemaphore.availablePermits(), 0);
+ }
+
+ @Test
+ public void testRelease() throws InterruptedException {
+ SimpleSemaphore simpleSemaphore = new SimpleSemaphore(2, true);
+ assertEquals(simpleSemaphore.availablePermits(), 2);
+ simpleSemaphore.acquire();
+ assertEquals(simpleSemaphore.availablePermits(), 1);
+ simpleSemaphore.acquire();
+ assertEquals(simpleSemaphore.availablePermits(), 0);
+ simpleSemaphore.release();
+ assertEquals(simpleSemaphore.availablePermits(), 1);
+ simpleSemaphore.release();
+ assertEquals(simpleSemaphore.availablePermits(), 2);
+ }
+
+ @Test
+ public void testAvailablePermits() throws InterruptedException{
+ SimpleSemaphore simpleSemaphore = new SimpleSemaphore(2, true);
+ assertEquals(simpleSemaphore.availablePermits(), 2);
+ simpleSemaphore.acquire();
+ assertEquals(simpleSemaphore.availablePermits(), 1);
+ }
+}
diff --git a/grading-drivers/week-4-assignment-3/.classpath b/grading-drivers/week-4-assignment-3/.classpath
new file mode 100644
index 000000000..b0e3db7ba
--- /dev/null
+++ b/grading-drivers/week-4-assignment-3/.classpath
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/ex/PingPong/console/.project b/grading-drivers/week-4-assignment-3/.project
similarity index 89%
rename from ex/PingPong/console/.project
rename to grading-drivers/week-4-assignment-3/.project
index 4a66e81b0..b8a07bc7e 100644
--- a/ex/PingPong/console/.project
+++ b/grading-drivers/week-4-assignment-3/.project
@@ -1,6 +1,6 @@
- PingPongConsole
+ W4-A3-PingPongRight-Grading
diff --git a/grading-drivers/week-4-assignment-3/Assessment-Description.txt b/grading-drivers/week-4-assignment-3/Assessment-Description.txt
new file mode 100644
index 000000000..8ef6a7912
--- /dev/null
+++ b/grading-drivers/week-4-assignment-3/Assessment-Description.txt
@@ -0,0 +1,56 @@
+Week 4: Programming Assignment 3 Peer Assessment Description
+
+Due Monday, June 23rd, 2014
+
+You will be asked to evaluate five other student's projects as well as
+do a self assessment of your own project. If you do not assess the
+work of others, then you will receive a 20% penalty for your solution.
+
+As you do your evaluation, please keep an open mind and focus on the
+positive. Our goal is not to find every way to deduct points over
+small deviations from the requirements or for legitimate differences
+in implementation styles, etc. Look for ways to give points when it's
+clear that the submitter has given a good faith effort to do the
+project, and when it's likely that they've succeeded.
+
+Review the submitter's source code. Since you did your own project,
+you know where the changes should be and you have a good idea of the
+kind of method calls and work flows that should be in the submitter's
+code. Remember that they may have done things differently than you
+did. That's fine.
+
+Next, get a copy of the grading driver files from
+
+https://github.com/douglascraigschmidt/POSA-14/tree/master/grading-drivers/week-4-assignment-3
+
+and copy the submitter's SimpleSemaphore.java and PingPongRight.java
+files into your IDE (which may have been given a random, odd name by
+Coursera, so you'll need to rename it) and store it in the
+src/edu/vuum/mocca/ directory. I recommend scanning the submitted
+code to ensure there are no changes other than adding the appropriate
+code as indicated by the "TODO" comments. A quick way to check for
+this automatically is to run "diff" on the original files and the
+submitted files to make sure all the changes are as expected, in the
+expected places in the code. Then make sure everything compiles
+properly in your environment. Note that the grading drivers are in a
+different location than the original skeletons to ensure that all
+submissions are tested against the "master" regression tests and
+minimize the amount of code that comes from "the wild".
+
+Once you've reviewed the code against the rubric described below
+please try to run it, walking through the steps called out in the
+grading video that we'll create in each week's "VIrtual Office Hours"
+(see item #38 in the POSA MOOC FAQ at
+http://www.coursera.org/course/posa for info on Virtual Office
+Hours). I suggest you make a fresh Eclipse workspace when doing this,
+so that the submitter's code is isolated and easily distinguishable
+from yours. One way you can do that is to select File > Open Workspace
+> Browse > New Folder. This will lead to a new, empty Eclipse
+workspace. Then you can copy that submitter's SimpleSemaphore.java and
+PingPongRight.java files into this new workspace. Also, it's
+recommended to run the submitted code a virtual machine sandbox to
+further isolate it from causing harm.
+
+And again, remember that most everyone is working very hard and
+putting in a serious effort. When in doubt, err on the side of giving
+too many points, rather than giving too few.
diff --git a/grading-drivers/week-4-assignment-3/src/edu/vuum/mocca/PingPongRight.java b/grading-drivers/week-4-assignment-3/src/edu/vuum/mocca/PingPongRight.java
new file mode 100644
index 000000000..b2845e4e2
--- /dev/null
+++ b/grading-drivers/week-4-assignment-3/src/edu/vuum/mocca/PingPongRight.java
@@ -0,0 +1,157 @@
+package edu.vuum.mocca;
+
+// Import the necessary Java synchronization and scheduling classes.
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * @class PingPongRight
+ *
+ * @brief This class implements a Java program that creates two
+ * instances of the PlayPingPongThread and start these thread
+ * instances to correctly alternate printing "Ping" and "Pong",
+ * respectively, on the console display.
+ */
+public class PingPongRight {
+ /**
+ * Number of iterations to run the test program.
+ */
+ public final static int mMaxIterations = 10;
+
+ /**
+ * Latch that will be decremented each time a thread exits.
+ */
+ public static CountDownLatch mLatch = null;
+
+ /**
+ * @class PlayPingPongThread
+ *
+ * @brief This class implements the ping/pong processing algorithm
+ * using the SimpleSemaphore to alternate printing "ping"
+ * and "pong" to the console display.
+ */
+ public static class PlayPingPongThread extends Thread {
+
+ /**
+ * Constants to distinguish between ping and pong
+ * SimpleSemaphores, if you choose to use an array of
+ * SimpleSemaphores. If you don't use this implementation
+ * feel free to remove these constants.
+ */
+ private final static int FIRST_SEMA = 0;
+ private final static int SECOND_SEMA = 1;
+
+ /**
+ * Maximum number of loop iterations.
+ */
+ private int mMaxLoopIterations = 0;
+
+ /**
+ * String to print (either "ping!" or "pong"!) for each
+ * iteration.
+ */
+ // TODO - You fill in here.
+
+ /**
+ * Two SimpleSemaphores use to alternate pings and pongs. You
+ * can use an array of SimpleSemaphores or just define them as
+ * two data members.
+ */
+ // TODO - You fill in here.
+
+ /**
+ * Constructor initializes the data member(s).
+ */
+ public PlayPingPongThread(String stringToPrint,
+ SimpleSemaphore semaphoreOne,
+ SimpleSemaphore semaphoreTwo,
+ int maxIterations) {
+ // TODO - You fill in here.
+ }
+
+ /**
+ * Main event loop that runs in a separate thread of control
+ * and performs the ping/pong algorithm using the
+ * SimpleSemaphores.
+ */
+ public void run() {
+ /**
+ * This method runs in a separate thread of control and
+ * implements the core ping/pong algorithm.
+ */
+
+ // TODO - You fill in here.
+ }
+
+ /**
+ * Method for acquiring the appropriate SimpleSemaphore.
+ */
+ private void acquire() {
+ // TODO fill in here
+ }
+
+ /**
+ * Method for releasing the appropriate SimpleSemaphore.
+ */
+ private void release() {
+ // TODO fill in here
+ }
+ }
+
+ /**
+ * The method that actually runs the ping/pong program.
+ */
+ public static void process(String startString,
+ String pingString,
+ String pongString,
+ String finishString,
+ int maxIterations) throws InterruptedException {
+
+ // TODO initialize this by replacing null with the appropriate
+ // constructor call.
+ mLatch = null;
+
+ // Create the ping and pong SimpleSemaphores that control
+ // alternation between threads.
+
+ // TODO - You fill in here, make pingSema start out unlocked.
+ SimpleSemaphore pingSema = null;
+ // TODO - You fill in here, make pongSema start out locked.
+ SimpleSemaphore pongSema = null;
+
+ System.out.println(startString);
+
+ // Create the ping and pong threads, passing in the string to
+ // print and the appropriate SimpleSemaphores.
+ PlayPingPongThread ping = new PlayPingPongThread(/*
+ * TODO - You fill in
+ * here
+ */);
+ PlayPingPongThread pong = new PlayPingPongThread(/*
+ * TODO - You fill in
+ * here
+ */);
+
+ // TODO - Initiate the ping and pong threads, which will call
+ // the run() hook method.
+
+ // TODO - replace the following line with a barrier
+ // synchronizer call to mLatch that waits for both threads to
+ // finish.
+ throw new java.lang.InterruptedException();
+
+ System.out.println(finishString);
+ }
+
+ /**
+ * The main() entry point method into PingPongRight program.
+ *
+ * @throws InterruptedException
+ */
+ public static void main(String[] args) throws InterruptedException {
+ process("Ready...Set...Go!",
+ "Ping! ",
+ " Pong! ",
+ "Done!",
+ mMaxIterations);
+ }
+}
diff --git a/grading-drivers/week-4-assignment-3/src/edu/vuum/mocca/SimpleSemaphore.java b/grading-drivers/week-4-assignment-3/src/edu/vuum/mocca/SimpleSemaphore.java
new file mode 100644
index 000000000..5e473adf6
--- /dev/null
+++ b/grading-drivers/week-4-assignment-3/src/edu/vuum/mocca/SimpleSemaphore.java
@@ -0,0 +1,66 @@
+package edu.vuum.mocca;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * @class SimpleSemaphore
+ *
+ * @brief This class provides a simple counting semaphore implementation using
+ * Java a ReentrantLock and a ConditionObject (which is accessed via a
+ * Condition). It must implement both "Fair" and "NonFair" semaphore
+ * semantics, just liked Java Semaphores.
+ */
+public class SimpleSemaphore {
+ /**
+ * Define a ReentrantLock to protect the critical section.
+ */
+ // TODO - you fill in here
+
+ /**
+ * Define a Condition that waits while the number of permits is 0.
+ */
+ // TODO - you fill in here
+
+ /**
+ * Define a count of the number of available permits.
+ */
+ // TODO - you fill in here. Make sure that this data member will
+ // ensure its values aren't cached by multiple Threads..
+
+ public SimpleSemaphore(int permits, boolean fair) {
+ // TODO - you fill in here to initialize the SimpleSemaphore,
+ // making sure to allow both fair and non-fair Semaphore
+ // semantics.
+ }
+
+ /**
+ * Acquire one permit from the semaphore in a manner that can be
+ * interrupted.
+ */
+ public void acquire() throws InterruptedException {
+ // TODO - you fill in here.
+ }
+
+ /**
+ * Acquire one permit from the semaphore in a manner that cannot be
+ * interrupted.
+ */
+ public void acquireUninterruptibly() {
+ // TODO - you fill in here.
+ }
+
+ /**
+ * Return one permit to the semaphore.
+ */
+ void release() {
+ // TODO - you fill in here.
+ }
+
+ /**
+ * Return the number of permits available.
+ */
+ public int availablePermits() {
+ // TODO - you fill in here to return the correct result
+ return 0;
+ }
+}
diff --git a/grading-drivers/week-4-assignment-3/test/edu/vuum/mocca/PingPongRightTest.java b/grading-drivers/week-4-assignment-3/test/edu/vuum/mocca/PingPongRightTest.java
new file mode 100644
index 000000000..5148fba59
--- /dev/null
+++ b/grading-drivers/week-4-assignment-3/test/edu/vuum/mocca/PingPongRightTest.java
@@ -0,0 +1,100 @@
+package edu.vuum.mocca;
+
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @class PingPongRightTest
+ *
+ * @brief This JUnit test checks the PingPong program to make sure it's working
+ * properly.
+ */
+public class PingPongRightTest {
+ /*
+ * These data members are used to capture [
+ * System.out.println() ] for testing.
+ */
+ private final ByteArrayOutputStream outContent =
+ new ByteArrayOutputStream();
+ private final ByteArrayOutputStream errContent =
+ new ByteArrayOutputStream();
+
+ /*
+ * Setup the output Capturing.
+ */
+ @Before
+ public void setUpStreams() {
+ System.setOut(new PrintStream(outContent));
+ System.setErr(new PrintStream(errContent));
+ }
+
+ /*
+ * Tear-down the output Capturing.
+ */
+ @After
+ public void cleanUpStreams() {
+ System.setOut(null);
+ System.setErr(null);
+ }
+
+ /*
+ * Test the process(...) logic/accuracy
+ *
+ * Gives some helpful error outputs for some simple error states.
+ */
+ @Test(timeout = 3000)
+ // This test will only run for 3000ms => 3 seconds, otherwise if fails
+ public void test() throws InterruptedException, IOException {
+ for (int i = 0; i < 10; i++) {
+ PingPongRight.process("start", "a", "b", "end", 10);
+ String outResults = outContent.toString();
+
+ if (outResults == null || outResults.length() == 0) {
+ fail("No output at all.");
+ }
+
+ //We need to split things up for Windows and UNIX due to
+ //differences in the way that newlines are handled.
+ boolean windowsTrue = outResults.equals(testResultWindows);
+ boolean unixTrue = outResults.equals(testResultUnix);
+ boolean pingAllFirstTrue = outResults.equals(pingAllFirst);
+ boolean pongAllFirstTrue = outResults.equals(pongAllFirst);
+
+ if (errContent.toString().length() != 0)
+ fail("There was error text.");
+
+ if (pingAllFirstTrue)
+ fail("Ping Thread completed before Pong started.");
+
+ if (pongAllFirstTrue)
+ fail("Pong Thread completed before Ping started.");
+
+ if (!(windowsTrue || unixTrue))
+ fail("Output was wrong.\n"
+ + "--- Received output ---\n"
+ + outResults
+ + "--- Expected output ---\n"
+ + testResultWindows);
+
+ outContent.reset();
+ }
+ }
+
+ // This is what should be output \n was replaced for visible
+ // endlines for inclusion into single line.
+ String testResultUnix =
+ "start\na(1)\nb(1)\na(2)\nb(2)\na(3)\nb(3)\na(4)\nb(4)\na(5)\nb(5)\na(6)\nb(6)\na(7)\nb(7)\na(8)\nb(8)\na(9)\nb(9)\na(10)\nb(10)\nend\n";
+ String testResultWindows =
+ "start\r\na(1)\r\nb(1)\r\na(2)\r\nb(2)\r\na(3)\r\nb(3)\r\na(4)\r\nb(4)\r\na(5)\r\nb(5)\r\na(6)\r\nb(6)\r\na(7)\r\nb(7)\r\na(8)\r\nb(8)\r\na(9)\r\nb(9)\r\na(10)\r\nb(10)\r\nend\r\n";
+ String pingAllFirst =
+ "start\r\na(1)\r\nb(1)\r\na(2)\r\na(3)\r\na(4)\r\na(5)\r\na(6)\r\na(7)\r\na(8)\r\na(9)\r\na(10)\r\nb(2)\r\nb(3)\r\nb(4)\r\nb(5)\r\nb(6)\r\nb(8)\r\nb(7)\r\nb(9)\r\nb(10)\r\nend\r\n";
+ String pongAllFirst =
+ "start\r\nb(1)\r\nb(2)\r\nb(3)\r\nb(4)\r\nb(5)\r\nb(6)\r\nb(8)\r\nb(7)\r\nb(9)\r\nb(10)\r\na(1)\r\na(2)\r\na(3)\r\na(4)\r\na(5)\r\na(6)\r\na(7)\r\na(8)\r\na(9)\r\na(10)\r\nend\r\n";
+}
diff --git a/grading-drivers/week-5-assignment-4/Assessment-Description.txt b/grading-drivers/week-5-assignment-4/Assessment-Description.txt
new file mode 100644
index 000000000..d201fbebb
--- /dev/null
+++ b/grading-drivers/week-5-assignment-4/Assessment-Description.txt
@@ -0,0 +1,58 @@
+Week 5: Programming Assignment 4 Peer Assessment Description
+
+Due Monday, June 30th, 2014
+
+Steps for Peer Assessors
+
+You will be asked to evaluate five other student's projects as well as
+do a self assessment of your own project. If you do not assess the
+work of others, then you will receive a 20% penalty for your solution.
+
+As you do your evaluation, please keep an open mind and focus on the
+positive. Our goal is not to find every way to deduct points over
+small deviations from the requirements or for legitimate differences
+in implementation styles, etc. Look for ways to give points when it's
+clear that the submitter has given a good faith effort to do the
+project, and when it's likely that they've succeeded.
+
+Review the submitter's source code. Since you did your own project,
+you know where the changes should be and you have a good idea of the
+kind of method calls and work flows that should be in the submitter's
+code. Remember that they may have done things differently than you
+did. That's fine.
+
+Next, get a copy of the grading driver files from
+
+https://github.com/douglascraigschmidt/POSA-14/tree/master/grading-drivers/week-5-assignment-4
+
+and copy the submitter's AndroidPlatformStrategy.java file into your
+IDE (which may have been given a random, odd name by Coursera, so
+you'll need to rename it) and store it in the src/edu/vuum/mocca/
+directory. I recommend scanning the submitted code to ensure there
+are no changes other than adding the appropriate code as indicated by
+the "TODO" comments. A quick way to check for this automatically is to
+run "diff" on the original files and the submitted files to make sure
+all the changes are as expected, in the expected places in the
+code. Then make sure everything compiles properly in your
+environment. Note that the grading drivers are in a different location
+than the original skeletons to ensure that all submissions are tested
+against the "master" regression tests and minimize the amount of code
+that comes from "the wild".
+
+Once you've reviewed the code against the rubric described below
+please try to run it, walking through the steps called out in the
+grading video that we'll create in each week's "VIrtual Office Hours"
+(see item #38 in the POSA MOOC FAQ at
+http://www.coursera.org/course/posa for info on Virtual Office
+Hours). I suggest you make a fresh Eclipse workspace when doing this,
+so that the submitter's code is isolated and easily distinguishable
+from yours. One way you can do that is to select File > Open Workspace
+> Browse > New Folder. This will lead to a new, empty Eclipse
+workspace. Then you can copy that submitter's
+AndroidPlatformStrategy.java file into this new workspace. Also, it's
+recommended to run the submitted code in the Android AVD emulator to
+further isolate it from causing harm.
+
+And again, remember that most everyone is working very hard and
+putting in a serious effort. When in doubt, err on the side of giving
+too many points, rather than giving too few.
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/.classpath b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/.classpath
new file mode 100644
index 000000000..e6dd6025b
--- /dev/null
+++ b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/.classpath
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/.project b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/.project
new file mode 100644
index 000000000..6a111f53c
--- /dev/null
+++ b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/.project
@@ -0,0 +1,33 @@
+
+
+ W5-A4-Android-Test-Grading
+
+
+
+
+
+ com.android.ide.eclipse.adt.ResourceManagerBuilder
+
+
+
+
+ com.android.ide.eclipse.adt.PreCompilerBuilder
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ com.android.ide.eclipse.adt.ApkBuilder
+
+
+
+
+
+ com.android.ide.eclipse.adt.AndroidNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/AndroidManifest.xml b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/AndroidManifest.xml
new file mode 100644
index 000000000..a226ce2e0
--- /dev/null
+++ b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/AndroidManifest.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/libs/android-support-v4.jar b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/libs/android-support-v4.jar
new file mode 100644
index 000000000..9056828a0
Binary files /dev/null and b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/libs/android-support-v4.jar differ
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/libs/robotium-solo-5.1.jar b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/libs/robotium-solo-5.1.jar
new file mode 100644
index 000000000..1931dd97f
Binary files /dev/null and b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/libs/robotium-solo-5.1.jar differ
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/proguard-project.txt b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/proguard-project.txt
new file mode 100644
index 000000000..f2fe1559a
--- /dev/null
+++ b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/project.properties b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/project.properties
new file mode 100644
index 000000000..a3ee5ab64
--- /dev/null
+++ b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/project.properties
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-17
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/res/drawable-hdpi/ic_launcher.png b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 000000000..96a442e5b
Binary files /dev/null and b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/res/drawable-hdpi/ic_launcher.png differ
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/res/drawable-mdpi/ic_launcher.png b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 000000000..359047dfa
Binary files /dev/null and b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/res/drawable-mdpi/ic_launcher.png differ
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/res/drawable-xhdpi/ic_launcher.png b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..71c6d760f
Binary files /dev/null and b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/res/values-v11/styles.xml b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/res/values-v11/styles.xml
new file mode 100644
index 000000000..3c02242ad
--- /dev/null
+++ b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/res/values-v11/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/res/values-v14/styles.xml b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/res/values-v14/styles.xml
new file mode 100644
index 000000000..a91fd0372
--- /dev/null
+++ b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/res/values-v14/styles.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/res/values/strings.xml b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/res/values/strings.xml
new file mode 100644
index 000000000..1711cecf9
--- /dev/null
+++ b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+
+ W5-A4-Android-Test
+
+
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/res/values/styles.xml b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/res/values/styles.xml
new file mode 100644
index 000000000..6ce89c7ba
--- /dev/null
+++ b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/res/values/styles.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/src/edu/vuum/mocca/test/PingPongActivityTest.java b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/src/edu/vuum/mocca/test/PingPongActivityTest.java
new file mode 100644
index 000000000..dcab37447
--- /dev/null
+++ b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/src/edu/vuum/mocca/test/PingPongActivityTest.java
@@ -0,0 +1,90 @@
+package edu.vuum.mocca.test;
+
+import android.content.Context;
+import android.test.ActivityInstrumentationTestCase2;
+import android.widget.Button;
+import android.widget.TextView;
+import android.view.WindowManager;
+
+import com.robotium.solo.Solo;
+
+import edu.vuum.mocca.PingPongActivity;
+
+/**
+ * @class PingPongActivityTest
+ *
+ * @brief Implements a Robotium test for the PingPongActivity.
+ */
+public class PingPongActivityTest
+ extends ActivityInstrumentationTestCase2 {
+ /**
+ * Constructor initializes the superclass.
+ */
+ public PingPongActivityTest() {
+ super(PingPongActivity.class);
+ }
+
+ /**
+ * This is the handle for Robotium, which allows us to interact
+ * with the UI.
+ */
+ Solo mSolo;
+
+ /**
+ * The context of this project, not the target project.
+ */
+ Context mContext;
+
+ Button playButton_;
+ TextView outputTextView_;
+
+ /**
+ * Called before each test is run to perform the initialization.
+ */
+ public void setUp() throws Exception {
+ super.setUp();
+
+ // Setup Robotium and get the EditText View.
+ mSolo = new Solo(getInstrumentation(), getActivity());
+
+ mContext = getInstrumentation().getContext();
+
+ playButton_ = (Button) mSolo.getView(edu.vuum.mocca.R.id.play_button);
+
+ outputTextView_ = (TextView) mSolo
+ .getView(edu.vuum.mocca.R.id.pingpong_output);
+
+ // Prevent lockscreen from preventing test.
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+ }
+ });
+
+ getInstrumentation().callActivityOnStart(getActivity());
+ getInstrumentation().callActivityOnResume(getActivity());
+ }
+
+ /**
+ * No need for @Test because of use of
+ * ActivityInstrumentationTestCase2 which makes each method in
+ * this class a @Test method TODO find better explanation, 100%
+ * proper.
+ */
+ public void testPlayPingPongButtonPress() throws Exception {
+ Thread.sleep(TestOptions.ACCEPTABLE_STARTUP_LENGTH);
+
+ // Asserttrue(outputTextView_.isAttachedToWindow() == true);
+ assertTrue(outputTextView_.getText().length() == 0);
+
+ // Click on the 'play' button
+ mSolo.clickOnView(mSolo.getView(edu.vuum.mocca.R.id.play_button));
+
+ // wait for the threads to execute
+ Thread.sleep(TestOptions.ACCEPTABLE_RUNTIME_LENGTH);
+
+ assertTrue(outputTextView_.getText().toString()
+ .equals(TestOptions.ANDROID_TEXTVIEW));
+ }
+}
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/src/edu/vuum/mocca/test/TestOptions.java b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/src/edu/vuum/mocca/test/TestOptions.java
new file mode 100644
index 000000000..fb1fb4c55
--- /dev/null
+++ b/grading-drivers/week-5-assignment-4/W5-A4-Android-Test/src/edu/vuum/mocca/test/TestOptions.java
@@ -0,0 +1,38 @@
+package edu.vuum.mocca.test;
+
+/**
+ * @class Options
+ *
+ * @brief Holds global constants for the testing suite. More convenient than
+ * grabbing resources from /res and trying to finagle a working Context
+ * out of the test classes every time we want to use TEST_URI.
+ *
+ */
+public class TestOptions {
+ /**
+ * Time we should wait for things to run.
+ */
+ static final long ACCEPTABLE_STARTUP_LENGTH = 1000;
+
+ /**
+ * Time we should wait for things to run.
+ */
+ static final long ACCEPTABLE_RUNTIME_LENGTH = 2000;
+
+ /**
+ * Time we should wait for things to download.
+ */
+ static final long LONG_WAIT_TIME = 5000;
+
+ /**
+ * Various test strings.
+ */
+ static final String JAVA_CONSOLE_UNIX = "Ready...Set...Go!\nping (1)\n_pong (1)\nping (2)\n_pong (2)\nping (3)\n_pong (3)\nping (4)\n_pong (4)\nping (5)\n_pong (5)\nping (6)\n_pong (6)\nping (7)\n_pong (7)\nping (8)\n_pong (8)\nping (9)\n_pong (9)\nping (10)\n_pong (10)\nDone!\n";
+ static final String PING_ALL_FIRST_UNIX = "Ready...Set...Go!\nping (1)\nping (2)\nping (3)\nping (4)\nping (5)\nping (6)\nping (7)\nping (8)\nping (9)\nping (10)\n_pong (1)\n_pong (2)\n_pong (3)\n_pong (4)\n_pong (5)\n_pong (6)\n_pong (7)\n_pong (8)\n_pong (9)\n_pong (10)\nDone!\n";
+ static final String PING_ALL_FIRST_WINDOWS = "Ready...Set...Go!\r\nping (1)\r\nping (2)\r\nping (3)\r\nping (4)\r\nping (5)\r\nping (6)\r\nping (7)\r\nping (8)\r\nping (9)\r\nping (10)\r\n_pong (1)\r\n_pong (2)\r\n_pong (3)\r\n_pong (4)\r\n_pong (5)\r\n_pong (6)\r\n_pong (7)\r\n_pong (8)\r\n_pong (9)\r\n_pong (10)\r\nDone!\r\n";
+ static final String PONG_ALL_FIRST_WINDOWS = "Ready...Set...Go!\r\n_pong (1)\r\n_pong (2)\r\n_pong (3)\r\n_pong (4)\r\n_pong (5)\r\n_pong (6)\r\n_pong (7)\r\n_pong (8)\r\n_pong (9)\r\n_pong (10)\r\nping (1)\r\nping (2)\r\nping (3)\r\nping (4)\r\nping (5)\r\nping (6)\r\nping (7)\r\nping (8)\r\nping (9)\r\nping (10)\r\nDone!";
+ static final String PONG_ALL_FIRST_UNIX = "Ready...Set...Go!\n_pong (1)\n_pong (2)\n_pong (3)\n_pong (4)\n_pong (5)\n_pong (6)\n_pong (7)\n_pong (8)\n_pong (9)\n_pong (10)\nping (1)\nping (2)\nping (3)\nping (4)\nping (5)\nping (6)\nping (7)\nping (8)\nping (9)\nping (10)\nDone!";
+ static final String JAVA_CONSOLE_WINDOWS = "Ready...Set...Go!\r\nping (1)\r\n_pong (1)\r\nping (2)\r\n_pong (2)\r\nping (3)\r\n_pong (3)\r\nping (4)\r\n_pong (4)\r\nping (5)\r\n_pong (5)\r\nping (6)\r\n_pong (6)\r\nping (7)\r\n_pong (7)\r\nping (8)\r\n_pong (8)\r\nping (9)\r\n_pong (9)\r\nping (10)\r\n_pong (10)\r\nDone!";
+ static final String ANDROID_TEXTVIEW = "Ready...Set...Go!\nping (1)\n_pong (1)\nping (2)\n_pong (2)\nping (3)\n_pong (3)\nping (4)\n_pong (4)\nping (5)\n_pong (5)\nping (6)\n_pong (6)\nping (7)\n_pong (7)\nping (8)\n_pong (8)\nping (9)\n_pong (9)\nping (10)\n_pong (10)\nDone!\n";
+
+}
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android/.classpath b/grading-drivers/week-5-assignment-4/W5-A4-Android/.classpath
new file mode 100644
index 000000000..51769745b
--- /dev/null
+++ b/grading-drivers/week-5-assignment-4/W5-A4-Android/.classpath
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android/.project b/grading-drivers/week-5-assignment-4/W5-A4-Android/.project
new file mode 100644
index 000000000..f6f278734
--- /dev/null
+++ b/grading-drivers/week-5-assignment-4/W5-A4-Android/.project
@@ -0,0 +1,33 @@
+
+
+ W5-A4-Android-Grading
+
+
+
+
+
+ com.android.ide.eclipse.adt.ResourceManagerBuilder
+
+
+
+
+ com.android.ide.eclipse.adt.PreCompilerBuilder
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ com.android.ide.eclipse.adt.ApkBuilder
+
+
+
+
+
+ com.android.ide.eclipse.adt.AndroidNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android/AndroidManifest.xml b/grading-drivers/week-5-assignment-4/W5-A4-Android/AndroidManifest.xml
new file mode 100644
index 000000000..949fedabf
--- /dev/null
+++ b/grading-drivers/week-5-assignment-4/W5-A4-Android/AndroidManifest.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android/libs/android-support-v4.jar b/grading-drivers/week-5-assignment-4/W5-A4-Android/libs/android-support-v4.jar
new file mode 100644
index 000000000..9056828a0
Binary files /dev/null and b/grading-drivers/week-5-assignment-4/W5-A4-Android/libs/android-support-v4.jar differ
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android/proguard-project.txt b/grading-drivers/week-5-assignment-4/W5-A4-Android/proguard-project.txt
new file mode 100644
index 000000000..f2fe1559a
--- /dev/null
+++ b/grading-drivers/week-5-assignment-4/W5-A4-Android/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android/project.properties b/grading-drivers/week-5-assignment-4/W5-A4-Android/project.properties
new file mode 100644
index 000000000..a3ee5ab64
--- /dev/null
+++ b/grading-drivers/week-5-assignment-4/W5-A4-Android/project.properties
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-17
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android/res/drawable-hdpi/ic_launcher.png b/grading-drivers/week-5-assignment-4/W5-A4-Android/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 000000000..96a442e5b
Binary files /dev/null and b/grading-drivers/week-5-assignment-4/W5-A4-Android/res/drawable-hdpi/ic_launcher.png differ
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android/res/drawable-mdpi/ic_launcher.png b/grading-drivers/week-5-assignment-4/W5-A4-Android/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 000000000..359047dfa
Binary files /dev/null and b/grading-drivers/week-5-assignment-4/W5-A4-Android/res/drawable-mdpi/ic_launcher.png differ
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android/res/drawable-xhdpi/ic_launcher.png b/grading-drivers/week-5-assignment-4/W5-A4-Android/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..71c6d760f
Binary files /dev/null and b/grading-drivers/week-5-assignment-4/W5-A4-Android/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android/res/layout/activity_ping_pong.xml b/grading-drivers/week-5-assignment-4/W5-A4-Android/res/layout/activity_ping_pong.xml
new file mode 100644
index 000000000..9461f2941
--- /dev/null
+++ b/grading-drivers/week-5-assignment-4/W5-A4-Android/res/layout/activity_ping_pong.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android/res/values-v11/styles.xml b/grading-drivers/week-5-assignment-4/W5-A4-Android/res/values-v11/styles.xml
new file mode 100644
index 000000000..3c02242ad
--- /dev/null
+++ b/grading-drivers/week-5-assignment-4/W5-A4-Android/res/values-v11/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android/res/values-v14/styles.xml b/grading-drivers/week-5-assignment-4/W5-A4-Android/res/values-v14/styles.xml
new file mode 100644
index 000000000..a91fd0372
--- /dev/null
+++ b/grading-drivers/week-5-assignment-4/W5-A4-Android/res/values-v14/styles.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android/res/values/dimens.xml b/grading-drivers/week-5-assignment-4/W5-A4-Android/res/values/dimens.xml
new file mode 100644
index 000000000..55c1e5908
--- /dev/null
+++ b/grading-drivers/week-5-assignment-4/W5-A4-Android/res/values/dimens.xml
@@ -0,0 +1,7 @@
+
+
+
+ 16dp
+ 16dp
+
+
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android/res/values/strings.xml b/grading-drivers/week-5-assignment-4/W5-A4-Android/res/values/strings.xml
new file mode 100644
index 000000000..cd5196b9a
--- /dev/null
+++ b/grading-drivers/week-5-assignment-4/W5-A4-Android/res/values/strings.xml
@@ -0,0 +1,9 @@
+
+
+ PlayPingPong
+ Settings
+ Play PingPong!
+ Reset Game
+
+
+
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android/res/values/styles.xml b/grading-drivers/week-5-assignment-4/W5-A4-Android/res/values/styles.xml
new file mode 100644
index 000000000..6ce89c7ba
--- /dev/null
+++ b/grading-drivers/week-5-assignment-4/W5-A4-Android/res/values/styles.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/AndroidPlatformStrategy.java b/grading-drivers/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/AndroidPlatformStrategy.java
new file mode 100644
index 000000000..bc684b5ce
--- /dev/null
+++ b/grading-drivers/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/AndroidPlatformStrategy.java
@@ -0,0 +1,82 @@
+package edu.vuum.mocca;
+
+import java.lang.ref.WeakReference;
+import java.util.concurrent.CountDownLatch;
+
+import android.app.Activity;
+import android.widget.TextView;
+import android.util.Log;
+
+/**
+ * @class AndroidPlatformStrategy
+ *
+ * @brief Provides methods that define a platform-independent API for
+ * output data to Android UI thread and synchronizing on thread
+ * completion in the ping/pong game. It plays the role of the
+ * "Concrete Strategy" in the Strategy pattern.
+ */
+public class AndroidPlatformStrategy extends PlatformStrategy
+{
+ /** TextViewVariable. */
+ private TextView mTextViewOutput;
+
+ /** Activity variable finds gui widgets by view. */
+ private WeakReference mActivity;
+
+ public AndroidPlatformStrategy(Object output,
+ final Object activityParam)
+ {
+ /**
+ * A textview output which displays calculations and
+ * expression trees.
+ */
+ mTextViewOutput = (TextView) output;
+
+ /** The current activity window (succinct or verbose). */
+ mActivity = new WeakReference((Activity) activityParam);
+ }
+
+ /**
+ * Latch to decrement each time a thread exits to control when the
+ * play() method returns.
+ */
+ private static CountDownLatch mLatch = null;
+
+ /** Do any initialization needed to start a new game. */
+ public void begin()
+ {
+ /** (Re)initialize the CountDownLatch. */
+ // TODO - You fill in here.
+ }
+
+ /** Print the outputString to the display. */
+ public void print(final String outputString)
+ {
+ /**
+ * Create a Runnable that's posted to the UI looper thread
+ * and appends the outputString to a TextView.
+ */
+ // TODO - You fill in here.
+ }
+
+ /** Indicate that a game thread has finished running. */
+ public void done()
+ {
+ // TODO - You fill in here.
+ }
+
+ /** Barrier that waits for all the game threads to finish. */
+ public void awaitDone()
+ {
+ // TODO - You fill in here.
+ }
+
+ /**
+ * Error log formats the message and displays it for the
+ * debugging purposes.
+ */
+ public void errorLog(String javaFile, String errorMessage)
+ {
+ Log.e(javaFile, errorMessage);
+ }
+}
diff --git a/ex/PingPong/console/src/edu/vuum/mocca/ConsolePlatformStrategy.java b/grading-drivers/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/ConsolePlatformStrategy.java
similarity index 78%
rename from ex/PingPong/console/src/edu/vuum/mocca/ConsolePlatformStrategy.java
rename to grading-drivers/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/ConsolePlatformStrategy.java
index c67f981ca..c83233f16 100644
--- a/ex/PingPong/console/src/edu/vuum/mocca/ConsolePlatformStrategy.java
+++ b/grading-drivers/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/ConsolePlatformStrategy.java
@@ -17,13 +17,13 @@ public class ConsolePlatformStrategy extends PlatformStrategy
* Latch to decrement each time a thread exits to control when the
* play() method returns.
*/
- private static CountDownLatch mLatch = new CountDownLatch(2);
+ private static CountDownLatch mLatch = null;
/** Contains information for outputting to console window. */
PrintStream mOutput;
/** Ctor. */
- ConsolePlatformStrategy(Object output)
+ public ConsolePlatformStrategy(Object output)
{
mOutput = (PrintStream) output;
}
@@ -31,14 +31,14 @@ public class ConsolePlatformStrategy extends PlatformStrategy
/** Do any initialization needed to start a new game. */
public void begin()
{
- mLatch = new CountDownLatch(2);
+ mLatch = new CountDownLatch(NUMBER_OF_THREADS);
}
/** Print the outputString to the display. */
public void print(String outputString)
{
/** Print to the console window. */
- System.out.println(outputString);
+ mOutput.println(outputString);
}
/** Indicate that a game thread has finished running. */
@@ -56,18 +56,13 @@ public void awaitDone()
}
}
- /** Returns a string revealing the platform in use. */
- public String platformName()
- {
- return System.getProperty("java.specification.vendor");
- }
-
/**
* Error log formats the message and displays it for the debugging
* purposes.
*/
public void errorLog(String javaFile, String errorMessage)
{
- System.out.println(javaFile + " " + errorMessage);
+ mOutput.println(javaFile + " " + errorMessage);
}
}
+
diff --git a/assignments/week-5-assignment-4/src/edu/vuum/mocca/Main.java b/grading-drivers/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/Main.java
similarity index 100%
rename from assignments/week-5-assignment-4/src/edu/vuum/mocca/Main.java
rename to grading-drivers/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/Main.java
diff --git a/ex/PingPong/console/src/edu/vuum/mocca/Options.java b/grading-drivers/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/Options.java
similarity index 79%
rename from ex/PingPong/console/src/edu/vuum/mocca/Options.java
rename to grading-drivers/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/Options.java
index 11d85015f..f07a88350 100644
--- a/ex/PingPong/console/src/edu/vuum/mocca/Options.java
+++ b/grading-drivers/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/Options.java
@@ -1,9 +1,5 @@
package edu.vuum.mocca;
-import java.io.File;
-import java.nio.*;
-import java.util.*;
-
/**
* @class Options
*
@@ -62,18 +58,20 @@ public String syncMechanism()
*/
public boolean parseArgs(String argv[])
{
- for (int argc = 0; argc < argv.length; argc += 2)
- if (argv[argc].equals("-i"))
- mMaxIterations = Integer.parseInt(argv[argc + 1]);
- else if (argv[argc].equals("-s"))
- mSyncMechanism = argv[argc + 1];
- else if (argv[argc].equals("-t"))
- mMaxTurns = Integer.parseInt(argv[argc + 1]);
- else
- {
- printUsage();
- return false;
- }
+ if (argv != null) {
+ for (int argc = 0; argc < argv.length; argc += 2)
+ if (argv[argc].equals("-i"))
+ mMaxIterations = Integer.parseInt(argv[argc + 1]);
+ else if (argv[argc].equals("-s"))
+ mSyncMechanism = argv[argc + 1];
+ else if (argv[argc].equals("-t"))
+ mMaxTurns = Integer.parseInt(argv[argc + 1]);
+ else
+ {
+ printUsage();
+ return false;
+ }
+ }
return true;
}
diff --git a/grading-drivers/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PingPongActivity.java b/grading-drivers/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PingPongActivity.java
new file mode 100644
index 000000000..3d283d399
--- /dev/null
+++ b/grading-drivers/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PingPongActivity.java
@@ -0,0 +1,76 @@
+package edu.vuum.mocca;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+/**
+ * @class MainActivity
+ *
+ * @brief Initial start up screen for the android GUI.
+ */
+public class PingPongActivity extends Activity {
+ /** TextView that PingPong will be "played" upon */
+ private TextView mAndroidPingPongOutput;
+
+ /** Button that allows playing and resetting of the game */
+ private Button mPlayButton;
+
+ /** Variables to track state of the game */
+ private static int PLAY = 0;
+ private static int RESET = 1;
+ private int mGameState = PLAY;
+
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Sets the content view to the xml file, activity_ping_pong.
+ setContentView(R.layout.activity_ping_pong);
+ mAndroidPingPongOutput =
+ (TextView) findViewById(R.id.pingpong_output);
+ mPlayButton = (Button) findViewById(R.id.play_button);
+
+ // Initializes the Platform singleton with the appropriate
+ // Platform strategy, which in this case will be the
+ // AndroidPlatform.
+ PlatformStrategy.instance
+ (new PlatformStrategyFactory
+ (mAndroidPingPongOutput,
+ this).makePlatformStrategy());
+
+ // Initializes the Options singleton.
+ Options.instance().parseArgs(null);
+ }
+
+ /** Sets the action of the button on click state. */
+ public void playButtonClicked(View view) {
+ if (mGameState == PLAY) {
+ // Use a factory method to create the appropriate type of
+ // OutputStrategy.
+ PlayPingPong pingPong =
+ new PlayPingPong(PlatformStrategy.instance(),
+ Options.instance().maxIterations(),
+ Options.instance().maxTurns(),
+ Options.instance().syncMechanism());
+
+ // Play ping-pong with the designated number of
+ // iterations.
+ new Thread(pingPong).start();
+ mPlayButton.setText(R.string.reset_button);
+ mGameState = RESET;
+ } else if (mGameState == RESET) {
+
+ // Empty TextView and prepare the UI to play another game.
+ mAndroidPingPongOutput.setText(R.string.empty_string);
+ mPlayButton.setText(R.string.play_button);
+ mGameState = PLAY;
+ } else {
+ // Notify the player that something has gone wrong and
+ // reset.
+ mAndroidPingPongOutput.setText("Unknown State entered!");
+ mGameState = RESET;
+ }
+ }
+}
diff --git a/ex/PingPong/console/src/edu/vuum/mocca/PlatformStrategy.java b/grading-drivers/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PlatformStrategy.java
similarity index 84%
rename from ex/PingPong/console/src/edu/vuum/mocca/PlatformStrategy.java
rename to grading-drivers/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PlatformStrategy.java
index 8524590ca..8ae0fadef 100644
--- a/ex/PingPong/console/src/edu/vuum/mocca/PlatformStrategy.java
+++ b/grading-drivers/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PlatformStrategy.java
@@ -12,6 +12,9 @@
*/
public abstract class PlatformStrategy
{
+ /** Number of threads used to play ping-pong. */
+ protected static final int NUMBER_OF_THREADS = 2;
+
/** The singleton @a PlatformStrategy instance. */
private static PlatformStrategy mUniqueInstance = null;
@@ -22,8 +25,8 @@ public static PlatformStrategy instance()
}
/**
- * Method that sets a new PlatformStrategy singleton and returns
- * the one and only singleton instance.
+ * Method that sets a new PlatformStrategy singleton and returns the one
+ * and only singleton instance.
*/
public static PlatformStrategy instance(PlatformStrategy platform)
{
@@ -42,11 +45,13 @@ public static PlatformStrategy instance(PlatformStrategy platform)
/** Barrier that waits for all the game threads to finish. */
public abstract void awaitDone();
- /**
+ /**
* Returns the name of the platform in a string. e.g., Android or
* a JVM.
+ * @deprecated This method is just here for backwards
+ * compatibility with the skeletons.
*/
- public abstract String platformName();
+ public String platformName() { return ""; }
/**
* Error log formats the message and displays it for the debugging
diff --git a/ex/PingPong/console/src/edu/vuum/mocca/PlatformStrategyFactory.java b/grading-drivers/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PlatformStrategyFactory.java
similarity index 52%
rename from ex/PingPong/console/src/edu/vuum/mocca/PlatformStrategyFactory.java
rename to grading-drivers/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PlatformStrategyFactory.java
index 4d5191a48..95d6bb962 100644
--- a/ex/PingPong/console/src/edu/vuum/mocca/PlatformStrategyFactory.java
+++ b/grading-drivers/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PlatformStrategyFactory.java
@@ -11,21 +11,29 @@
public class PlatformStrategyFactory
{
/**
- * This interface uses the Strategy pattern to create @a PlatformStrategy
- * implementations at runtime.
+ * This interface uses the Strategy pattern to create @a
+ * PlatformStrategy implementations at runtime.
*/
private static interface IPlatformStrategyFactoryStrategy
{
public PlatformStrategy execute();
}
+ /**
+ * Enumeration distinguishing platforms Android from plain ol' Java.
+ */
+ public enum PlatformType {
+ ANDROID,
+ PLAIN_JAVA
+ }
+
/**
* HashMap used to map strings containing the Java platform names
* and dispatch the execute() method of the associated @a PlatformStrategy
* implementation.
*/
- private HashMap mPlatformStrategyMap =
- new HashMap();
+ private HashMap mPlatformStrategyMap =
+ new HashMap();
/**
* Ctor that stores the objects that perform output for a
@@ -36,23 +44,29 @@ public PlatformStrategyFactory(final Object output,
final Object activity)
{
/**
- * The "Sun Microsystems Inc." string maps to a command object
- * that creates an @a ConsolePlatformStrategy implementation.
+ * The "The Android Project" string maps to a command object
+ * that creates an @a AndroidPlatformStrategy implementation.
*/
- mPlatformStrategyMap.put("Sun Microsystems Inc.",
+ mPlatformStrategyMap.put(PlatformType.ANDROID,
new IPlatformStrategyFactoryStrategy()
{
+ /**
+ * Receives the three parameters, input
+ * (EditText), output (TextView), activity
+ * (activity).
+ */
public PlatformStrategy execute()
{
- return new ConsolePlatformStrategy(output);
+ return new AndroidPlatformStrategy(output,
+ activity);
}
});
-
+
/**
- * The "Oracle Corporation" string maps to a command object
+ * The "Sun Microsystems Inc." string maps to a command object
* that creates an @a ConsolePlatformStrategy implementation.
*/
- mPlatformStrategyMap.put("Oracle Corporation",
+ mPlatformStrategyMap.put(PlatformType.PLAIN_JAVA,
new IPlatformStrategyFactoryStrategy()
{
public PlatformStrategy execute()
@@ -62,14 +76,34 @@ public PlatformStrategy execute()
});
}
+ /**
+ * Returns the name of the platform in a string. e.g., Android or
+ * a JVM.
+ */
+ public static String platformName()
+ {
+ return System.getProperty("java.specification.vendor");
+ }
+
+ /**
+ * Returns the type of the platformm e.g. Android or
+ * a JVM.
+ */
+ public static PlatformType platformType() {
+ if(platformName().indexOf("Android") >= 0)
+ return PlatformType.ANDROID;
+ else
+ return PlatformType.PLAIN_JAVA;
+ }
+
/**
* Create a new @a PlatformStrategy object based on underlying Java
* platform.
*/
public PlatformStrategy makePlatformStrategy()
{
- String name = System.getProperty("java.specification.vendor");
+ PlatformType type = platformType();
- return mPlatformStrategyMap.get(name).execute();
+ return mPlatformStrategyMap.get(type).execute();
}
}
diff --git a/assignments/week-5-assignment-4/src/edu/vuum/mocca/PlayPingPong.java b/grading-drivers/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PlayPingPong.java
similarity index 61%
rename from assignments/week-5-assignment-4/src/edu/vuum/mocca/PlayPingPong.java
rename to grading-drivers/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PlayPingPong.java
index a72d8246a..aa8cc4b4e 100644
--- a/assignments/week-5-assignment-4/src/edu/vuum/mocca/PlayPingPong.java
+++ b/grading-drivers/week-5-assignment-4/W5-A4-Android/src/edu/vuum/mocca/PlayPingPong.java
@@ -1,21 +1,18 @@
package edu.vuum.mocca;
+import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Semaphore;
/**
* @class PlayPingPong
- *
- * @brief This class implements a Java program that creates two
- * threads, Ping and Pong, to alternately print "Ping" and
- * "Pong", respectively, on the display. It uses the Template
- * Method, Strategy, and Factory Method patterns to factor out
- * common code and simplify the program design.
+ *
+ * @brief This class implements a Java program that creates two threads, Ping
+ * and Pong, to alternately print "Ping" and "Pong", respectively, on the
+ * display. It uses the Template Method, Strategy, and Factory Method
+ * patterns to factor out common code and simplify the program design.
*/
-public class PlayPingPong implements Runnable
-{
+public class PlayPingPong implements Runnable {
/**
* Number of iterations to ping/pong.
*/
@@ -25,44 +22,42 @@ public class PlayPingPong implements Runnable
private static int mMaxTurns = 1;
/**
- * Keeps track of the platform that we're running on, e.g.,
- * Android vs. Console.
+ * Keeps track of the platform that we're running on, e.g., Android vs.
+ * Console.
*/
private static volatile PlatformStrategy mPlatformStrategy;
/**
- * Which synchronization to use, e.g., "SEMA" vs. "COND".
- * Defaults to "SEMA".
+ * Which synchronization to use, e.g., "SEMA" vs. "COND". Defaults to
+ * "SEMA".
*/
private static String mSyncMechanism = "SEMA";
-
+
/**
- * Constants used to distinguish between ping and pong threads.
+ * Constants used to distinguish between ping and pong threads.
*/
private final static int PING_THREAD = 0;
private final static int PONG_THREAD = 1;
/**
* @Brief PingPongThread
- *
+ *
* @class This class implements the core ping/pong algorithm, but
- * defers the scheduling aspect to subclasses. It plays
- * the role of the "Abstract Class" in the Template Method
+ * defers the scheduling aspect to subclasses. It plays the
+ * role of the "Abstract Class" in the Template Method
* pattern.
*/
- static abstract class PingPongThread extends Thread
- {
- /**
+ static abstract class PingPongThread extends Thread {
+ /**
* Constructor initializes the various fields.
*/
- PingPongThread(String stringToPrint)
- {
+ PingPongThread(String stringToPrint) {
mStringToPrint = stringToPrint;
}
/**
- * Abstract hook methods that determine the ping/pong
- * scheduling protocol in the run() template method.
+ * Abstract hook methods that determine the ping/pong scheduling
+ * protocol in the run() template method.
*/
abstract void acquire();
abstract void release();
@@ -70,28 +65,25 @@ static abstract class PingPongThread extends Thread
/**
* Sets the id of the other thread.
*/
- void setOtherThreadId(long id) {}
-
+ void setOtherThreadId(long id) {
+ }
+
/**
* This method runs in a separate thread of control and
- * implements the core ping/pong algorithm. It plays the role
+ * implements the core ping/pong algorithm. It plays the role
* of the "template method" in the Template Method pattern.
*/
- public void run()
- {
- for (int loopsDone = 1;
- loopsDone <= mMaxIterations;
- ++loopsDone) {
+ public void run() {
+ for (int loopsDone = 1; loopsDone <= mMaxIterations; ++loopsDone) {
// Perform the template method protocol for printing a
- // "ping" or a "pong" on the display. Note that the
+ // "ping" or a "pong" on the display. Note that the
// acquire() and release() hook methods that control
// the scheduling of the threads are deferred to
// subclasses.
acquire();
- mPlatformStrategy.print
- (mStringToPrint + "(" + loopsDone + ")");
+ mPlatformStrategy.print(mStringToPrint + "(" + loopsDone + ")");
release();
}
@@ -109,14 +101,13 @@ public void run()
/**
* @class PingPongThreadSema
- *
- * @brief This class uses semaphores to implement the acquire()
- * and release() hook methods that schedule the ping/pong
- * algorithm. It plays the role of the "Concrete Class" in
- * the Template Method pattern.
+ *
+ * @brief This class uses semaphores to implement the acquire() and
+ * release() hook methods that schedule the ping/pong algorithm. It
+ * plays the role of the "Concrete Class" in the Template Method
+ * pattern.
*/
- static class PingPongThreadSema extends PingPongThread
- {
+ static class PingPongThreadSema extends PingPongThread {
/**
* Semaphores that schedule the ping/pong algorithm
*/
@@ -128,10 +119,8 @@ static class PingPongThreadSema extends PingPongThread
private final static int FIRST_SEMA = 0;
private final static int SECOND_SEMA = 1;
- PingPongThreadSema(String stringToPrint,
- Semaphore firstSema,
- Semaphore secondSema)
- {
+ PingPongThreadSema(String stringToPrint, Semaphore firstSema,
+ Semaphore secondSema) {
super(stringToPrint);
mSemas[FIRST_SEMA] = firstSema;
mSemas[SECOND_SEMA] = secondSema;
@@ -140,16 +129,14 @@ static class PingPongThreadSema extends PingPongThread
/**
* Hook method for ping/pong acquire.
*/
- void acquire()
- {
+ void acquire() {
mSemas[FIRST_SEMA].acquireUninterruptibly();
}
/**
* Hook method for ping/pong release.
*/
- void release()
- {
+ void release() {
mSemas[SECOND_SEMA].release();
}
@@ -163,10 +150,9 @@ void release()
* plays the role of the "Concrete Class" in the Template Method
* pattern.
*/
- static class PingPongThreadCond extends PingPongThread
- {
+ static class PingPongThreadCond extends PingPongThread {
/**
- * Conditions that schedule the ping/pong algorithm.
+ * Semaphores that schedule the ping/pong algorithm.
*/
private Condition mConds[] = new Condition[2];
@@ -179,7 +165,7 @@ static class PingPongThreadCond extends PingPongThread
* Number of times we've iterated thus far in our "turn".
*/
private int mIterationCount = 0;
-
+
/**
* Id for the other thread.
*/
@@ -189,9 +175,8 @@ static class PingPongThreadCond extends PingPongThread
* Thread whose turn it currently is.
*/
private static long mThreadOwner;
-
- public void setOtherThreadId(long otherThreadId)
- {
+
+ public void setOtherThreadId(long otherThreadId) {
this.mOtherThreadId = otherThreadId;
}
@@ -201,18 +186,14 @@ public void setOtherThreadId(long otherThreadId)
private final static int FIRST_COND = 0;
private final static int SECOND_COND = 1;
- PingPongThreadCond(String stringToPrint,
- ReentrantLock lock,
- Condition firstCond,
- Condition secondCond,
- boolean isOwner)
- {
+ PingPongThreadCond(String stringToPrint, ReentrantLock lock,
+ Condition firstCond, Condition secondCond, boolean isOwner) {
super(stringToPrint);
mIterationCount = mMaxTurns;
mLock = lock;
mConds[FIRST_COND] = firstCond;
mConds[SECOND_COND] = secondCond;
- if (isOwner)
+ if (isOwner)
mThreadOwner = this.getId();
}
@@ -247,14 +228,11 @@ void release() {
}
/**
- * Constructor stores the PlatformStrategy and the number of
- * iterations to play ping/pong.
+ * Constructor stores the PlatformStrategy and the number of iterations to
+ * play ping/pong.
*/
- public PlayPingPong (PlatformStrategy platformStrategy,
- int maxIterations,
- int maxTurns,
- String syncMechanism)
- {
+ public PlayPingPong(PlatformStrategy platformStrategy, int maxIterations,
+ int maxTurns, String syncMechanism) {
// The PlatformStrategy being used.
mPlatformStrategy = platformStrategy;
@@ -263,57 +241,62 @@ public PlayPingPong (PlatformStrategy platformStrategy,
// Number of iterations to perform pings and pongs per "turn".
mMaxTurns = maxTurns;
-
+
// Which synchronization to use (e.g., "SEMA" vs. "COND").
mSyncMechanism = syncMechanism;
}
- private void makePingPongThreads(String schedMechanism,
- PingPongThread[] pingPongThreads)
- {
+ static String pingString = "ping ";
+ static String pongString = "_pong ";
+
+ static boolean checkedStringFormatting = false;
+
+ private void formatStrings() {
+ if (!checkedStringFormatting) {
+ PlatformStrategyFactory.PlatformType type =
+ PlatformStrategyFactory.PlatformType.ANDROID;
+
+ if (PlatformStrategyFactory.platformType().equals(type))
+ pingString += " ";
+ checkedStringFormatting = true;
+ }
+ }
+
+ private void makePingPongThreads(String schedMechanism,
+ PingPongThread[] pingPongThreads) {
+ formatStrings();
if (schedMechanism.equals("SEMA")) {
// Create the semaphores that schedule threads
- // printing "ping" and "pong" in the correct
+ // printing "ping " and "_pong" in the correct
// alternating order.
Semaphore pingSema = new Semaphore(1); // Starts out unlocked.
Semaphore pongSema = new Semaphore(0);
-
- pingPongThreads[PING_THREAD] =
- new PingPongThreadSema("ping", pingSema, pongSema);
- pingPongThreads[PONG_THREAD] =
- new PingPongThreadSema("pong", pongSema, pingSema);
- }
- else if (schedMechanism.equals("COND")) {
+
+ pingPongThreads[PING_THREAD] = new PingPongThreadSema(pingString,
+ pingSema, pongSema);
+ pingPongThreads[PONG_THREAD] = new PingPongThreadSema(pongString,
+ pongSema, pingSema);
+ } else if (schedMechanism.equals("COND")) {
ReentrantLock lock = new ReentrantLock();
Condition pingCond = lock.newCondition();
Condition pongCond = lock.newCondition();
- int numberOfTurnsEach = 2;
-
- pingPongThreads[PING_THREAD] =
- new PingPongThreadCond("ping",
- lock,
- pingCond,
- pongCond,
- true);
- pingPongThreads[PONG_THREAD] =
- new PingPongThreadCond("pong",
- lock,
- pongCond,
- pingCond,
- false);
+
+ pingPongThreads[PING_THREAD] = new PingPongThreadCond(pingString,
+ lock, pingCond, pongCond, true);
+ pingPongThreads[PONG_THREAD] = new PingPongThreadCond(pongString,
+ lock, pongCond, pingCond, false);
pingPongThreads[PING_THREAD]
.setOtherThreadId(pingPongThreads[PONG_THREAD].getId());
pingPongThreads[PONG_THREAD]
.setOtherThreadId(pingPongThreads[PING_THREAD].getId());
}
}
-
+
/**
- * Start running the ping/pong code, which can be called from a
- * main() function in a Java class, an Android Activity, etc.
- */
- public void run()
- {
+ * Start running the ping/pong code, which can be called from a main()
+ * function in a Java class, an Android Activity, etc.
+ */
+ public void run() {
// Indicate a new game is beginning.
mPlatformStrategy.begin();
@@ -325,24 +308,22 @@ public void run()
pingPongThreads[PING_THREAD] = null;
pingPongThreads[PONG_THREAD] = null;
- /**
- * Create the appropriate type of threads with the designated
- * scheduling mechanism (e.g., "SEMA" for Semaphores, "COND"
- * for ConditionObjects, etc.).
+ /**
+ * Create the appropriate type of threads with the designated scheduling
+ * mechanism (e.g., "SEMA" for Semaphores, "COND" for ConditionObjects,
+ * etc.).
*/
- makePingPongThreads(mSyncMechanism,
- pingPongThreads);
+ makePingPongThreads(mSyncMechanism, pingPongThreads);
/**
- * Start ping and pong threads, which calls their run()
- * methods.
+ * Start ping and pong threads, which calls their run() methods.
*/
pingPongThreads[PING_THREAD].start();
pingPongThreads[PONG_THREAD].start();
/**
- * Barrier synchronization to wait for all work to be done
- * before exiting play().
+ * Barrier synchronization to wait for all work to be done before
+ * exiting play().
*/
mPlatformStrategy.awaitDone();
diff --git a/grading-drivers/week-6-assignment-5/Assignment-Description.txt b/grading-drivers/week-6-assignment-5/Assignment-Description.txt
new file mode 100644
index 000000000..b1752ceaf
--- /dev/null
+++ b/grading-drivers/week-6-assignment-5/Assignment-Description.txt
@@ -0,0 +1,114 @@
+Week 6: Programming Assignment 5
+
+Released Monday, June 16th, 2014
+Due Monday, June 30th, 2014
+
+In this assignment, you will implement a program that downloads images
+using started services and then displays them in the context of the UI
+Thread. The user can optionally select one of two different Started
+Services whose implementations you will complete:
+
+. A simple DownloadIntentService that uses on the Android
+ IntentService framework to download files in a single background
+ Thread and
+
+. A more scalable ThreadPoolDownloadService that uses a
+ ThreadPoolExecutor to download several files concurrently within a
+ fixed pool of Threads.
+
+In this directory you'll find Java source code, AndroidManifest.xml,
+and associated XML metadata files. The directory
+W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/ contains
+the skeleton Java code that you'll implement by completing the "TODO -
+You fill in here" comments to provide a working solution. DO NOT
+CHANGE THE OVERALL STRUCTURE OF THE SKELETON - just fill in the "TODO
+- You fill in here" portions!!!
+
+The main goal of this project is to understand the pattern-oriented
+threaded download application and how to start and interact with
+Services using the IntentService and Messenger IPC mechanisms. In
+particular, you'll need to do the following:
+
+. Understand how to launch Started Services using Intents and receive
+ replies from Services using the Messenger IPC mechanism in the
+ DownloadActivity class, which are covered in these videos:
+
+ Section 1: Module 3: Part 5: Sending and Handling Messages with Android Handler
+ Section 2: Module 1: Part 2: Programming Started Services (Part 1)
+ Section 2: Module 1: Part 3: Programming Started Services (Part 2)
+
+. Understand how to implement the DownloadIntentService and
+ ThreadPoolDownloadService by handling Intents, doing work in the
+ background, and sending results the Messenger IPC mechanism covered
+ in these videos:
+
+ Section 2: Module 1: Part 4: Android IntentService
+ Section 2: Module 1: Part 6: Service to Activity Communication via Android Messenger
+
+. Understand factory methods and implement the makeIntent() factory
+ methods in each service, which are similar to the
+ DownloadService.makeIntent() factory method covered in this video:
+
+ Section 2: Module 1: Part 3: Programming Started Services (Part 2)
+
+. Understand the ThreadPoolExecutor class and utilize a factory method
+ in the Executors class to create a new thread pool, which is
+ (briefly) outlined in this video
+
+ Section 1: Module 3: Part 7: The AsyncTask Framework (Part 2)
+
+ and described further in the documentation at:
+
+ http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html
+
+ There's also an example that shows how to use a Executor thread pool
+ available at
+
+ https://github.com/douglascraigschmidt/POSA-14/tree/master/ex/UniqueIDGeneratorApplication
+
+. Understand the methods provided in the DownloadUtils class, which
+ contains functionality that is common to both the
+ DownloadIntentService and ThreadPoolDownloadService.
+
+These videos are available at
+
+https://class.coursera.org/posa-002/lecture
+
+We'll also discuss this assignment specification (and later its
+solution) in the POSA MOOC "Virtual Office Hours", which are described
+in item #38 at the POSA MOOC FAQ available from
+
+http://www.courera.org/course/posa
+
+You'll need to build this Android application in the Eclipse ADT
+environment. Unlike previous assignments, this program is
+user-driven, so output will vary depending on which URL is provided.
+In addition to using the default URL (which is available from
+https://d396qusza40orc.cloudfront.net/posa/ka.png) you should also
+test this application by finding an Internet URL to an image (ending
+in .jpg, .gif, .png etc.) and see if your application correctly
+displays the image.
+
+We provide several JUnit tests that should test your program's
+functionality. The unit tests test the Service classes independently
+of the DownloadActivity, and then test the overall functionality of
+the program. By default, the unit tests try to download the default
+URL and then check if the proper image was downloaded using Bitmap
+comparison.
+
+To Run the Android Application, right click on the 'W6-A5-Android'
+project and select [Run As] -> [Android Application]
+
+To Run the Android Unit Test, right click on the 'W6-A5-Android-Test'
+project and select [Run As] -> [Android Unit Test]
+
+By default, the application and test program do an "offline" download
+since students often run this code an emulator or some other
+environment with slow/no Internet connectivity. If you'd like to
+run/test it in the Internet please change the DOWNLOAD_OFFLINE boolean
+in
+
+W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/DownloadUtils.java
+
+to false.
+
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/.classpath b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/.classpath
new file mode 100644
index 000000000..ef565ae48
--- /dev/null
+++ b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/.classpath
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/.project b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/.project
new file mode 100644
index 000000000..1b16bd56a
--- /dev/null
+++ b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/.project
@@ -0,0 +1,34 @@
+
+
+ W6-A5-ThreadedDownloads-StartedServices-Tests-Grading
+
+
+ week-6-assignment-5
+
+
+
+ com.android.ide.eclipse.adt.ResourceManagerBuilder
+
+
+
+
+ com.android.ide.eclipse.adt.PreCompilerBuilder
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ com.android.ide.eclipse.adt.ApkBuilder
+
+
+
+
+
+ com.android.ide.eclipse.adt.AndroidNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/AndroidManifest.xml b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/AndroidManifest.xml
new file mode 100644
index 000000000..2d80b1796
--- /dev/null
+++ b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/AndroidManifest.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/project.properties b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/project.properties
new file mode 100644
index 000000000..a3ee5ab64
--- /dev/null
+++ b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/project.properties
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-17
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-hdpi/ic_launcher.png b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 000000000..96a442e5b
Binary files /dev/null and b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-hdpi/ic_launcher.png differ
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-ldpi/ic_launcher.png b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 000000000..99238729d
Binary files /dev/null and b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-ldpi/ic_launcher.png differ
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-mdpi/ic_launcher.png b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 000000000..359047dfa
Binary files /dev/null and b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-mdpi/ic_launcher.png differ
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-nodpi/dougs.jpg b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-nodpi/dougs.jpg
new file mode 100644
index 000000000..e31e8b98b
Binary files /dev/null and b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-nodpi/dougs.jpg differ
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-xhdpi/ic_launcher.png b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..71c6d760f
Binary files /dev/null and b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/values/strings.xml b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/values/strings.xml
new file mode 100644
index 000000000..111dfea6e
--- /dev/null
+++ b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+
+
+ W6-A5-ThreadedDownloads-StartedServices-Tests
+
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/AllTests.java b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/AllTests.java
new file mode 100644
index 000000000..0d878f947
--- /dev/null
+++ b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/AllTests.java
@@ -0,0 +1,22 @@
+package edu.vuum.mocca.test;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * @class AllTests
+ *
+ * @brief Combine all the unit tests into one suite, so that you can run them together with one click.
+ */
+public class AllTests {
+ public static Test suite() {
+ TestSuite suite = new TestSuite(AllTests.class.getName());
+ //$JUnit-BEGIN$
+ suite.addTestSuite(DownloadIntentServiceTests.class);
+ suite.addTestSuite(DownloadUtilsTests.class);
+ suite.addTestSuite(ThreadPoolDownloadServiceTests.class);
+ suite.addTestSuite(DownloadActivityTests.class);
+ //$JUnit-END$
+ return suite;
+ }
+}
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/DownloadActivityTests.java b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/DownloadActivityTests.java
new file mode 100644
index 000000000..991dd170e
--- /dev/null
+++ b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/DownloadActivityTests.java
@@ -0,0 +1,121 @@
+package edu.vuum.mocca.test;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.test.ActivityInstrumentationTestCase2;
+import android.widget.EditText;
+import android.view.WindowManager;
+
+import com.robotium.solo.Solo;
+
+import edu.vuum.mocca.DownloadActivity;
+
+/**
+ * @class DownloadActivityTests
+ *
+ * @brief Tests the functionality of DownloadActivity. Current tests
+ * only press buttons and check that the correct image was downloaded.
+ */
+public class DownloadActivityTests
+ extends
+ ActivityInstrumentationTestCase2 {
+ /**
+ * Constructor initializes the superclass
+ */
+ public DownloadActivityTests() {
+ super(DownloadActivity.class);
+ }
+
+ /**
+ * This is the handle for Robotium, which allows us to interact
+ * with the UI.
+ */
+ Solo mSolo;
+
+ /**
+ * The context of this project, not the target project.
+ */
+ Context mContext;
+
+ /**
+ * The activity we're testing
+ */
+ DownloadActivity mActivity;
+
+ /**
+ * Store the bitmap that we expect to be downloaded.
+ */
+ Bitmap mExpected;
+
+ /**
+ * Called before each test is run to perform the initialization.
+ */
+ public void setUp() throws Exception{
+ super.setUp();
+
+ mActivity = getActivity();
+
+ // Setup Robotium and get the EditText View.
+ mSolo = new Solo(getInstrumentation(), mActivity);
+
+ mContext = getInstrumentation().getContext();
+
+ EditText edit_ = (EditText) mSolo.getView(edu.vuum.mocca.R.id.url);
+ mSolo.clearEditText(edit_);
+ mSolo.enterText(edit_, Options.TEST_URI);
+
+ mExpected = BitmapFactory.decodeResource(mContext.getResources(),
+ R.drawable.dougs);
+
+ getInstrumentation().callActivityOnStart(mActivity);
+ getInstrumentation().callActivityOnResume(mActivity);
+
+ // Let us dismiss the lockscreen
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+ }
+ });
+
+ // Wait for things to settle
+ Thread.sleep(Options.SHORT_WAIT_TIME);
+ }
+
+ /**
+ * Push the button and see if the correct image is displayed.
+ */
+ public void testThreadPoolButton() throws Exception {
+
+ // Make sure the current image isn't the one we're looking for
+ assertFalse(mExpected.sameAs(mActivity.mCurrentBitmap));
+
+ // Click on the "Start ThreadPool Button"
+ mSolo.clickOnView(mSolo.getView(edu.vuum.mocca.R.id.thread_pool_button));
+
+ // Wait for the image to download
+ Thread.sleep(Options.LONG_WAIT_TIME);
+
+ // Check if we displayed the correct image
+ assertTrue(mExpected.sameAs(mActivity.mCurrentBitmap));
+ }
+
+ /**
+ * Push the button and see if the correct image is displayed.
+ */
+ public void testIntentServiceButton() throws Exception {
+
+ // Make sure the current image isn't the one we're looking for
+ assertFalse(mExpected.sameAs(mActivity.mCurrentBitmap));
+
+ // Click on the "Start ThreadPool Button"
+ mSolo.clickOnView(mSolo.getView(edu.vuum.mocca.R.id.intent_service_button));
+
+ // Wait for the image to download
+ Thread.sleep(Options.LONG_WAIT_TIME);
+
+ // Check if we displayed the correct image
+ assertTrue(mExpected.sameAs(mActivity.mCurrentBitmap));
+ }
+}
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/DownloadIntentServiceTests.java b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/DownloadIntentServiceTests.java
new file mode 100644
index 000000000..8148d4dac
--- /dev/null
+++ b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/DownloadIntentServiceTests.java
@@ -0,0 +1,162 @@
+package edu.vuum.mocca.test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.test.ServiceTestCase;
+import edu.vuum.mocca.DownloadIntentService;
+import edu.vuum.mocca.DownloadUtils;
+
+/**
+ * @class DownloadIntentServiceTests
+ *
+ * @brief Tests the functionality of the DownloadIntentService.
+ */
+public class DownloadIntentServiceTests
+ extends
+ ServiceTestCase {
+ /**
+ * Use this latch to wait for messages to be received by the
+ * Handler's thread.
+ */
+ static CountDownLatch mLatch;
+
+ /**
+ * Stores the Uri received by the Handler.
+ */
+ static String mReceivedUri = null;
+
+ /**
+ * The Handler that receives a Message reply from the
+ * IntentService.
+ */
+ static MessageHandler mHandler;
+
+ /**
+ * Store the intent from makeIntent() to test proper creations.
+ */
+ Intent mIntent;
+
+ /**
+ * The context of THIS project, not the target project
+ */
+ Context mContext;
+
+ /**
+ * Constructor initializes the superclass.
+ */
+ public DownloadIntentServiceTests() {
+ super(DownloadIntentService.class);
+ }
+
+ /**
+ * @class MessageHandler
+ *
+ * @brief Make a handler to catch Messages sent from the service.
+ */
+ static class MessageHandler extends Handler {
+ /**
+ * Allows us to explicitly specify which thread's Looper to use.
+ */
+ public MessageHandler(Looper myLooper) {
+ super(myLooper);
+ }
+
+ /**
+ * Handle return messages from the IntentService. Store the
+ * received Uri and notify the JUnit thread.
+ */
+ public void handleMessage(Message msg) {
+ // We don't know what tag they'll use for the path, so we
+ // have to search for it.
+ mReceivedUri = Utilities.searchForPath(msg);
+
+ // Let the unit test thread know we've gotten a message.
+ mLatch.countDown();
+ }
+ }
+
+ /**
+ * This method is called before each test is called to perform
+ * initialization activities.
+ */
+ public void setUp() throws Exception{
+ super.setUp();
+
+ // Make an intent with the handler using the factory method.
+ mIntent = DownloadIntentService.makeIntent(getContext(),
+ new Handler(),
+ Options.TEST_URI);
+
+ // Get the context for THIS project, not the target project.
+ mContext = getContext().createPackageContext(this.getClass().getPackage().getName(),
+ Context.CONTEXT_IGNORE_SECURITY);
+
+ }
+
+ /**
+ * Check that the intent starts the correct service.
+ */
+ public void test_makeIntent_Class () {
+ assertTrue(Utilities.checkClass(mIntent, DownloadIntentService.class));
+ }
+
+ /**
+ * Check that the intent has the correct Uri.
+ */
+ public void test_makeIntent_Uri () {
+ assertTrue(Utilities.checkUri(mIntent));
+ }
+
+ /**
+ * Check that the intent has a Messenger attached.
+ */
+ public void test_makeIntent_Messenger () {
+ assertTrue(Utilities.checkMessenger(mIntent));
+ }
+
+ /**
+ * Try starting the service
+ */
+ public void test_startService () throws Exception {
+
+ mLatch = new CountDownLatch(1);
+
+ // Start a thread to handle the message when it's sent.
+ new Thread(new Runnable() {
+ public void run() {
+ Looper.prepare();
+ mHandler = new MessageHandler(Looper.myLooper());
+ Looper.loop();
+ }
+ }).start();
+
+ // Wait for the handler to get instantiated
+ Thread.sleep(Options.SHORT_WAIT_TIME);
+
+ //Create an Intent to start the service
+ mIntent = DownloadIntentService.makeIntent(getContext(),
+ mHandler,
+ Options.TEST_URI);
+
+ // Start the service
+ startService(mIntent);
+
+ assertNotNull(getService());
+
+ // Wait for it to send us a Message (or time out)
+ mLatch.await(Options.LONG_WAIT_TIME, TimeUnit.MILLISECONDS);
+
+ // Check if we got a Message or timed out
+ assertNotNull(mReceivedUri);
+
+ // Check that the image actually downloaded.
+ assertTrue(Utilities.checkDownloadedImage(mContext, mReceivedUri));
+ }
+}
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/DownloadUtilsTests.java b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/DownloadUtilsTests.java
new file mode 100644
index 000000000..d82d0087b
--- /dev/null
+++ b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/DownloadUtilsTests.java
@@ -0,0 +1,202 @@
+package edu.vuum.mocca.test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.test.ActivityInstrumentationTestCase2;
+import edu.vuum.mocca.DownloadActivity;
+import edu.vuum.mocca.DownloadUtils;
+
+/**
+ * @class DownloadUtilsTests
+ *
+ * @brief Test the functionality of the DownloadUtils class
+ */
+public class DownloadUtilsTests
+ extends
+ ActivityInstrumentationTestCase2 {
+ /**
+ * Constructor initializes the superclass.
+ */
+ public DownloadUtilsTests () {
+ super (DownloadActivity.class);
+ }
+
+ // The intent returned by makeMessengerIntent()
+ Intent mIntent;
+
+ // The bundle that is part of the intent
+ Bundle mExtras;
+
+ /**
+ * Use this latch to wait for messages to be received by the
+ * Handler's thread.
+ */
+ static CountDownLatch mLatch;
+
+ /**
+ * Stores the Uri received by the Handler.
+ */
+ static String mReceivedUri = null;
+
+ /**
+ * The Handler that receives a Message reply from the
+ * IntentService.
+ */
+ static MessageHandler mHandler;
+
+ /**
+ * @class MessageHandler
+ *
+ * @brief Define a handler to catch messages from sendPath()
+ */
+ static class MessageHandler extends Handler {
+ /**
+ * Constructor initializes the superclass
+ */
+ public MessageHandler(Looper myLooper) {
+ super(myLooper);
+ }
+
+ /**
+ * Handle return messages from the IntentService. Store the
+ * received Uri and notify the JUnit thread.
+ */
+ public void handleMessage(Message msg) {
+
+ // We don't know what tag they'll use for the path, so we
+ // have to search for it.
+ mReceivedUri = Utilities.searchForPath(msg);
+
+ // Let the unit test thread know we've gotten a message.
+ mLatch.countDown();
+ }
+ }
+
+ /**
+ * This method is called before each test is run to perform
+ * initialization activities.
+ */
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ // Make an arbitrary Messenger intent.
+ mIntent = DownloadUtils.makeMessengerIntent(getActivity(),
+ DownloadUtilsTests.class,
+ new MessageHandler(Looper.myLooper()),
+ Options.TEST_URI);
+ mExtras = mIntent.getExtras();
+ }
+
+ /**
+ * Try downloading a file.
+ */
+ public void test_downloadFile () {
+ Context context = getInstrumentation().getContext();
+ String result = DownloadUtils.downloadFile(getActivity(),
+ Uri.parse(Options.TEST_URI));
+
+ assertTrue(Utilities.checkDownloadedImage(context, result));
+ }
+
+ /**
+ * Check that the intent has a Messenger attached.
+ */
+ public void test_makeMessengerIntent_Messenger_Extra () {
+ assertTrue(Utilities.checkMessenger(mIntent));
+ }
+
+ /**
+ * Check that the intent has the proper Uri attached
+ */
+ public void test_makeMessengerIntent_Uri_Extra () {
+ assertTrue(Utilities.checkUri(mIntent));
+ }
+
+ /**
+ * Check that the intent will start the class we told it to
+ */
+ public void test_makeMessengerIntent_Class () {
+ assertTrue(Utilities.checkClass(mIntent, DownloadUtilsTests.class));
+ }
+
+ /**
+ * Try sending a message using sendPath().
+ */
+ public void test_sendPath () throws Exception {
+ mLatch = new CountDownLatch(1);
+
+ // Start a thread to catch the message from sendPath()
+ new Thread ( new Runnable () {
+ public void run() {
+ Looper.prepare();
+ mHandler = new MessageHandler(Looper.myLooper());
+ Looper.loop();
+ }
+ }).start();
+
+ // Wait for the handler to instantiate
+ Thread.sleep(Options.SHORT_WAIT_TIME);
+
+ // Send a message to ourselves
+ DownloadUtils.sendPath(Options.TEST_URI, new Messenger(mHandler));
+
+ // Wait for it to get here
+ mLatch.await(Options.LONG_WAIT_TIME, TimeUnit.MILLISECONDS);
+
+ // See if we got the message or timed out.
+ assertNotNull(mReceivedUri);
+
+ // Check that the Uri is correct
+ assertTrue(mReceivedUri.equals(Options.TEST_URI));
+
+ // Other tests use this
+ mReceivedUri = null;
+ }
+
+ /**
+ * Test that the downloadAndRespond method properly uses the other
+ * two methods in DownloadUtils.
+ */
+ public void test_downloadAndRespond() throws Exception {
+ mLatch = new CountDownLatch(1);
+
+ // Start a thread to handle messages we send to ourselves
+ new Thread ( new Runnable () {
+ public void run() {
+ Looper.prepare();
+ mHandler = new MessageHandler(Looper.myLooper());
+ Looper.loop();
+ }
+ }).start();
+
+ // Wait for mHandler to instantiate
+ Thread.sleep(Options.SHORT_WAIT_TIME);
+
+ // Download the image and send a message to ourselves
+ DownloadUtils.downloadAndRespond(getActivity(),
+ Uri.parse(Options.TEST_URI),
+ new Messenger(mHandler));
+
+ // Wait for it to get here.
+ mLatch.await(Options.LONG_WAIT_TIME, TimeUnit.MILLISECONDS);
+
+ // Check if we timed out or got the message
+ assertNotNull(mReceivedUri);
+
+ // Make sure the image downloaded correctly.
+ assertTrue(Utilities.checkDownloadedImage(getInstrumentation().getContext(), mReceivedUri));
+
+ // Other tests use this
+ mReceivedUri = null;
+ }
+}
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/Options.java b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/Options.java
new file mode 100644
index 000000000..042f8f648
--- /dev/null
+++ b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/Options.java
@@ -0,0 +1,34 @@
+package edu.vuum.mocca.test;
+
+/**
+ * @class Options
+ *
+ * @brief Holds global constants for the testing suite. More
+ * convenient than grabbing resources from /res and trying to
+ * finagle a working Context out of the test classes every time
+ * we want to use TEST_URI.
+ *
+ */
+public class Options {
+
+ /**
+ * The online URL of the image we're using for testing.
+ */
+ static final String TEST_URI = "https://d396qusza40orc.cloudfront.net/posa/dougs-xsmall.jpg";
+
+ /**
+ * Whatever image we're testing, store it in res/drawable-nodpi so
+ * we can compare it to what's downloaded.
+ */
+ static final int TEST_IMAGE = R.drawable.dougs;
+
+ /**
+ * Time we should wait for things to instantiate.
+ */
+ static final long SHORT_WAIT_TIME = 10000;
+
+ /**
+ * Time we should wait for things to download.
+ */
+ static final long LONG_WAIT_TIME = 25000;
+}
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/ThreadPoolDownloadServiceTests.java b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/ThreadPoolDownloadServiceTests.java
new file mode 100644
index 000000000..c1cc6020a
--- /dev/null
+++ b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/ThreadPoolDownloadServiceTests.java
@@ -0,0 +1,168 @@
+package edu.vuum.mocca.test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.test.ServiceTestCase;
+import edu.vuum.mocca.DownloadUtils;
+import edu.vuum.mocca.ThreadPoolDownloadService;
+
+/**
+ * @class ThreadPoolDownloadServiceTests
+ *
+ * @brief Tests the functionality of the ThreadPoolDownloadService.
+ */
+public class ThreadPoolDownloadServiceTests
+ extends
+ ServiceTestCase {
+ /**
+ * Use this latch to wait for messages to be received by the
+ * Handler's thread.
+ */
+ static CountDownLatch mLatch;
+
+ /**
+ * Stores the Uri received by the Handler.
+ */
+ static String mReceivedUri = null;
+
+ /**
+ * The Handler that receives a Message reply from the
+ * IntentService.
+ */
+ static MessageHandler mHandler;
+
+ /**
+ * Store the intent from makeIntent() to test proper creations.
+ */
+ Intent mIntent;
+
+ /**
+ * The context of THIS project, not the target project
+ */
+ Context mContext;
+
+ /**
+ * Constructor initializes the superclass.
+ */
+ public ThreadPoolDownloadServiceTests() {
+ super(ThreadPoolDownloadService.class);
+ }
+
+ /**
+ * @class MessageHandler
+ *
+ * @brief Define a private handler to catch messages from the service.
+ */
+ static class MessageHandler extends Handler {
+ /**
+ * Constructor initializes the superclass
+ */
+ public MessageHandler(Looper myLooper) {
+ super(myLooper);
+ }
+
+ /**
+ * Handle return messages from the IntentService. Store the
+ * received Uri and notify the JUnit thread.
+ */
+ public void handleMessage(Message msg) {
+ // We don't know what tag they'll use for the path, so we
+ // have to search for it.
+ mReceivedUri = Utilities.searchForPath(msg);
+
+ // Let the unit test thread know we've gotten a message.
+ mLatch.countDown();
+ }
+ }
+
+ /**
+ * This is called once before each test is run.
+ */
+ public void setUp() throws Exception{
+ super.setUp();
+
+ // Make an intent using the factory method
+ mIntent = ThreadPoolDownloadService.makeIntent(getContext(),
+ new Handler(),
+ Options.TEST_URI);
+
+ // Get the context for THIS project, not the target project.
+ mContext = getContext().createPackageContext(this.getClass().getPackage().getName(),
+ Context.CONTEXT_IGNORE_SECURITY);
+
+ }
+
+ /**
+ * Check that the intent will start the correct service.
+ */
+ public void test_makeIntent_Class () {
+ assertTrue(Utilities.checkClass(mIntent, ThreadPoolDownloadService.class));
+ }
+
+ /**
+ * Check that the intent has the correct uri.
+ */
+ public void test_makeIntent_Uri () {
+ assertTrue(Utilities.checkUri(mIntent));
+ }
+
+ /**
+ * Check that the intent has a Messenger attached.
+ */
+ public void test_makeIntent_Messenger () {
+ assertTrue(Utilities.checkMessenger(mIntent));
+ }
+
+ /**
+ * Test starting the service.
+ */
+ public void test_startService () throws Exception{
+
+ mLatch = new CountDownLatch(1);
+
+ // Start a thread to handle the message when it's sent.
+ new Thread(new Runnable() {
+ public void run() {
+ Looper.prepare();
+ mHandler = new MessageHandler(Looper.myLooper());
+ Looper.loop();
+ }
+ }).start();
+
+ // Wait for the handler to get instantiated
+ Thread.sleep(Options.SHORT_WAIT_TIME);
+
+ //Create an Intent to start the service
+ mIntent = ThreadPoolDownloadService.makeIntent(getContext(),
+ mHandler,
+ Options.TEST_URI);
+
+ // Start the service
+ startService(mIntent);
+
+ // Check if the service actually started
+ assertNotNull(getService());
+
+ // Wait for the service to download and send us a message
+ mLatch.await(Options.LONG_WAIT_TIME, TimeUnit.MILLISECONDS);
+
+ // See if we timed out or actually got a message
+ assertNotNull(mReceivedUri);
+
+ // Get the context for THIS project, not the target project
+ Context context = getContext().createPackageContext(this.getClass().getPackage().getName(),
+ Context.CONTEXT_IGNORE_SECURITY);
+
+ // Check that the file downloaded correctly
+ assertTrue(Utilities.checkDownloadedImage(context, mReceivedUri));
+ }
+}
+
+
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/Utilities.java b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/Utilities.java
new file mode 100644
index 000000000..107d69d8e
--- /dev/null
+++ b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices-Tests/src/edu/vuum/mocca/test/Utilities.java
@@ -0,0 +1,86 @@
+package edu.vuum.mocca.test;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.Messenger;
+import edu.vuum.mocca.DownloadUtils;
+
+/**
+ * @class Utilities
+ *
+ * @brief A few checks are repeated several times, such as checking
+ * whether a Messenger is present in an Intent, or if the proper
+ * Uri is set.
+ */
+public class Utilities {
+ /**
+ * Check that the proper Uri is set for the provided Intent.
+ */
+ static boolean checkUri(Intent intent) {
+ return Options.TEST_URI.equals(intent.getData().toString());
+ }
+
+ /**
+ * Check that the provided Intent contains a Messenger extra.
+ */
+ static boolean checkMessenger(Intent intent) {
+ Bundle extras = intent.getExtras();
+
+ // We don't know what tag they'll use for the Messenger, so we
+ // have to search for it.
+ Messenger messenger = null;
+
+ for (String key : extras.keySet()) {
+ if (extras.get(key) instanceof Messenger)
+ messenger = (Messenger) extras.get(key);
+ }
+
+ return (messenger != null);
+ }
+
+ /**
+ * Check that the provided Intent will start the expected class.
+ */
+ static boolean checkClass(Intent intent, Class> class_) {
+ return intent.getComponent().getClassName().equals(class_.getName());
+ }
+
+
+ /**
+ * Check if the downloaded Bitmap is equivalent to the expected
+ * bitmap.
+ */
+ static boolean checkDownloadedImage(Context test_context, String result) {
+ Bitmap downloaded = BitmapFactory.decodeFile(result);
+
+ Bitmap expected = BitmapFactory.decodeResource(test_context.getResources(),
+ Options.TEST_IMAGE);
+ return downloaded.sameAs(expected);
+ }
+
+ /**
+ * Search for the pathname that should be bundled in the provided
+ * Message. (Returns the last String extra found).
+ */
+ static String searchForPath (Message msg) {
+ Bundle msg_extras = msg.getData();
+
+ String path = null;
+ for (String key : msg_extras.keySet()) {
+ if (msg_extras.get(key) instanceof String)
+ path = (String) msg_extras.get(key);
+ }
+
+ return path;
+ }
+
+}
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/.classpath b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/.classpath
new file mode 100644
index 000000000..980bef1db
--- /dev/null
+++ b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/.classpath
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/.gitignore b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/.gitignore
new file mode 100644
index 000000000..102b6fc94
--- /dev/null
+++ b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/.gitignore
@@ -0,0 +1 @@
+*.properties
\ No newline at end of file
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/.project b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/.project
new file mode 100644
index 000000000..6051c75a9
--- /dev/null
+++ b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/.project
@@ -0,0 +1,33 @@
+
+
+ W6-A5-ThreadedDownloads-StartedServices-Grading
+
+
+
+
+
+ com.android.ide.eclipse.adt.ResourceManagerBuilder
+
+
+
+
+ com.android.ide.eclipse.adt.PreCompilerBuilder
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ com.android.ide.eclipse.adt.ApkBuilder
+
+
+
+
+
+ com.android.ide.eclipse.adt.AndroidNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/AndroidManifest.xml b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/AndroidManifest.xml
new file mode 100644
index 000000000..33958ae4e
--- /dev/null
+++ b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/AndroidManifest.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/libs/robotium-solo-5.1.jar b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/libs/robotium-solo-5.1.jar
new file mode 100644
index 000000000..1931dd97f
Binary files /dev/null and b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/libs/robotium-solo-5.1.jar differ
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/project.properties b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/project.properties
new file mode 100644
index 000000000..a3ee5ab64
--- /dev/null
+++ b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/project.properties
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-17
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable-hdpi/ic_launcher.png b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 000000000..96a442e5b
Binary files /dev/null and b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable-hdpi/ic_launcher.png differ
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable-mdpi/ic_launcher.png b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 000000000..359047dfa
Binary files /dev/null and b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable-mdpi/ic_launcher.png differ
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable-xhdpi/ic_launcher.png b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..71c6d760f
Binary files /dev/null and b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable/ka.png b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable/ka.png
new file mode 100644
index 000000000..3a751e9cd
Binary files /dev/null and b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/drawable/ka.png differ
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/layout/activity_download.xml b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/layout/activity_download.xml
new file mode 100644
index 000000000..0ae335689
--- /dev/null
+++ b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/layout/activity_download.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/raw/dougs.jpg b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/raw/dougs.jpg
new file mode 100644
index 000000000..e31e8b98b
Binary files /dev/null and b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/raw/dougs.jpg differ
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/values-v11/styles.xml b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/values-v11/styles.xml
new file mode 100644
index 000000000..3c02242ad
--- /dev/null
+++ b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/values-v11/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/values-v14/styles.xml b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/values-v14/styles.xml
new file mode 100644
index 000000000..a91fd0372
--- /dev/null
+++ b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/values-v14/styles.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/values/strings.xml b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/values/strings.xml
new file mode 100644
index 000000000..605931a4b
--- /dev/null
+++ b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/values/strings.xml
@@ -0,0 +1,9 @@
+
+
+ W6-A5-ThreadedDownloads-StartedServices
+ Enter URL:
+ https://d396qusza40orc.cloudfront.net/posa/ka.png
+ Run ThreadPool Messenger
+ Run IntentService Messenger
+ Reset Image
+
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/values/styles.xml b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/values/styles.xml
new file mode 100644
index 000000000..6ce89c7ba
--- /dev/null
+++ b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/res/values/styles.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/DownloadActivity.java b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/DownloadActivity.java
new file mode 100644
index 000000000..88cc49857
--- /dev/null
+++ b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/DownloadActivity.java
@@ -0,0 +1,130 @@
+package edu.vuum.mocca;
+
+import java.lang.ref.WeakReference;
+
+import android.os.Handler;
+import android.os.Message;
+import android.view.View;
+import android.widget.Toast;
+
+/**
+ * This is the main activity that the program uses to start the
+ * ThreadedDownloads application. It allows the user to input the URL
+ * of an image and download that image using one of two different
+ * Android Service implementations. When the service is done
+ * downloading the image, it stores it on the Android file system,
+ * then notifies this activity using the Messenger IPC mechanism
+ * discussed in the class.
+ *
+ * Starting services to run synchronously from the asynchronous UI
+ * Thread is an example of the Half-Sync/Half-Async Pattern. Starting
+ * services using Intents is an example of the Command Processor
+ * Pattern. This activity, the Creator, creates a Command in the form
+ * of an Intent. The Intent is received by the service process, which
+ * plays the role of the Executor.
+ *
+ * Returning a result using Messages and Handlers is an example of the
+ * Active Object Pattern. The Service must invoke a method to update
+ * the UI. However, the service thread is not allowed to interact with
+ * the UI. To decouple the invocation of this method from execution,
+ * the Service encapsulates the request in a Message, which plays the
+ * role of Active Object. The message is then passed to the UI
+ * thread's handler, which eventually executes the request on the UI
+ * Thread.
+ *
+ * Note: all UI functionality has been factored out into
+ * DownloadBase. If you wish to display an image, use
+ * displayBitmap(). If you want to get the URL from the EditText
+ * object, use getUrlString().
+ */
+public class DownloadActivity extends DownloadBase {
+ /**
+ * This is the handler used for handling messages sent by a
+ * Messenger. It receives a message containing a pathname to an
+ * image and displays that image in the ImageView.
+ *
+ * The handler plays several roles in the Active Object pattern,
+ * including Proxy, Future, and Servant.
+ *
+ * Please use displayBitmap() defined in DownloadBase
+ */
+ static class MessengerHandler extends Handler {
+
+ // A weak reference to the enclosing class
+ WeakReference outerClass;
+
+ /**
+ * A constructor that gets a weak reference to the enclosing class.
+ * We do this to avoid memory leaks during Java Garbage Collection.
+ *
+ * @see https://groups.google.com/forum/#!msg/android-developers/1aPZXZG6kWk/lIYDavGYn5UJ
+ */
+ public MessengerHandler(DownloadActivity outer) {
+ outerClass = new WeakReference(outer);
+ }
+
+ // Handle any messages that get sent to this Handler
+ @Override
+ public void handleMessage(Message msg) {
+
+ // Get an actual reference to the DownloadActivity
+ // from the WeakReference.
+ final DownloadActivity activity = outerClass.get();
+
+ // If DownloadActivity hasn't been garbage collected
+ // (closed by user), display the sent image.
+ if (activity != null) {
+ // TODO - You fill in here to display the image
+ // bitmap that's been downloaded and returned to
+ // the DownloadActivity as a pathname who's Bundle
+ // key is defined by DownloadUtils.PATHNAME_KEY
+ }
+ }
+ }
+
+ /**
+ * Instantiate the MessengerHandler, passing in the
+ * DownloadActivity to be stored as a WeakReference
+ */
+ MessengerHandler handler = new MessengerHandler(this);
+
+ /**
+ * This method is called when a user presses a button (see
+ * res/layout/activity_download.xml)
+ *
+ * Start a service using startService() or make a call to a Bound
+ * Service using an AIDL interface. The action performed depends
+ * on the button pressed.
+ *
+ * To get the URL from the EditText, please use getUrlString()
+ * defined in DownloadBase.
+ */
+ public void runService(View view) {
+ String which = "";
+
+ switch (view.getId()) {
+ case R.id.intent_service_button:
+ // TODO - You fill in here to start the
+ // DownloadIntentService with the appropriate Intent
+ // returned from the makeIntent() factory method.
+
+ which = "Starting IntentService";
+ break;
+
+ case R.id.thread_pool_button:
+ // TODO - You fill in here to start the
+ // ThreadPoolDownloadService with the appropriate Intent
+ // returned from the makeIntent() factory method.
+
+ which = "Starting ThreadPoolDownloadService";
+ break;
+
+ }
+
+ // Display a short pop-up notification telling the user which
+ // service was started.
+ Toast.makeText(this,
+ which,
+ Toast.LENGTH_SHORT).show();
+ }
+}
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/DownloadBase.java b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/DownloadBase.java
new file mode 100644
index 000000000..ad99ab54c
--- /dev/null
+++ b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/DownloadBase.java
@@ -0,0 +1,116 @@
+package edu.vuum.mocca;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.BitmapDrawable;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.ImageView;
+
+/**
+ * This class is used as a base for the two ThreadedDownloads
+ * Assignment Activities. It instantiates the UI and handles
+ * displaying images and getting text from the EditText object by
+ * making displayBitmap() and getUrlString() available to subclasses.
+ *
+ * This design creates a separation of concerns, where this class
+ * handles UI functionality, while subclasses handle any
+ * service-related functionality that downloads images.
+ *
+ * DownloadBase, which extends Activity and overrides its primitive
+ * operation onCreate(), is an example of the Template Method
+ * Pattern. More generically, any object that extends Activity and
+ * overrides its primitive methods such as onStart() or onPause() is
+ * also an example of the Template Method Pattern.
+ */
+public class DownloadBase extends Activity {
+ /**
+ * Used for debugging.
+ */
+ private final String TAG = this.getClass().getSimpleName();
+
+ /**
+ * This object is the reference to the text box that allows the user to
+ * input a URL to an image for downloading.
+ */
+ private EditText mEditText;
+
+ /**
+ * This object is a reference to the container of an image in the
+ * UI. When we finish downloading, we update this to display the
+ * image we just stored on the file system.
+ */
+ private ImageView mImageView;
+
+ /**
+ * The original bitmap (used for reseting the image).
+ */
+ private Bitmap mDefaultBitmap;
+
+ /**
+ * Store the current bitmap for testing purposes.
+ */
+ public Bitmap mCurrentBitmap;
+
+ /**
+ * Display the given file in the ImageView. Use
+ * BitmapFactory.decodeFile(). Store the bitmap used to update
+ * the file to make testing easier.
+ */
+ void displayBitmap (String pathname) {
+ mCurrentBitmap = BitmapFactory.decodeFile(pathname);
+
+ mImageView.setImageBitmap(mCurrentBitmap);
+ }
+
+ /**
+ * Gets the URL from the EditText.
+ */
+ String getUrlString () {
+ return mEditText.getText().toString();
+ }
+
+ /**
+ * Resets image to the default image stored with the program and
+ * reset the image default URL.
+ */
+ public void resetImage(View view) {
+ mImageView.setImageBitmap(mDefaultBitmap);
+ mCurrentBitmap = mDefaultBitmap;
+ mEditText.setText(getResources().getString(R.string.default_url));
+ Log.d(TAG, "reset Image");
+ }
+
+ /**
+ * This hook method is called when the Activity is initially
+ * created. It's where we setup the UI for the activity and
+ * initialize any objects that need to exist while the activity
+ * exists.
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ Log.d(getClass().getSimpleName(), "onCreate");
+ super.onCreate(savedInstanceState);
+
+ // Use the Android framework to create a User Interface for
+ // this activity. The interface that should be created is
+ // defined in activity_download.xml in the res/layout folder.
+ setContentView(R.layout.activity_download);
+
+ // Once the UI is created, get a reference to the instantiated
+ // EditText and ImageView objects by providing their ids to
+ // the Android framework.
+ mEditText = (EditText) findViewById(R.id.url);
+ mImageView = (ImageView) findViewById(R.id.imageView1);
+
+ // Store whatever image is originally displayed in the
+ // ImageView as a local Bitmap object so that we can quickly
+ // reset the image when a button is pressed.
+ mCurrentBitmap =
+ ((BitmapDrawable)(mImageView.getDrawable())).getBitmap();
+ mDefaultBitmap = mCurrentBitmap;
+ }
+}
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/DownloadIntentService.java b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/DownloadIntentService.java
new file mode 100644
index 000000000..82556049c
--- /dev/null
+++ b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/DownloadIntentService.java
@@ -0,0 +1,90 @@
+package edu.vuum.mocca;
+
+import android.app.IntentService;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Messenger;
+
+/**
+ * @class DownloadIntentService
+ *
+ * @brief This class extends the IntentService, which provides a
+ * framework that simplifies programming and processing Android
+ * Started Services concurrently.
+ *
+ * DownloadIntentService receives an Intent containing a URL
+ * (which is a type of URI) and a Messenger (which is an IPC
+ * mechanism). It downloads the file at the URL, stores it on
+ * the file system, then returns the path name to the caller
+ * using the supplied Messenger.
+ *
+ * The IntentService class implements the CommandProcessor
+ * pattern and the Template Method Pattern. The Messenger is
+ * used as part of the Active Object pattern.
+ */
+public class DownloadIntentService extends IntentService {
+ /**
+ * The default constructor for this service. Simply forwards
+ * construction to IntentService, passing in a name for the Thread
+ * that the service runs in.
+ */
+ public DownloadIntentService() {
+ super("IntentService Worker Thread");
+ }
+
+ /**
+ * Optionally allow the instantiator to specify the name of the
+ * thread this service runs in.
+ */
+ public DownloadIntentService(String name) {
+ super(name);
+ }
+
+ /**
+ * Make an intent that will start this service if supplied to
+ * startService() as a parameter.
+ *
+ * @param context The context of the calling component.
+ * @param handler The handler that the service should
+ * use to respond with a result
+ * @param uri The web URL of a file to download
+ *
+ * This method utilizes the Factory Method makeMessengerIntent()
+ * from the DownloadUtils class. The returned intent is a Command
+ * in the Command Processor Pattern. The intent contains a
+ * messenger, which plays the role of Proxy in the Active Object
+ * Pattern.
+ */
+ public static Intent makeIntent(Context context,
+ Handler handler,
+ String uri) {
+ // TODO - You fill in here to replace null with a call to the
+ // factory method in DownloadUtils that makes a Messenger
+ // Intent with the appropriate parameters.
+
+ return null;
+ }
+
+ /**
+ * Hook method called when a component calls startService() with
+ * the proper intent. This method serves as the Executor in the
+ * Command Processor Pattern. It receives an Intent, which serves
+ * as the Command, and executes some action based on that intent
+ * in the context of this service.
+ *
+ * This method is also a Hook Method in the Template Method
+ * Pattern. The Template class has an overall design goal and
+ * strategy, but it allows subclasses to how some steps in the
+ * strategy are implemented. For example, IntentService handles
+ * the creation and lifecycle of a started service, but allows a
+ * user to define what happens when an Intent is actually handled.
+ */
+ @Override
+ protected void onHandleIntent (Intent intent) {
+ // TODO - You fill in here with a call the appropriate helper
+ // method from the DownloadUtils class that downloads the uri
+ // in the intent and returns the file's pathname using a
+ // Messenger who's Bundle key is defined by DownloadUtils.MESSENGER_KEY
+ }
+}
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/DownloadUtils.java b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/DownloadUtils.java
new file mode 100644
index 000000000..b4ef53a7d
--- /dev/null
+++ b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/DownloadUtils.java
@@ -0,0 +1,259 @@
+package edu.vuum.mocca;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+
+import edu.vuum.mocca.R;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Base64;
+import android.util.Log;
+
+/**
+ * @class DownloadUtils
+ *
+ * @brief This class encapsulates several static methods so that all
+ * Services can access them without redefining them in each
+ * Service.
+ */
+public class DownloadUtils {
+ /**
+ * Used for debugging.
+ */
+ static final String TAG = "DownloadActivity";
+
+ /**
+ * The key used to store/retrieve a Messenger extra from a Bundle.
+ */
+ public static final String MESSENGER_KEY = "MESSENGER";
+
+ /**
+ * The key used to store/retrieve a file's pathname from a Bundle.
+ */
+ public static final String PATHNAME_KEY = "PATHNAME";
+
+ /**
+ * If you have access to a stable Internet connection for testing
+ * purposes, feel free to change this variable to false so it
+ * actually downloads the image from a remote server.
+ */
+ // TODO - You can change this to the appropriate setting for your
+ // environment.
+ static final boolean DOWNLOAD_OFFLINE = true;
+
+ /**
+ * Make an Intent which will start a service if provided as a
+ * parameter to startService().
+ *
+ * @param context The context of the calling component
+ * @param service The class of the service to be
+ * started. (For example, ThreadPoolDownloadService.class)
+ * @param handler The handler that the service should
+ * use to return a result.
+ * @param uri The web URL that the service should download
+ *
+ * This method is an example of the Factory Method Pattern,
+ * because it creates and returns a different Intent depending on
+ * the parameters provided to it.
+ *
+ * The Intent is used as the Command Request in the Command
+ * Processor Pattern when it is passed to the
+ * ThreadPoolDownloadService using startService().
+ *
+ * The Handler is used as the Proxy, Future, and Servant in the
+ * Active Object Pattern when it is passed a message, which serves
+ * as the Active Object, and acts depending on what the message
+ * contains.
+ *
+ * The handler *must* be wrapped in a Messenger to allow it to
+ * operate across process boundaries.
+ */
+ public static Intent makeMessengerIntent(Context context,
+ Class> service,
+ Handler handler,
+ String uri) {
+ Messenger messenger = new Messenger(handler);
+
+ Intent intent = new Intent(context,
+ service);
+ intent.putExtra(MESSENGER_KEY,
+ messenger);
+ intent.setData(Uri.parse(uri));
+
+ return intent;
+ }
+
+ /**
+ * Use the provided Messenger to send a Message to a Handler in
+ * another process.
+ *
+ * The provided string, outputPath, should be put into a Bundle
+ * and included with the sent Message.
+ */
+ public static void sendPath (String outputPath,
+ Messenger messenger) {
+ Message msg = Message.obtain();
+ Bundle data = new Bundle();
+ data.putString(PATHNAME_KEY,
+ outputPath);
+
+ // Make the Bundle the "data" of the Message.
+ msg.setData(data);
+
+ try {
+ // Send the Message back to the client Activity.
+ messenger.send(msg);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Download a file to the Android file system, then respond with
+ * the file location using the provided Messenger.
+ */
+ public static void downloadAndRespond(Context context,
+ Uri uri,
+ Messenger messenger) {
+ sendPath(DownloadUtils.downloadFile(context,
+ uri),
+ messenger);
+ }
+
+ /**
+ * The resource that we write to the file system in offline
+ * mode. Note that this must be the same image that the testing
+ * project expects. (found in res/drawable-nodpi and Options.java)
+ */
+ static final int OFFLINE_TEST_IMAGE = R.raw.dougs;
+
+ /**
+ * The file name that we should use to store the image in offline mode
+ */
+ static final String OFFLINE_FILENAME = "dougs.jpg";
+
+ /**
+ * Download the file located at the provided internet url using
+ * the URL class, store it on the android file system using
+ * openFileOutput(), and return the path to the file on disk.
+ *
+ * @param context the context in which to write the file
+ * @param uri the web url
+ *
+ * @return the path to the downloaded file on the file system
+ */
+ public static String downloadFile (Context context,
+ Uri uri) {
+
+ try {
+
+ // If we're offline, write the image in our resources to
+ // disk, then return that pathname.
+ if (DOWNLOAD_OFFLINE) {
+
+ // Store the image on the file system. We can store it
+ // as private since the test project runs in the same
+ // process as the target project
+ FileOutputStream out =
+ context.openFileOutput(OFFLINE_FILENAME, 0);
+
+ // Get a stream from the image resource
+ InputStream in =
+ context.getResources().openRawResource(OFFLINE_TEST_IMAGE);
+
+ // Write the resource to disk.
+ copy(in, out);
+ in.close();
+ out.close();
+
+ return context.getFilesDir().toString() + File.separator + OFFLINE_FILENAME;
+ }
+
+ // Otherwise, go ahead and download the file
+ else {
+ // Create a temp file.
+ final File file = getTemporaryFile(context,
+ uri.toString());
+ Log.d(TAG, " downloading to " + file);
+
+ // Download the contents at the URL, which should
+ // reference an image.
+ final InputStream in = (InputStream)
+ new URL(uri.toString()).getContent();
+ final OutputStream os =
+ new FileOutputStream(file);
+
+ // Copy the contents of the downloaded image to the
+ // temp file.
+ copy(in, os);
+ in.close();
+ os.close();
+
+ // Return the pathname of the temp file.
+ return file.getAbsolutePath();
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Exception while downloading. Returning null.");
+ Log.e(TAG, e.toString());
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ /**
+ * Create a temp file to store the result of a download.
+ *
+ * @param context
+ * @param url
+ * @return
+ * @throws IOException
+ */
+ static private File getTemporaryFile(final Context context,
+ final String url) throws IOException {
+
+ // This is what you'd normally call to get a unique temporary
+ // file, but for testing purposes we always name the file the
+ // same to avoid filling up student phones with numerous
+ // files!
+ // return context.getFileStreamPath(Base64.encodeToString(url.getBytes(),
+ // Base64.NO_WRAP)
+ // + System.currentTimeMillis());
+
+ return context.getFileStreamPath(Base64.encodeToString(url.getBytes(),
+ Base64.NO_WRAP));
+ }
+
+ /**
+ * Copy the contents of an InputStream into an OutputStream.
+ *
+ * @param in
+ * @param out
+ * @return
+ * @throws IOException
+ */
+ static public int copy(final InputStream in,
+ final OutputStream out) throws IOException {
+ final int BUFFER_LENGTH = 1024;
+ final byte[] buffer = new byte[BUFFER_LENGTH];
+ int totalRead = 0;
+ int read = 0;
+
+ while ((read = in.read(buffer)) != -1) {
+ out.write(buffer, 0, read);
+ totalRead += read;
+ }
+
+ return totalRead;
+ }
+}
diff --git a/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/ThreadPoolDownloadService.java b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/ThreadPoolDownloadService.java
new file mode 100644
index 000000000..6be4ebf7e
--- /dev/null
+++ b/grading-drivers/week-6-assignment-5/W6-A5-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/ThreadPoolDownloadService.java
@@ -0,0 +1,128 @@
+package edu.vuum.mocca;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Messenger;
+
+/**
+ * @class ThreadPoolDownloadService
+ *
+ * @brief This Service handles downloading several files concurrently
+ * within a pool of Threads. When it is created, it creates a
+ * ThreadPoolExecutor using the newFixedThreadPool() method of
+ * the Executors class.
+ *
+ * When this Service is started, it should be supplied with a
+ * URI for download and a Messenger. It downloads the URI
+ * supplied, stores it on the Android file system, then returns
+ * the pathname of the downloaded file using the supplied
+ * Messenger.
+ *
+ * This class implements the Synchronous Service layer of the
+ * Half-Sync/Half-Async pattern. It also implements a variant
+ * of the Factory Method pattern.
+ */
+public class ThreadPoolDownloadService extends Service {
+ /**
+ * A class constant that determines the maximum number of threads
+ * used to service download requests.
+ */
+ static final int MAX_THREADS = 4;
+
+ /**
+ * The ExecutorService that references a ThreadPool.
+ */
+ ExecutorService mExecutor;
+
+ /**
+ * Hook method called when the Service is created.
+ */
+ @Override
+ public void onCreate() {
+ // TODO - You fill in here to replace null with a new
+ // FixedThreadPool Executor that's configured to use
+ // MAX_THREADS. Use a factory method in the Executors class.
+
+ mExecutor = null;
+ }
+
+ /**
+ * Make an intent that will start this service if supplied to
+ * startService() as a parameter.
+ *
+ * @param context The context of the calling component.
+ * @param handler The handler that the service should
+ * use to respond with a result
+ * @param uri The web URL of a file to download
+ *
+ * This method utilizes the Factory Method makeMessengerIntent()
+ * from the DownloadUtils class. The returned intent is a Command
+ * in the Command Processor Pattern. The intent contains a
+ * messenger, which plays the role of Proxy in the Active Object
+ * Pattern.
+ */
+ public static Intent makeIntent(Context context,
+ Handler handler,
+ String uri) {
+ // TODO - You fill in here, by replacing null with an
+ // invocation of the appropriate factory method in
+ // DownloadUtils that makes a MessengerIntent.
+
+ return null;
+ }
+
+ /**
+ * Hook method called when a component calls startService() with
+ * the proper Intent.
+ */
+ @Override
+ public int onStartCommand(final Intent intent,
+ int flags,
+ int startId) {
+ // TODO - You fill in here to replace null with a new Runnable
+ // that the ThreadPoolExecutor will execute to download the
+ // image and respond to the client. The Runnable's run()
+ // method implementation should forward to the appropriate
+ // helper method from the DownloadUtils class that downloads
+ // the uri in the intent and returns the file's pathname using
+ // a Messenger who's Bundle key is defined by DownloadUtils.MESSENGER_KEY.
+
+ Runnable downloadRunnable = null;
+
+ mExecutor.execute(downloadRunnable);
+
+ // Tell the Android framework how to behave if this service is
+ // interrupted. In our case, we want to restart the service
+ // then re-deliver the intent so that all files are eventually
+ // downloaded.
+ return START_REDELIVER_INTENT;
+ }
+
+ /**
+ * Called when the service is destroyed, which is the last call
+ * the Service receives informing it to clean up any resources it
+ * holds.
+ */
+ @Override
+ public void onDestroy() {
+ // Ensure that the threads used by the ThreadPoolExecutor
+ // complete and are reclaimed by the system.
+
+ mExecutor.shutdown();
+ }
+
+ /**
+ * Return null since this class does not implement a Bound
+ * Service.
+ */
+ @Override
+ public IBinder onBind (Intent intent) {
+ return null;
+ }
+}
diff --git a/grading-drivers/week-8-assignment-7/Assignment-Description.txt b/grading-drivers/week-8-assignment-7/Assignment-Description.txt
new file mode 100644
index 000000000..ea3075a95
--- /dev/null
+++ b/grading-drivers/week-8-assignment-7/Assignment-Description.txt
@@ -0,0 +1,117 @@
+Week 8: Programming Assignment 7
+
+Released Monday, June 30th, 2014
+Due Monday, July 14th, 2014
+
+In this assignment, you will implement a program that downloads images
+using Bound Services and then displays them in the context of the UI
+Thread. The user can optionally select one of two different Bound
+Services whose implementations you will complete:
+
+. A DownloadBoundServiceSync that uses on the Android Interface
+ Definition Language (AIDL) and Binder framework to download files in
+ a Bound Service that communicates synchronously with the
+ DownloadActivity.
+
+. A DownloadBoundServiceAsync that uses on the Android Interface
+ Definition Language (AIDL) and Binder framework to download files in
+ a Bound Service that communicates asynchronously with the
+ DownloadActivity.
+
+In this directory you'll find Java source code, AndroidManifest.xml,
+and associated XML metadata files. The directory
+W8-A7-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/ contains
+the skeleton Java code that you'll implement by completing the "TODO -
+You fill in here" comments to provide a working solution. DO NOT
+CHANGE THE OVERALL STRUCTURE OF THE SKELETON - just fill in the "TODO
+- You fill in here" portions!!!
+
+The main goal of this project is to understand the pattern-oriented
+threaded download application and how to start and interact with Bound
+Services using AIDL and the underlying Binder IPC mechanisms. In
+particular, you'll need to do the following:
+
+. Understand the protocol for launching, connecting, interacting with,
+ and shutting down Bound Services, as described in these videos:
+
+ Section 2: Module 1: Part 7: Programming Bound Services (Part 1)
+ Section 2: Module 1: Part 8: Programming Bound Services (Part 2)
+
+. Understand how to program the DownloadActivity,
+ DownloadBoundServiceSync, and DownloadBoundServiceAsync classes to
+ launch the Bound Services using Intents, establish connections via
+ callbacks, and send/receive typed messages from the using AIDL and
+ the Binder IPC mechanisms, which are covered in these videos:
+
+ . Section 2: Module 1: Part 9: Programming Bound Services with AIDL
+ (which will be available shortly on the Coursera website)
+
+ . Another overview of AIDL is available (starting at timestamp 7:00)
+ in the video at
+
+ https://class.coursera.org/android-001/lecture/85
+
+ . More detailed coverage of AIDL and Binder RPC (starting at
+ timestamp 40:18) in the video at
+
+ https://www.youtube.com/watch?v=PvRTTvLOzag&list=PLZ9NgFYEMxp50tvT8806xllaCbd31DpDy&index=20
+
+ . Additional discussion of Advanced Bound Service Communication is
+ available in the video at
+
+ https://www.youtube.com/watch?v=N3bZWje8KQc&list=PLZ9NgFYEMxp50tvT8806xllaCbd31DpDy&index=21
+
+. Understand factory methods and implement the makeIntent() factory
+ methods in each service, which are similar to the
+ UniqueIDGeneratorService.makeIntent() factory method covered in
+ these videos:
+
+ Section 2: Module 1: Part 7: Programming Bound Services (Part 1)
+ Section 2: Module 1: Part 8: Programming Bound Services (Part 2)
+
+. Understand the methods provided in the DownloadUtils class, which
+ contains functionality that is common to both the
+ DownloadBoundServiceSync and DownloadBoundServiceAsync classes.
+
+These videos are available at
+
+https://class.coursera.org/posa-002/lecture
+
+We'll also discuss this assignment specification (and later its
+solution) in the POSA MOOC "Virtual Office Hours", which are described
+in item #38 at the POSA MOOC FAQ available from
+
+http://www.courera.org/course/posa
+
+You'll need to build this Android application in the Eclipse ADT
+environment. Unlike previous assignments, this program is
+user-driven, so output will vary depending on which URL is provided.
+In addition to using the default URL (which is available from
+https://d396qusza40orc.cloudfront.net/posa/ka.png) you should also
+test this application by finding an Internet URL to an image (ending
+in .jpg, .gif, .png etc.) and see if your application correctly
+displays the image.
+
+We provide several JUnit tests that should test your program's
+functionality. The unit tests test the Service classes independently
+of the DownloadActivity, and then test the overall functionality of
+the program. By default, the unit tests try to download the default
+URL and then check if the proper image was downloaded using Bitmap
+comparison.
+
+To Run the Android Application, right click on the 'W8-A7-Android'
+project and select [Run As] -> [Android Application]
+
+To Run the Android Unit Test, right click on the 'W8-A7-Android-Test'
+project and select [Run As] -> [Android JUnit Test]
+
+By default, the application and test program do an "offline" download
+since students often run this code an emulator or some other
+environment with slow/no Internet connectivity. If you'd like to
+run/test it in the Internet please change the DOWNLOAD_OFFLINE boolean
+in
+
+W8-A7-ThreadedDownloads-StartedServices/src/edu/vuum/mocca/DownloadUtils.java
+
+to false.
+
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/.classpath b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/.classpath
new file mode 100644
index 000000000..933b1be9c
--- /dev/null
+++ b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/.classpath
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/.project b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/.project
new file mode 100644
index 000000000..2a465c9c4
--- /dev/null
+++ b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/.project
@@ -0,0 +1,34 @@
+
+
+ W8-A7-ThreadedDownloads-BoundServices-Tests-Grading
+
+
+ week-8-assignment-7
+
+
+
+ com.android.ide.eclipse.adt.ResourceManagerBuilder
+
+
+
+
+ com.android.ide.eclipse.adt.PreCompilerBuilder
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ com.android.ide.eclipse.adt.ApkBuilder
+
+
+
+
+
+ com.android.ide.eclipse.adt.AndroidNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/AndroidManifest.xml b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/AndroidManifest.xml
new file mode 100644
index 000000000..2d80b1796
--- /dev/null
+++ b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/AndroidManifest.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/assets/dougs.jpg b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/assets/dougs.jpg
new file mode 100644
index 000000000..e31e8b98b
Binary files /dev/null and b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/assets/dougs.jpg differ
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/project.properties b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/project.properties
new file mode 100644
index 000000000..a3ee5ab64
--- /dev/null
+++ b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/project.properties
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-17
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/res/drawable-hdpi/ic_launcher.png b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 000000000..96a442e5b
Binary files /dev/null and b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/res/drawable-hdpi/ic_launcher.png differ
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/res/drawable-ldpi/ic_launcher.png b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 000000000..99238729d
Binary files /dev/null and b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/res/drawable-ldpi/ic_launcher.png differ
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/res/drawable-mdpi/ic_launcher.png b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 000000000..359047dfa
Binary files /dev/null and b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/res/drawable-mdpi/ic_launcher.png differ
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/res/drawable-xhdpi/ic_launcher.png b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..71c6d760f
Binary files /dev/null and b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/res/values/strings.xml b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/res/values/strings.xml
new file mode 100644
index 000000000..52655b2cd
--- /dev/null
+++ b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+
+
+ W8-A7-ThreadedDownloads-BoundServices-Tests
+
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/src/edu/vuum/mocca/test/AllTests.java b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/src/edu/vuum/mocca/test/AllTests.java
new file mode 100644
index 000000000..6722b5254
--- /dev/null
+++ b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/src/edu/vuum/mocca/test/AllTests.java
@@ -0,0 +1,24 @@
+package edu.vuum.mocca.test;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * @class AllTests
+ *
+ * @brief Combine all the unit tests into one suite so that we can run them all with one click.
+ */
+public class AllTests {
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite(AllTests.class.getName());
+ //$JUnit-BEGIN$
+ suite.addTestSuite(DownloadBoundServiceAsyncTests.class);
+ suite.addTestSuite(DownloadBoundServiceSyncTests.class);
+ suite.addTestSuite(DownloadUtilsTests.class);
+ suite.addTestSuite(DownloadActivityTests.class);
+ //$JUnit-END$
+ return suite;
+ }
+
+}
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/src/edu/vuum/mocca/test/DownloadActivityTests.java b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/src/edu/vuum/mocca/test/DownloadActivityTests.java
new file mode 100644
index 000000000..a4711bc63
--- /dev/null
+++ b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/src/edu/vuum/mocca/test/DownloadActivityTests.java
@@ -0,0 +1,218 @@
+package edu.vuum.mocca.test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.test.ActivityInstrumentationTestCase2;
+import android.view.WindowManager;
+import android.widget.EditText;
+
+import com.robotium.solo.Solo;
+
+import edu.vuum.mocca.DownloadActivity;
+import edu.vuum.mocca.DownloadCall;
+import edu.vuum.mocca.DownloadCallback;
+import edu.vuum.mocca.DownloadRequest;
+import edu.vuum.mocca.DownloadUtils;
+
+/**
+ * @class DownloadActivityTests
+ *
+ * @brief Tests the functionality of DownloadActivity. Current tests
+ * only press buttons and check that the correct image was downloaded.
+ */
+public class DownloadActivityTests extends ActivityInstrumentationTestCase2 {
+ /**
+ * Constructor initializes the superclass
+ */
+ public DownloadActivityTests() {
+ super(DownloadActivity.class);
+ }
+
+ // This is the handle to the Robotium framework, which lets us interact with the UI
+ Solo mSolo;
+
+ /**
+ * The context of this project, not the target project.
+ */
+ Context mContext;
+
+ // Store the bitmap we expect to be downloaded
+ Bitmap mExpected;
+
+ // The pathname that we received from DownloadCallback
+ static String mReceivedPathname = null;
+
+ // The latch used to synchronize DownloadCallback and the JUnit thread.
+ static CountDownLatch mLatch = null;
+
+ /**
+ * This is the implementation of DownloadCallback we'll use to test mDownloadRequest
+ */
+ DownloadCallback.Stub mDownloadCallback = new DownloadCallback.Stub() {
+ /**
+ * Set the received pathname in this activity then notify the JUnit thread that
+ * we've gotten a result.
+ */
+ @Override
+ public void sendPath(final String imagePathname) throws RemoteException {
+ mReceivedPathname = imagePathname;
+
+ if (mLatch != null)
+ mLatch.countDown();
+ }
+ };
+
+ // This is called once before each test is run.
+ public void setUp() throws Exception{
+ super.setUp();
+
+ // Setup Robotium and get the EditText View
+ mSolo = new Solo(getInstrumentation(), getActivity());
+
+ mContext = getInstrumentation().getContext();
+
+ EditText edit_ = (EditText) mSolo.getView(edu.vuum.mocca.R.id.url);
+ mSolo.clearEditText(edit_);
+ mSolo.enterText(edit_, Options.TEST_URI);
+
+ mExpected = Utilities.getTestBitmap(mContext);
+
+ getInstrumentation().callActivityOnStart(getActivity());
+ getInstrumentation().callActivityOnResume(getActivity());
+
+ // Let us dismiss the lockscreen
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+ }
+ });
+
+ // Wait for things to settle
+ Thread.sleep(Options.SHORT_WAIT_TIME);
+
+ }
+
+ /**
+ * Check that we're bound to the DownloadBoundServiceSync
+ */
+ public void testBoundToSync () throws Exception {
+ assertTrue(getActivity().isBoundToSync());
+ }
+
+ /**
+ * Check that we're bound to the DownloadBoundServiceSync
+ */
+ public void testBoundToAsync () throws Exception {
+ assertTrue(getActivity().isBoundToAsync());
+ }
+
+ /**
+ * Check that we correctly received mDownloadCall
+ */
+ public void testDownloadCall () throws Exception {
+
+ // Get the DownloadCall object
+ DownloadCall downloadCall = getActivity().getDownloadCall();
+
+ // Make sure it's not null
+ assertNotNull(downloadCall);
+
+ // Ask downloadCall to download a file
+ String fileName = downloadCall.downloadImage(Uri.parse(Options.TEST_URI));
+
+ // Then check if it's there.
+ assertTrue(Utilities.checkDownloadedImage(mContext, fileName));
+ }
+
+ /**
+ * Check that we correctly received mDownloadRequest
+ */
+ public void testDownloadRequest () throws Exception {
+
+ // Get the DownloadCall object
+ DownloadRequest downloadRequest = getActivity().getDownloadRequest();
+
+ // Make sure it's not null
+ assertNotNull(downloadRequest);
+
+ // Setup synchronization stuff
+ mLatch = new CountDownLatch(1);
+
+ // Ask downloadCall to download a file.
+ downloadRequest.downloadImage(Uri.parse(Options.TEST_URI), mDownloadCallback);
+
+ // Wait for a result
+ mLatch.await(Options.LONG_WAIT_TIME, TimeUnit.MILLISECONDS);
+
+ // Check that we actually got a result
+ assertNotNull(mReceivedPathname);
+
+ // Check that the the file is correctly downloaded
+ assertTrue(Utilities.checkDownloadedImage(mContext, mReceivedPathname));
+ }
+
+ /**
+ * Call the target activity's mDownloadCallback and see if the correct image is displayed.
+ */
+ public void testDownloadCallback() throws Exception {
+
+ // Make sure the current image isn't the one we're looking for
+ assertFalse(mExpected.sameAs(getActivity().mCurrentBitmap));
+
+ // Download the file we want displayed
+ String pathName = DownloadUtils.downloadFile(getActivity(), Uri.parse(Options.TEST_URI));
+
+ // Send the pathname to mDownloadCallback
+ getActivity().getDownloadCallback().sendPath(pathName);
+
+ // Wait for the image to display
+ Thread.sleep(Options.SHORT_WAIT_TIME);
+
+ // Check if we displayed the correct image
+ assertTrue(mExpected.sameAs(getActivity().mCurrentBitmap));
+ }
+
+
+ /**
+ * Push the button and see if the correct image is displayed.
+ */
+ public void testBoundAsyncButton() throws Exception {
+
+ // Make sure the current image isn't the one we're looking for
+ assertFalse(mExpected.sameAs(getActivity().mCurrentBitmap));
+
+ // Click on the "Start ThreadPool Button"
+ mSolo.clickOnView(mSolo.getView(edu.vuum.mocca.R.id.bound_async_button));
+
+ // Wait for the image to download
+ Thread.sleep(Options.LONG_WAIT_TIME);
+
+ // Check if we displayed the correct image
+ assertTrue(mExpected.sameAs(getActivity().mCurrentBitmap));
+ }
+
+ /**
+ * Push the button and see if the correct image is displayed
+ */
+ public void testBoundSyncButton() throws Exception {
+
+ // Make sure the current image isn't the one we're looking for
+ assertFalse(mExpected.sameAs(getActivity().mCurrentBitmap));
+
+ // Click on the "Start ThreadPool Button"
+ mSolo.clickOnView(mSolo.getView(edu.vuum.mocca.R.id.bound_sync_button));
+
+ // Wait for the image to download
+ Thread.sleep(Options.LONG_WAIT_TIME);
+
+ // Check if we displayed the correct image
+ assertTrue(mExpected.sameAs(getActivity().mCurrentBitmap));
+ }
+}
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/src/edu/vuum/mocca/test/DownloadBoundServiceAsyncTests.java b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/src/edu/vuum/mocca/test/DownloadBoundServiceAsyncTests.java
new file mode 100644
index 000000000..850037be2
--- /dev/null
+++ b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/src/edu/vuum/mocca/test/DownloadBoundServiceAsyncTests.java
@@ -0,0 +1,91 @@
+package edu.vuum.mocca.test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.test.ServiceTestCase;
+import edu.vuum.mocca.DownloadBoundServiceAsync;
+import edu.vuum.mocca.DownloadCallback;
+import edu.vuum.mocca.DownloadRequest;
+
+/**
+ * @class DownloadBoundServiceAsyncTests
+ *
+ * @brief Tests the functionality of the Asynchronous Bound Service.
+ */
+public class DownloadBoundServiceAsyncTests extends ServiceTestCase{
+ // Store the intent from makeIntent() to ensure proper creation
+ Intent mIntent;
+
+ // Use this latch to wait for messages to be received by the Handler's thread
+ static CountDownLatch mLatch;
+
+ // Stores the Uri received by the Handler
+ static String mReceivedUri = null;
+
+ // Create a callback to allow the service to send us a result
+ DownloadCallback.Stub callback_ = new DownloadCallback.Stub () {
+ // Store the received Uri and notify the JUnit thread
+ public void sendPath(String path) {
+ mReceivedUri = path;
+
+ // Let the unit test thread know we've gotten a
+ // message.
+ mLatch.countDown();
+ }
+ };
+
+ /**
+ * Constructor initializes the superclass
+ */
+ public DownloadBoundServiceAsyncTests() {
+ super(DownloadBoundServiceAsync.class);
+ }
+
+ // This is called before each test is run
+ public void setUp() throws Exception{
+ super.setUp();
+
+ // Create an intent using the factory method
+ mIntent = DownloadBoundServiceAsync.makeIntent(getContext());
+ }
+
+ /**
+ * Check that the intent will start the correct class
+ */
+ public void test_makeMIntentClass () {
+ assertTrue(Utilities.checkClass(mIntent, DownloadBoundServiceAsync.class));
+ }
+
+ /**
+ * Test bindService functionality
+ */
+ public void test_bindService () throws Exception{
+ mLatch = new CountDownLatch(1);
+
+ // Bind to the service and get the AIDL interface
+ DownloadRequest request = DownloadRequest.Stub.asInterface(bindService(mIntent));
+
+ // Make sure the service is running
+ assertNotNull(getService());
+
+ // Download the image
+ request.downloadImage(Uri.parse(Options.TEST_URI), callback_);
+
+ // Wait for the callback
+ mLatch.await(Options.LONG_WAIT_TIME, TimeUnit.MILLISECONDS);
+
+ // Make sure the callback was called
+ assertNotNull(mReceivedUri);
+
+ // Get the context for THIS project, not the target project
+ Context context = getContext().createPackageContext(this.getClass().getPackage().getName(),
+ Context.CONTEXT_IGNORE_SECURITY);
+
+ // Make sure the file actually downloaded
+ assertTrue(Utilities.checkDownloadedImage(context, mReceivedUri));
+ }
+}
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/src/edu/vuum/mocca/test/DownloadBoundServiceSyncTests.java b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/src/edu/vuum/mocca/test/DownloadBoundServiceSyncTests.java
new file mode 100644
index 000000000..09d5377b1
--- /dev/null
+++ b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/src/edu/vuum/mocca/test/DownloadBoundServiceSyncTests.java
@@ -0,0 +1,58 @@
+package edu.vuum.mocca.test;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.test.ServiceTestCase;
+import edu.vuum.mocca.DownloadBoundServiceSync;
+import edu.vuum.mocca.DownloadCall;
+
+/**
+ * @class
+ *
+ * @brief Test the functionality of the Bound Synchronous Service.
+ */
+public class DownloadBoundServiceSyncTests extends ServiceTestCase{
+ // Store the intent from makeIntent() to ensure proper creation
+ Intent mIntent;
+
+ /**
+ * Constructor initializes the superclass
+ */
+ public DownloadBoundServiceSyncTests() {
+ super(DownloadBoundServiceSync.class);
+ }
+
+ // This is called before each test is run.
+ public void setUp() throws Exception{
+ super.setUp();
+
+ // Create an intent using the factory method
+ mIntent = DownloadBoundServiceSync.makeIntent(getContext());
+ }
+
+ // Make sure the intent will start the correct service
+ public void test_makeIntent_Class () {
+ // Make sure the intent will start the correct service
+ assertTrue(Utilities.checkClass(mIntent, DownloadBoundServiceSync.class));
+ }
+
+ // Call bindService and see if the service correctly downloads the image and responds.
+ public void test_bindService () throws Exception {
+ // Bind to the service and get the AIDL interface
+ DownloadCall downloadCall = DownloadCall.Stub.asInterface(bindService(mIntent));
+
+ // Check that the service is alive
+ assertNotNull(getService());
+
+ // Download the image synchronously
+ String result = downloadCall.downloadImage(Uri.parse(Options.TEST_URI));
+
+ // Get the context of THIS project, not the target project
+ Context context = getContext().createPackageContext(this.getClass().getPackage().getName(),
+ Context.CONTEXT_IGNORE_SECURITY);
+
+ // Check that the image actually downloaded
+ assertTrue(Utilities.checkDownloadedImage(context, result));
+ }
+}
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/src/edu/vuum/mocca/test/DownloadUtilsTests.java b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/src/edu/vuum/mocca/test/DownloadUtilsTests.java
new file mode 100644
index 000000000..aa3496661
--- /dev/null
+++ b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/src/edu/vuum/mocca/test/DownloadUtilsTests.java
@@ -0,0 +1,36 @@
+package edu.vuum.mocca.test;
+
+import java.io.IOException;
+
+import android.content.Context;
+import android.net.Uri;
+import android.test.ActivityInstrumentationTestCase2;
+import edu.vuum.mocca.DownloadActivity;
+import edu.vuum.mocca.DownloadUtils;
+
+/**
+ * @class DownloadUtilsTests
+ *
+ * @brief Test the functionality of the DownloadUtils class
+ */
+public class DownloadUtilsTests extends ActivityInstrumentationTestCase2 {
+ /**
+ * Constructor initializes the superclass
+ */
+ public DownloadUtilsTests () {
+ super (DownloadActivity.class);
+ }
+
+ /**
+ * Try downloading a file
+ * @throws IOException
+ */
+ public void test_downloadFile () throws IOException {
+ Context context = getInstrumentation().getContext();
+ String result = DownloadUtils.downloadFile(getActivity(),
+ Uri.parse(Options.TEST_URI));
+
+ assertTrue(Utilities.checkDownloadedImage(context, result));
+ }
+
+}
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/src/edu/vuum/mocca/test/Options.java b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/src/edu/vuum/mocca/test/Options.java
new file mode 100644
index 000000000..2754189fd
--- /dev/null
+++ b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/src/edu/vuum/mocca/test/Options.java
@@ -0,0 +1,34 @@
+package edu.vuum.mocca.test;
+
+/**
+ * @class Options
+ *
+ * @brief Holds global constants for the testing suite. More
+ * convenient than grabbing resources from /res and trying to
+ * finagle a working Context out of the test classes every time
+ * we want to use TEST_URI.
+ *
+ */
+public class Options {
+
+ /**
+ * The online URL of the image we're using for testing.
+ */
+ static final String TEST_URI = "https://d396qusza40orc.cloudfront.net/posa/dougs-xsmall.jpg";
+
+ /**
+ * Whatever image we're testing, store it in assets/ so
+ * we can compare it to what's downloaded.
+ */
+ static final String TEST_IMAGE = "dougs.jpg";
+
+ /**
+ * Time we should wait for things to instantiate.
+ */
+ static final long SHORT_WAIT_TIME = 10000;
+
+ /**
+ * Time we should wait for things to download.
+ */
+ static final long LONG_WAIT_TIME = 25000;
+}
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/src/edu/vuum/mocca/test/Utilities.java b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/src/edu/vuum/mocca/test/Utilities.java
new file mode 100644
index 000000000..1bfddf5e5
--- /dev/null
+++ b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices-Tests/src/edu/vuum/mocca/test/Utilities.java
@@ -0,0 +1,75 @@
+package edu.vuum.mocca.test;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.AssetManager;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.os.Message;
+
+/**
+ * @class Utilities
+ *
+ * @brief A few checks are repeated several times, such as checking
+ * whether the correct class is set for an Intent, or if the proper
+ * Uri is present in getData().
+ */
+public class Utilities {
+ // Check that the proper Uri is set for the provided Intent
+ static boolean checkUri(Intent intent) {
+ return Options.TEST_URI.equals(intent.getData().toString());
+ }
+
+ // Check that the provided Intent will start the expected class.
+ static boolean checkClass(Intent intent, Class> class_) {
+ return intent.getComponent().getClassName().equals(class_.getName());
+ }
+
+
+ /**
+ * Check if the downloaded Bitmap is equivalent to the expected
+ * bitmap.
+ * @throws IOException
+ */
+ static boolean checkDownloadedImage(Context test_context, String result) throws IOException {
+ Bitmap downloaded = BitmapFactory.decodeFile(result);
+
+ Bitmap expected = getTestBitmap(test_context);
+ return downloaded.sameAs(expected);
+ }
+
+ /**
+ * Get the Bitmap that we're expecting to be downloaded from the assets folder.
+ *
+ * @param context The context of THIS (the test) project
+ * @throws IOException
+ */
+ public static Bitmap getTestBitmap(Context context) throws IOException {
+ AssetManager assetManager = context.getAssets();
+
+ InputStream istr;
+ Bitmap bitmap = null;
+ istr = assetManager.open(Options.TEST_IMAGE);
+ bitmap = BitmapFactory.decodeStream(istr);
+
+
+ return bitmap;
+ }
+
+ // Search for the pathname that should be bundled in the provided Message. (Returns the last String extra found)
+ static String searchForPath (Message msg) {
+ Bundle msg_extras = msg.getData();
+
+ String path = null;
+ for (String key : msg_extras.keySet()) {
+ if (msg_extras.get(key) instanceof String)
+ path = (String) msg_extras.get(key);
+ }
+
+ return path;
+ }
+}
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/.classpath b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/.classpath
new file mode 100644
index 000000000..ed9fc3580
--- /dev/null
+++ b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/.classpath
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/.project b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/.project
new file mode 100644
index 000000000..4e7355f92
--- /dev/null
+++ b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/.project
@@ -0,0 +1,33 @@
+
+
+ W8-A7-ThreadedDownloads-BoundServices-Grading
+
+
+
+
+
+ com.android.ide.eclipse.adt.ResourceManagerBuilder
+
+
+
+
+ com.android.ide.eclipse.adt.PreCompilerBuilder
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ com.android.ide.eclipse.adt.ApkBuilder
+
+
+
+
+
+ com.android.ide.eclipse.adt.AndroidNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/AndroidManifest.xml b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/AndroidManifest.xml
new file mode 100644
index 000000000..0c95749d0
--- /dev/null
+++ b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/AndroidManifest.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/libs/robotium-solo-5.1.jar b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/libs/robotium-solo-5.1.jar
new file mode 100644
index 000000000..1931dd97f
Binary files /dev/null and b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/libs/robotium-solo-5.1.jar differ
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/project.properties b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/project.properties
new file mode 100644
index 000000000..a3ee5ab64
--- /dev/null
+++ b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/project.properties
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-17
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/res/drawable-hdpi/ic_launcher.png b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 000000000..96a442e5b
Binary files /dev/null and b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/res/drawable-hdpi/ic_launcher.png differ
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/res/drawable-mdpi/ic_launcher.png b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 000000000..359047dfa
Binary files /dev/null and b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/res/drawable-mdpi/ic_launcher.png differ
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/res/drawable-xhdpi/ic_launcher.png b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..71c6d760f
Binary files /dev/null and b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/res/drawable/ka.png b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/res/drawable/ka.png
new file mode 100644
index 000000000..3a751e9cd
Binary files /dev/null and b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/res/drawable/ka.png differ
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/res/layout/activity_download.xml b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/res/layout/activity_download.xml
new file mode 100644
index 000000000..c458a3846
--- /dev/null
+++ b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/res/layout/activity_download.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/res/raw/dougs.jpg b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/res/raw/dougs.jpg
new file mode 100644
index 000000000..e31e8b98b
Binary files /dev/null and b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/res/raw/dougs.jpg differ
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/res/values-v11/styles.xml b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/res/values-v11/styles.xml
new file mode 100644
index 000000000..3c02242ad
--- /dev/null
+++ b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/res/values-v11/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/res/values-v14/styles.xml b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/res/values-v14/styles.xml
new file mode 100644
index 000000000..a91fd0372
--- /dev/null
+++ b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/res/values-v14/styles.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/res/values/strings.xml b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/res/values/strings.xml
new file mode 100644
index 000000000..131c6df17
--- /dev/null
+++ b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/res/values/strings.xml
@@ -0,0 +1,9 @@
+
+
+ W8-A7-ThreadedDownloads-BoundServices
+ Enter URL:
+ http://www.dre.vanderbilt.edu/~schmidt/ka.png
+ Run Synchronous Bound Service
+ Run Asynchronous Bound Service
+ Reset Image
+
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/res/values/styles.xml b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/res/values/styles.xml
new file mode 100644
index 000000000..6ce89c7ba
--- /dev/null
+++ b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/res/values/styles.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/src/edu/vuum/mocca/DownloadActivity.java b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/src/edu/vuum/mocca/DownloadActivity.java
new file mode 100644
index 000000000..c52c0e435
--- /dev/null
+++ b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/src/edu/vuum/mocca/DownloadActivity.java
@@ -0,0 +1,236 @@
+package edu.vuum.mocca;
+
+import android.content.ComponentName;
+import android.content.ServiceConnection;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.View;
+
+/**
+ * This is the main Activity that the program uses to start the
+ * ThreadedDownloads application. It allows the user to input the URL
+ * of an image and download that image using one of two different
+ * Android Bound Service implementations: synchronous and
+ * asynchronous. The Activity starts the Service using bindService().
+ * After the Service is started, its onBind() hook method returns an
+ * implementation of an AIDL interface to the Activity by
+ * asynchronously calling the onServiceConnected() hook method in the
+ * Activity. The AIDL interface object that's returned can then be
+ * used to interact with the Service either synchronously or
+ * asynchronously, depending on the type of AIDL interface requested.
+ *
+ * Starting Bound Services to run synchronously in background Threads
+ * from the asynchronous UI Thread is an example of the
+ * Half-Sync/Half-Async Pattern. Starting Bound Services using
+ * Intents is an example of the Activator and Command Processor
+ * patterns. The DownloadActivity plays the role of the Creator and
+ * creates a Command in the form of an Intent. The Intent is received
+ * by the Service process, which plays the role of the Executor.
+ *
+ * The use of AIDL interfaces to pass information between two
+ * different processes is an example of the Broker Pattern, in which
+ * all communication-related functionality is encapsulated in the AIDL
+ * interface and the underlying Android Binder framework, shielding
+ * applications from tedious and error-prone aspects of inter-process
+ * communication.
+ */
+public class DownloadActivity extends DownloadBase {
+ /**
+ * Used for debugging.
+ */
+ private final String TAG = this.getClass().getSimpleName();
+
+ /**
+ * The AIDL Interface that's used to make twoway calls to the
+ * DownloadServiceSync Service. This object plays the role of
+ * Requestor in the Broker Pattern. If it's null then there's no
+ * connection to the Service.
+ */
+ DownloadCall mDownloadCall;
+
+ /**
+ * The AIDL Interface that we will use to make oneway calls to the
+ * DownloadServiceAsync Service. This plays the role of Requestor
+ * in the Broker Pattern. If it's null then there's no connection
+ * to the Service.
+ */
+ DownloadRequest mDownloadRequest;
+
+ /**
+ * This ServiceConnection is used to receive results after binding
+ * to the DownloadServiceSync Service using bindService().
+ */
+ ServiceConnection mServiceConnectionSync = new ServiceConnection() {
+ /**
+ * Cast the returned IBinder object to the DownloadCall
+ * AIDL Interface and store it for later use in
+ * mDownloadCall.
+ */
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ Log.d(TAG, "ComponentName: " + name);
+ // TODO You fill in here to replace null with a call
+ // to a generated stub method that converts the
+ // service parameter into an interface that can be
+ // used to make RPC calls to the Service.
+
+ mDownloadCall = null;
+ }
+
+ /**
+ * Called if the remote service crashes and is no longer
+ * available. The ServiceConnection will remain bound,
+ * but the service will not respond to any requests.
+ */
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mDownloadCall = null;
+ }
+
+ };
+
+ /**
+ * This ServiceConnection is used to receive results after binding
+ * to the DownloadServiceAsync Service using bindService().
+ */
+ ServiceConnection mServiceConnectionAsync = new ServiceConnection() {
+ /**
+ * Cast the returned IBinder object to the DownloadRequest
+ * AIDL Interface and store it for later use in
+ * mDownloadRequest.
+ */
+ @Override
+ public void onServiceConnected(ComponentName name,
+ IBinder service) {
+ // TODO You fill in here to replace null with a call
+ // to a generated stub method that converts the
+ // service parameter into an interface that can be
+ // used to make RPC calls to the Service.
+
+ mDownloadRequest = null;
+ }
+
+ /**
+ * Called if the remote service crashes and is no longer
+ * available. The ServiceConnection will remain bound,
+ * but the service will not respond to any requests.
+ */
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mDownloadRequest = null;
+ }
+ };
+
+ /**
+ * The implementation of the DownloadCallback AIDL
+ * Interface. Should be passed to the DownloadBoundServiceAsync
+ * Service using the DownloadRequest.downloadImage() method.
+ *
+ * This implementation of DownloadCallback.Stub plays the role of
+ * Invoker in the Broker Pattern.
+ */
+ DownloadCallback.Stub mDownloadCallback = new DownloadCallback.Stub() {
+ /**
+ * Called when the DownloadServiceAsync finishes obtaining
+ * the results from the GeoNames Web service. Use the
+ * provided String to display the results in a TextView.
+ */
+ @Override
+ public void sendPath(final String imagePathname) throws RemoteException {
+ // TODO - You fill in here to replace null with a new
+ // Runnable whose run() method displays the bitmap
+ // image whose pathname is passed as a parameter to
+ // sendPath(). Please use displayBitmap() defined in
+ // DownloadBase.
+
+ Runnable displayRunnable = null;
+ }
+ };
+
+ /**
+ * This method is called when a user presses a button (see
+ * res/layout/activity_download.xml)
+ */
+ public void runService(View view) {
+ Uri uri = Uri.parse(getUrlString());
+
+ hideKeyboard();
+
+ switch (view.getId()) {
+ case R.id.bound_sync_button:
+ // TODO - You fill in here to use mDownloadCall to
+ // download the image & then display it.
+ break;
+
+ case R.id.bound_async_button:
+ // TODO - You fill in here to call downloadImage() on
+ // mDownloadRequest, passing in the appropriate Uri and
+ // callback.
+ break;
+ }
+ }
+
+ /**
+ * Hook method called when the DownloadActivity becomes visible to
+ * bind the Activity to the Services.
+ */
+ @Override
+ public void onStart () {
+ super.onStart();
+
+ // Bind this activity to the DownloadBoundService* Services if
+ // they aren't already bound Use mBoundSync/mBoundAsync
+ if (mDownloadCall == null)
+ bindService(DownloadBoundServiceSync.makeIntent(this),
+ mServiceConnectionSync,
+ BIND_AUTO_CREATE);
+ if (mDownloadRequest == null)
+ bindService(DownloadBoundServiceAsync.makeIntent(this),
+ mServiceConnectionAsync,
+ BIND_AUTO_CREATE);
+ }
+
+ /**
+ * Hook method called when the DownloadActivity becomes completely
+ * hidden to unbind the Activity from the Services.
+ */
+ @Override
+ public void onStop () {
+ super.onStop();
+
+ // Unbind the Sync/Async Services if they are bound. Use
+ // mBoundSync/mBoundAsync
+ if (mDownloadCall != null)
+ unbindService(mServiceConnectionSync);
+ if (mDownloadRequest != null)
+ unbindService(mServiceConnectionAsync);
+ }
+
+ // Public accessor method for testing purposes
+ public DownloadCall getDownloadCall () {
+ return mDownloadCall;
+ }
+
+ // Public accessor method for testing purposes
+ public DownloadRequest getDownloadRequest () {
+ return mDownloadRequest;
+ }
+
+ // Public accessor method for testing purposes
+ public DownloadCallback getDownloadCallback () {
+ return mDownloadCallback;
+ }
+
+ // Public accessor method for testing purposes
+ public boolean isBoundToSync () {
+ return mDownloadCall != null;
+ }
+
+ // Public accessor method for testing purposes
+ public boolean isBoundToAsync () {
+ return mDownloadRequest != null;
+ }
+
+}
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/src/edu/vuum/mocca/DownloadBase.java b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/src/edu/vuum/mocca/DownloadBase.java
new file mode 100644
index 000000000..361916a54
--- /dev/null
+++ b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/src/edu/vuum/mocca/DownloadBase.java
@@ -0,0 +1,146 @@
+package edu.vuum.mocca;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.BitmapDrawable;
+import android.os.Bundle;
+import android.os.StrictMode;
+import android.util.Log;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+import android.widget.ImageView;
+
+/**
+ * This class is used the base class for the DownloadActivity. It
+ * instantiates the UI and handles displaying images and getting text
+ * from the EditText object by making displayBitmap() and
+ * getUrlString() available to subclasses. This design separates
+ * concerns by having DownloadBase handle UI functionality while
+ * subclasses (such as DownloadActivity) handle any Service-related
+ * communication with the GeoNames Web service.
+ *
+ * GeoNamesBase is an example of the Template Method pattern since it
+ * extends Activity and overrides its onCreate() hook method. More
+ * generally, any object that extends Activity and overrides its hook
+ * methods, such as onStart() or onPause(), is also an example of the
+ * Template Method pattern.
+ */
+public class DownloadBase extends Activity {
+ /**
+ * Used for debugging.
+ */
+ private final String TAG = this.getClass().getSimpleName();
+
+ /**
+ * This is the reference to the text box that allows the user to
+ * input a URL to an image for downloading.
+ */
+ private EditText mUrlEditText;
+
+ /**
+ * This is a reference to the container of an image in the UI.
+ * When we finish downloading, we update this to display the image
+ * we just stored on the file system.
+ */
+ private ImageView mImageView;
+
+ /**
+ * The original bitmap (used for reseting the image).
+ */
+ private Bitmap mDefaultBitmap;
+
+ /**
+ * Store the current bitmap for testing purposes.
+ */
+ public Bitmap mCurrentBitmap;
+
+ /**
+ * Hide the keyboard after a user has finished typing the url.
+ */
+ protected void hideKeyboard() {
+ InputMethodManager mgr =
+ (InputMethodManager) getSystemService
+ (Context.INPUT_METHOD_SERVICE);
+ mgr.hideSoftInputFromWindow(mUrlEditText.getWindowToken(),
+ 0);
+ }
+
+ /**
+ * Display the given file in the ImageView. Use
+ * BitmapFactory.decodeFile(). Store the bitmap used to update
+ * the file to make testing easier.
+ */
+ void displayBitmap (String pathname) {
+ mCurrentBitmap = BitmapFactory.decodeFile(pathname);
+
+ mImageView.setImageBitmap(mCurrentBitmap);
+ }
+
+ /**
+ * Gets the URL from the EditText
+ */
+ String getUrlString () {
+ return mUrlEditText.getText().toString();
+ }
+
+ /**
+ * Resets image to the default image stored with the program.
+ */
+ public void resetImage(View view) {
+ mImageView.setImageBitmap(mDefaultBitmap);
+ mCurrentBitmap = mDefaultBitmap;
+ Log.d(TAG, "reset Image");
+ }
+
+ /**
+ * This is called when the Activity is initially created. This is
+ * where we setup the UI for the activity and initialize any
+ * objects that need to exist while the activity exists.
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ Log.d(getClass().getSimpleName(), "onCreate");
+ super.onCreate(savedInstanceState);
+
+ // Use the Android framework to create a User Interface for
+ // this activity. The interface that should be created is
+ // defined in activity_download.xml in the res/layout folder.
+ setContentView(R.layout.activity_download);
+
+ // Once the UI is created, get a reference to the instantiated
+ // EditText and ImageView objects by providing their ids to
+ // the Android framework.
+ mUrlEditText = (EditText) findViewById(R.id.url);
+ mImageView = (ImageView) findViewById(R.id.imageView1);
+
+ // Store whatever image is originally displayed in the
+ // ImageView as a local Bitmap object so that we can quickly
+ // reset the image when a button is pressed.
+ mCurrentBitmap = ((BitmapDrawable)(mImageView.getDrawable())).getBitmap();
+ mDefaultBitmap = mCurrentBitmap;
+
+ /**
+ * Turn off strict mode.
+ *
+ * Normally, if you try to do any networking from the main UI
+ * thread, the Android framework will throw an exception and
+ * stop working. However, part of this application uses a
+ * synchronous AIDL interface to demonstrate how to execute
+ * functions in services synchronously. For this purpose, we
+ * turn off strict mode so that the Android framework will
+ * work without complaining.
+ *
+ * Please note that this is for demonstration purposes ONLY,
+ * and you should NEVER, EVER turn off strict mode in
+ * production code. You should also not do networking
+ * operations on the main thread, because it might cause your
+ * application to crash.
+ */
+ StrictMode.ThreadPolicy policy =
+ new StrictMode.ThreadPolicy.Builder().permitAll().build();
+ StrictMode.setThreadPolicy(policy);
+ }
+}
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/src/edu/vuum/mocca/DownloadBoundServiceAsync.java b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/src/edu/vuum/mocca/DownloadBoundServiceAsync.java
new file mode 100644
index 000000000..6bc8a7c5a
--- /dev/null
+++ b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/src/edu/vuum/mocca/DownloadBoundServiceAsync.java
@@ -0,0 +1,80 @@
+package edu.vuum.mocca;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+/**
+ * @class DownloadBoundServiceAsync
+
+ * @brief This class handles downloads using asynchronous AIDL
+ * interactions. The component that binds to this service
+ * should receive an IBinder. This IBinder should be an
+ * instance of DownloadRequest, which extends IBinder. The
+ * component can then interact with this service by making
+ * normal calls on the DownloadRequest object. Specifically,
+ * the component can ask this service to download an image,
+ * passing in a DownloadCallback object. Once the download is
+ * finished, this service should send the pathname of the
+ * downloaded file back to the calling component by calling
+ * sendPath() on the DownloadCallback object.
+ *
+ * AIDL is an example of the Broker Pattern, in which all
+ * interprocess communication details are hidden behind the
+ * AIDL interfaces.
+ */
+public class DownloadBoundServiceAsync extends Service{
+ /**
+ * The concrete implementation of the AIDL Interface
+ * DownloadRequest. We extend the Stub class, which implements
+ * DownloadRequest, so that Android can properly handle calls
+ * across process boundaries.
+ *
+ * This implementation plays the role of Invoker in the Broker
+ * Pattern.
+ */
+ DownloadRequest.Stub mDownloadRequestImpl = new DownloadRequest.Stub() {
+ /**
+ * Download the image at the given Uri and return a
+ * pathname to the file on the Android file system by
+ * calling the sendPath() method on the provided callback
+ *
+ * Use the methods defined in DownloadUtils for code brevity.
+ */
+ @Override
+ public void downloadImage(Uri uri,
+ DownloadCallback callback)
+ throws RemoteException {
+ // TODO You fill in here to download the file using
+ // the appropriate helper method in DownloadUtils and
+ // then send the pathname back to the client via the
+ // callback object.
+ }
+
+ };
+
+ /**
+ * Called when a component calls bindService() with the proper
+ * intent. Return the concrete implementation of DownloadRequest
+ * cast as an IBinder.
+ */
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mDownloadRequestImpl;
+ }
+
+ /**
+ * Make an Intent that will start this service when passed to
+ * bindService().
+ *
+ * @param context The context of the calling component.
+ */
+ public static Intent makeIntent(Context context) {
+ // TODO - replace the null to create the appropriate Intent
+ // and return it to the caller.
+ return null;
+ }
+}
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/src/edu/vuum/mocca/DownloadBoundServiceSync.java b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/src/edu/vuum/mocca/DownloadBoundServiceSync.java
new file mode 100644
index 000000000..2df99d886
--- /dev/null
+++ b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/src/edu/vuum/mocca/DownloadBoundServiceSync.java
@@ -0,0 +1,79 @@
+
+package edu.vuum.mocca;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+/**
+ * @class DownloadBoundServiceSync
+ *
+ * @brief This class handles downloads using synchronous AIDL
+ * interactions.
+ *
+ * The component that binds to this service should receive an
+ * IBinder. This IBinder should be an instance of DownloadCall,
+ * which extends IBinder. The component can then interact with
+ * this service by making normal calls on the DownloadCall
+ * object. Specifically, the component can ask this service to
+ * download an image by calling downloadImage() on the
+ * DownloadCall object, which will run synchronously in this
+ * service until it finishes downloading and returns the file
+ * name of the downloaded file as a String.
+ *
+ * AIDL is an example of the Broker Pattern, in which all
+ * interprocess communication details are hidden behind the
+ * AIDL interfaces.
+ */
+public class DownloadBoundServiceSync extends Service {
+ /**
+ * An implementation of the AIDL Interface DownloadCall. We
+ * extend the Stub class, which implements DownloadCall, so that
+ * Android can properly handle calls across process boundaries.
+ *
+ * This implementation plays the role of Invoker in the Broker
+ * Pattern
+ */
+ DownloadCall.Stub mDownloadCallImpl = new DownloadCall.Stub() {
+ /**
+ * Download the image at the given Uri and return a
+ * pathname to the file on the Android file system.
+ *
+ * Use the methods defined in DownloadUtils for code
+ * brevity.
+ */
+ @Override
+ public String downloadImage(Uri uri) throws RemoteException {
+ // TODO You fill in here to replace the null and
+ // download the file using the appropriate helper
+ // method in DownloadUtils and then return the
+ // pathname back to the client.
+ return null;
+ }
+ };
+
+ /**
+ * Called when a component calls bindService() with the proper
+ * intent. Return the concrete implementation of DownloadCall
+ * cast as an IBinder.
+ */
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mDownloadCallImpl;
+ }
+
+ /**
+ * Make an Intent that will start this service when passed to
+ * bindService().
+ *
+ * @param context The context of the calling component.
+ */
+ public static Intent makeIntent(Context context) {
+ // TODO - replace the null to create the appropriate Intent
+ // and return it to the caller.
+ return null;
+ }
+}
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/src/edu/vuum/mocca/DownloadCall.aidl b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/src/edu/vuum/mocca/DownloadCall.aidl
new file mode 100644
index 000000000..f4ca92ec4
--- /dev/null
+++ b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/src/edu/vuum/mocca/DownloadCall.aidl
@@ -0,0 +1,15 @@
+package edu.vuum.mocca;
+
+/**
+ * @class DownloadCall
+ *
+ * @brief An AIDL interface used to download an image in another
+ * process. Any invocations of downloadImage() will block until
+ * the function completes and returns from the other process.
+ *
+ * This file generates a java interface in /gen
+ */
+interface DownloadCall {
+ // Download the image at the given web uri then return a string to its location on the file system.
+ String downloadImage (in Uri uri);
+}
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/src/edu/vuum/mocca/DownloadCallback.aidl b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/src/edu/vuum/mocca/DownloadCallback.aidl
new file mode 100644
index 000000000..b2b0a7b04
--- /dev/null
+++ b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/src/edu/vuum/mocca/DownloadCallback.aidl
@@ -0,0 +1,14 @@
+package edu.vuum.mocca;
+
+/**
+ * @class
+ *
+ * @brief An AIDL Interface used for receiving results from a call to
+ * DownloadRequest.downloadImage()
+ *
+ * This file generates a java interface in /gen
+ */
+interface DownloadCallback {
+ // Send the location of a downloaded file on the file system back to the caller
+ oneway void sendPath (in String filePath);
+}
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/src/edu/vuum/mocca/DownloadRequest.aidl b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/src/edu/vuum/mocca/DownloadRequest.aidl
new file mode 100644
index 000000000..5b9745cf1
--- /dev/null
+++ b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/src/edu/vuum/mocca/DownloadRequest.aidl
@@ -0,0 +1,21 @@
+package edu.vuum.mocca;
+
+import edu.vuum.mocca.DownloadCallback;
+
+/**
+ * @class DownloadRequest
+ *
+ * @brief An AIDL interface for downloading an image from another
+ * process. The caller should provide a DownloadCallback object
+ * so that the downloading process can return a result across
+ * process boundaries asynchronously.
+ *
+ * This file generates a java interface in /gen
+ */
+interface DownloadRequest {
+ // Download an image at the given uri and store it on the file system.
+ // When finished, call the appropriate method on the callback object to
+ // send the downloaded file's file name on the file system.
+ oneway void downloadImage (in Uri uri,
+ in DownloadCallback callback);
+}
diff --git a/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/src/edu/vuum/mocca/DownloadUtils.java b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/src/edu/vuum/mocca/DownloadUtils.java
new file mode 100644
index 000000000..dc0c93fe9
--- /dev/null
+++ b/grading-drivers/week-8-assignment-7/W8-A7-ThreadedDownloads-BoundServices/src/edu/vuum/mocca/DownloadUtils.java
@@ -0,0 +1,164 @@
+package edu.vuum.mocca;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+
+import edu.vuum.mocca.R;
+
+import android.content.Context;
+import android.net.Uri;
+import android.util.Base64;
+import android.util.Log;
+
+/**
+ * @class DownloadUtils
+ *
+ * @brief This class encapsulates several static methods so that all
+ * Services can access them without redefining them in each
+ * Service.
+ */
+public class DownloadUtils {
+ /**
+ * Used for debugging.
+ */
+ static final String TAG = "DownloadActivity";
+
+ /**
+ * If you have access to a stable Internet connection for testing
+ * purposes, feel free to change this variable to false so it
+ * actually downloads the image from a remote server.
+ */
+ // TODO - You can change this to the appropriate setting for your
+ // environment.
+ static final boolean DOWNLOAD_OFFLINE = true;
+
+ /**
+ * The resource that we write to the file system in offline
+ * mode. Note that this must be the same image that the testing
+ * project expects. (found in res/drawable-nodpi and Options.java)
+ */
+ static final int OFFLINE_TEST_IMAGE = R.raw.dougs;
+
+ /**
+ * The file name that we should use to store the image in offline mode
+ */
+ static final String OFFLINE_FILENAME = "dougs.jpg";
+
+ /**
+ * Download the file located at the provided internet url using
+ * the URL class, store it on the android file system using
+ * openFileOutput(), and return the path to the file on disk.
+ *
+ * @param context the context in which to write the file
+ * @param uri the web url
+ *
+ * @return the path to the downloaded file on the file system
+ */
+ public static String downloadFile (Context context,
+ Uri uri) {
+
+ try {
+
+ // If we're offline, write the image in our resources to
+ // disk, then return that pathname.
+ if (DOWNLOAD_OFFLINE) {
+
+ // Store the image on the file system. We can store it
+ // as private since the test project runs in the same
+ // process as the target project
+ FileOutputStream out =
+ context.openFileOutput(OFFLINE_FILENAME, 0);
+
+ // Get a stream from the image resource
+ InputStream in =
+ context.getResources().openRawResource(OFFLINE_TEST_IMAGE);
+
+ // Write the resource to disk.
+ copy(in, out);
+ in.close();
+ out.close();
+
+ return context.getFilesDir().toString() + File.separator + OFFLINE_FILENAME;
+ }
+
+ // Otherwise, go ahead and download the file
+ else {
+ // Create a temp file.
+ final File file = getTemporaryFile(context,
+ uri.toString());
+ Log.d(TAG, " downloading to " + file);
+
+ // Download the contents at the URL, which should
+ // reference an image.
+ final InputStream in = (InputStream)
+ new URL(uri.toString()).getContent();
+ final OutputStream os =
+ new FileOutputStream(file);
+
+ // Copy the contents of the downloaded image to the
+ // temp file.
+ copy(in, os);
+ in.close();
+ os.close();
+
+ // Return the pathname of the temp file.
+ return file.getAbsolutePath();
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Exception while downloading. Returning null.");
+ Log.e(TAG, e.toString());
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ /**
+ * Create a temp file to store the result of a download.
+ *
+ * @param context
+ * @param url
+ * @return
+ * @throws IOException
+ */
+ static private File getTemporaryFile(final Context context,
+ final String url) throws IOException {
+
+ // This is what you'd normally call to get a unique temporary
+ // file, but for testing purposes we always name the file the
+ // same to avoid filling up student phones with numerous
+ // files!
+ // return context.getFileStreamPath(Base64.encodeToString(url.getBytes(),
+ // Base64.NO_WRAP)
+ // + System.currentTimeMillis());
+
+ return context.getFileStreamPath(Base64.encodeToString(url.getBytes(),
+ Base64.NO_WRAP));
+ }
+
+ /**
+ * Copy the contents of an InputStream into an OutputStream.
+ *
+ * @param in
+ * @param out
+ * @return
+ * @throws IOException
+ */
+ static public int copy(final InputStream in,
+ final OutputStream out) throws IOException {
+ final int BUFFER_LENGTH = 1024;
+ final byte[] buffer = new byte[BUFFER_LENGTH];
+ int totalRead = 0;
+ int read = 0;
+
+ while ((read = in.read(buffer)) != -1) {
+ out.write(buffer, 0, read);
+ totalRead += read;
+ }
+
+ return totalRead;
+ }
+}