This lab will be a continuation of Lab 2. You will expand on your knowledge of the Android user interface library. It is important to note that this lab is meant to be done in order, from start to finish. Each activity builds on the previous one, so skipping over earlier activities in the lab may cause you to miss an important lesson that you should be using in later activities.
Objectives
At the end of this lab you will be expected to know:
For this lab we will be extending the "Joke List" application that you created in Lab2. This version of the app will be more advanced. It will allow the user to give ratings to Jokes, delete Jokes, upload Jokes to a server, and download Jokes from a server. All tasks for this lab will be based off of this application. Over the course of the lab you will be iteratively refining and adding functionality to the Joke List app. With each iteration you will be either improving upon the previous iteration's functionality, or you will be implementing the same functionality in a different way.
IMPORTANT:
You will be given a Skeleton Project to work with. This project contains all of the java and resource files you will need to complete the lab. Some method stubs, member variables, and resource values and ids have been added as well. It is important that you not change the names of these methods, variables, and resource values and ids. These are given to you because there are unit tests included in this project as well that depend on these items being declared exactly as they are. These unit tests will be used to evaluate the correctness of your lab. You have complete access to these test cases during development, which gives you the ability to run these tests yourself. In fact, you are encouraged to run these tests to ensure that your application is functioning properly.
1. Setting Up...
1.1 Creating the Project
To begin, you will need to download and extract the skeleton project for the JokeList application.
Next you will need to setup a "Joke List" Android project for this app. Since the skeleton project was created in Eclipse, the easiest thing is to import this project into Eclipse.
1.2 Fill in the Joke Class
You may fill in the Joke Class using the functionality that you implemented for this class in Lab2. However, there is one key difference. A member variable named m_strAuthorName has been added to the class which will contain the name of the Joke's Author. In particular:
Run the JokeTest.java Unit Tests to ensure that you have properly filled in this class.
1.3 Use SimpleJokeList as a Starting Point
You may fill in the AdvancedJokeList class using some of the code that you implemented for SimpleJokelist in Lab2:
2 Declaring Static Layouts in XML
Read the Android Developer Guide on Declaring Layout for complete background on declaring layouts. Declaring your user interface in XML is the preferred method of implementation. By declaring your UI in an XML resource file it gives you better separation between the presentation layer of your application and the code controlling things underneath. One benefit of this is that modifications to your UI can be made without having to change any source code or recompile. This allows you to define different views for different screen sizes, resolutions, and scenarios while using the same code to control everything.
2.1 Porting Your Dynamic Layout Into Static XML
In order to get some practice with setting up layouts in XML, you will begin by converting the layout you setup dynamically in SimpleJokeList to an XML layout file. You will then inflate this layout in AdvancedJokeList and set it as your ContentView.
2.2 Building Custom UI Components
Sometimes the standard View library will not supply the functionality that you need. In situations like this it is completely acceptable to define your own UI Components. There are three general approaches to creating custom UI components:
In this section you will be using the third approach to develop a custom component. You will combine a number of different existing View classes to create a coherent Widget for displaying Jokes. For a complete background on this approach, as well as the other two approaches, read the Android Developer Guide on Compound Controls.
The custom component that you are going to implement will have two states, an expanded and a collapsed state. It will look something like this:
In the collapsed state there is an expand/collapse Button that displays a "+" and a joke TextView displaying the first two lines of the joke. If the joke is longer than two lines, it will only display two lines and append to the displayed text an ellipsis.
In the expanded state there exist the exact same components that existed in the collapsed state. Additionally, there will exist a RadioGroup containing two RadioButtons, that will appear horizontally centered underneath the expand/collapse Button and joke EditText. In the expanded state, the expand/collapse Button will display a "-" and the joke EditText will no longer display an ellipsis, but rather display the entire text of the joke (no matter how long it is). The expand/collapse button should remain anchored to the top left corner; it should not be centered vertically.
2.2.1 Declare a Custom JokeView XML Layout
The first step is to create the XML layout file that the custom component will use. You have to implement this custom component using a single XML layout file. Switching between the collapsed and expanded states is only a matter of hiding the RadioGroup, changing the Button text, and changing settings on the TextView.
2.2.2 Create a Custom JokeView Widget
The next step is to implement your custom component class. This class will be the JokeView class. It is your task to fill in JokeView.java that has been stubbed out for you. In general when creating a compound component, after you have established your layout, you want your component class to extend the class of the root ViewGroup in your layout. Your component class then becomes a special subclass of that ViewGroup.
Open up JokeView.java:
LayoutInflater inflater = (LayoutInflater)context.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.joke_view, this, true);
2.2.3 Make AdvancedJokeList use the JokeView class
The last step is to update AdvancedJokeList to make use of your new JokeView custom component.
3. Adapters & AdapterViews
The purpose of this next section is to introduce you to the concept of AdapterViews. An AdapterView is a View class that allows us to bind it to a dataset. This binding then takes care of responding to user selections as well as populating the AdapterView with data. The binding is performed by a third intermediate class, called an Adapter. It is the Adapter that is responsible for keeping track of the selection and supplying the AdapterView with a View object representation of each item in the dataset. Read the Android Developer Guide on Binding to Data with AdapterViews for a complete background on the topic.
In the context of this section, the AdapterView is a scrollable vertical ViewGroup called a ListView. The dataset is then our ArrayList of Joke objects. The Adapter class is the JokeListAdapter, which follows the standard Object Adapter Design Pattern; read the wiki on Object Adapter for more information. JokeListAdapter contains a reference to our list of Joke objects and supplies ListView with a JokeView for each them.
3.1 Implement JokeListAdapter.java
Begin by filling in the constructor and the stubbed getSelection() method:
Make the JokeListAdapter class extend the BaseAdapter class. Check the Android Documentation on BaseAdapter for details. You will have to add and implement the following abstract methods:
3.2 Make AdvancedJokeList Activity Use ListView
You will now make the AdvancedJokeList Activity class use the ListView and the JokeListAdapter classes to maintain your list of Jokes. You can read the Android Documentation on ListView for details on the class.
3.3 Enable ChoiceMode on ListView
The ListView class provides an API for maintaining a selected, or chosen view. It does this in much the same way that a RadioGroup maintains a chosen RadioButton. When you select a RadioButton, the previously chosen RadioButton becomes un-chosen. ListView has the ability to do this as well for the view objects it contains.
In this section, you will enable the ChoiceMode on ListView, so that only one JokeView can be in the expanded state at a time. The JokeView that is in the expanded state will be the chosen View. When you click on another JokeView, the previously chosen JokeView will collapse, and the clicked JokeView will expand. In order for this to work, the Views that the ListView contain must implement the Checkable interface. Read the Documentation on the Checkable interface to understand how to implement it.
android:focusable="false"
4 Menus
This section is devoted to working with Menus. Read the Android Developer Guide on Menus to get a good overview on the Android Menu system. In short, there are two different types of Menus. The first type of Menu is the Options Menu that can be brought up when the user hits the Menu button. Each Activity has the ability to declare its own Options Menu.
The second type of Menu is the Context Menu. Context Menus can be assigned to different Views and thus change depending on which View has focus. Hence the Menu depends on the Context in which it was triggered. Context Menus are brought up when the user holds the center D-pad button, clicks the trackball, or long-touches the screen for a few seconds when a particular View has focus.
In the two subsections that follow you will use both types of Menus. First, you will create a Context Menu for the ListView that will allow you to delete a JokeView. Then you will add an Options Menu to the AdvancedJokeList Activity that will allow users to filter the Jokes that are displayed by their rating.
4.1 Adding a "Delete Joke" Context Menu
When a user long-touches a JokeView, a Context Menu displaying a single MenuItem should be displayed. The MenuItem should simply display the text "Remove". When the Remove MenuItem is selected, the JokeView that was long-touched should be removed from the screen and from the list of Jokes.
There are two ways to create Context Menus. The first is to have a particular View, like our ListView, be responsible for creating the Context Menu. The second and more common way is to have the Activity create the Context Menu, and then register the ListView to use the Context Menu. In this section, you will be taking the latter approach.
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)
4.2 Adding a "Filter Jokes" Options Menu
Your final menu task is to implement an Options Menu that contains a Submenu entitled "Filter". The Submenu will have four radio button options that allow the user to choose which Jokes to display. The radio button options are:
On startup, the activity should show all jokes by default. To simplify things, the Filter functionality need only filter the jokes that are currently displayed. For example, let's say the user has selected the Like filter option so that only jokes with a rating of Joke.LIKE are displayed. Then the user adds a new Joke which will have a rating of Joke.UNRATED by default. This Joke will still be displayed. Similarly, if the user changed one of the Joke's ratings from Joke.LIKE to Joke.DISLIKE, this Joke will still be displayed. Thus, the filter does not need to monitor the addition of new Jokes, or changes in Joke ratings. (Click on the image below for an example story board Use Case)
IMPORTANT Additional Requirements:
You are tasked to do this on your own.
Hints:
5. Establishing Http Connections
Http connections are immensely useful for transferring data between a mobile device and a server. Such connections can be used by mobile devices for retrieving information as well as sending data back to a server. For the last section of the lab, you will be implementing functionality to share your jokes with the rest of the class. By making Http Requests, you will connect with a server to upload your own jokes and download other people's jokes.
The Android framework doesn't supply its own API for establishing connections to the internet. Instead, it includes both of the java.net and org.apache.http packages. The java.net.URL class provides a very clean and simple interface for creating an Http connection and retrieving a response. The apache.http package provides a more robust collection of classes and is not as straightforward to use.
5.1 Uploading Jokes to the Server
You will begin by adding an "Upload Joke" MenuItem to the Context Menu that gets displayed when a user long-presses/long-touches a joke. When the user selects the "Upload Joke" MenuItem, your application should send the text of the joke and the name of the Author to a server. The server will then send back a response indicating whether the joke was recieved. Your application should then notify the user that the upload succeeded or failed via a Toast Notification.
5.1.1 Update Your Manifest
Accessing the Internet requires your application to have permission to use the internet. Your application must declare in its manifest that it uses the internet. When the application gets installed, the user is presented with a list of permissions that the application says it will use. At this point, the user will have the option to grant the application these permissions, or deny these permissions and cancel the installation.
Add the following line to your AndroidManifest.xml file:
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
It should be nested between the root <manifest></manifest> tags, one level deep, at the same level as your <application></application> tags. It should look something like this:
<manifest ... >
...
<application ...>
...
</application>
...
<uses-permission android:name="android.permission.INTERNET">
</uses-permission>
...
</manifest>
5.1.2 Add "Upload Joke" Context MenuItem
In your onCreateContextMenu(...) method:
5.1.3 Fill in "uploadJokeToServer(Joke joke)" Method
You will have to test for and catch any Exceptions that are thrown by using classes from the java.net package. You don't have to do anything special should an exception be thrown, just exit the method gracefully. Feel free to print out whatever exception information you feel may be of use. This would be a great time to use Log.
Begin by constructing a string that will contain the complete URL you will use to submit your Joke to the server:
http://simexusa.com/aac/addOneJoke.php?joke=This+is+my+joke&author=reed
Make your Http connection and read the response from the server:
Test the response and notify the user of success or failure:
Run your application and ensure that you can successfully upload jokes to the server.
5.2 Downloading Jokes from the Server
Your final task is to add a "Download Jokes" Options MenuItem. When the MenuItem is clicked your application will download all the jokes from a server and display them in the list of jokes. You don't have to display the progress indicator dialog box that is shown in the screen shots below.
5.2.1 Add "Download Jokes" Options MenuItem
Add a "Download Jokes" MenuItem to your menu.xml layout file:
5.2.2 Fill in "getJokesFromServer()" Method
Construct a new URL object:
Parse the response:
Run your application and ensure that you can successfully download jokes from the server.
6. Deliverables
To complete this lab you will be required to:
Primary Author: James Reed
Adviser: Dr. David Janzen