DCC052 - Project Assignment


Introduction

This class contains a project assignment that is divided into eight parts. This assignment consists in developing a small phone-book to the Android platform. This is an incremental project, divided into eigth parts. Some parts depend on others. The dependence graph is given below. Notice that this is not a linear dependence graph:

Homework 1

Number of partners: students are allowed to work in pairs. Bigger groups are not accepted.

Grading: the whole project is worth 50 points. Each part of the homework is worth 7.5 points. Only the seven biggest grades will be computed; thus, you can make up to 52.5 points. If you turn in all the parts, then you will automatically get the 2.5 extra points, or as much as it takes for you to fill up your quota of extra points. Notice that you can accumulate a total of 2.5 extra points in this assignment.

Delays: delays are penalized according to the following formula, where n is the number of late days:

static int computePenalty(int n) {
  int penalty = 0;
  for (int i = 1; i <= n; i++) {
    penalty += i*i
  }
  return penalty;
}
Notice that if you are three days late, you lose 14 points, which is more than the 7.5 points that each home-work is worth.

Objectives: the academic objectives of this assignment are twofold:

Android is an operating system that runs on top of cell phones and other small mobile computers. This platform is sponsored by an international consortium, the Open Handset Alliance. Android provides to application developers a Java based programming API. The best and quickest way to get into Android is through the tutorials, available in the platform website. In particular, you should try the Hello, World! and the Notepad tutorials.

Turning the project in

Android applications have entry points called activities. Each part of this homework will have a main activity called TPX, where X is the homework number. For instance, here is the Eclipse dialog box right before creating the project for the sixth part of the homework.

Each assignment must be developed as an eclipse project. In order to enforce good coding practices, you must use the checkstyle plug-in for Eclipse. The project must be sent throw e-mail, as an attached zip file. The e-mail must be sent to rodrigosol+dcc052@gmail.com. The e-mail must have the following subject: [DCC052]:[Num]:[XXXXXXXXX_YYYYYYYY], where NUM is the part number, i.e, 1, 2, 3, ..., 8, and XXXXXXXXX_YYYYYYYY are the IDs of the group members. Example: [DCC052]:[2]:[2008457623_2008189732]. You should not send any written documentation other than the comments in the Java code. To turn the project in, follow these instructions:

  1. Click File
  2. Click Export
  3. Choose General
  4. Chose File Archive
  5. Write the following archive name: hw(number)_(studentName1)_(studentName2).zip. For instance: hw1_Juan_Alejandro.zip.
  6. Choose Save in ZIP format.
  7. Choose Create directory structure.
  8. Click OK.

What do to

This project is divided into eigth parts. At the end of the eighth step, you will have developed a full-blown Android application. We will be developing a phone-book, that associates names to phone numbers. You can also assign one or more labels to each entry, like gmail does, for instance. Each part of the homework is described below:

  1. Step 1 Set up of the Android development environment, and kick start the application. You will have to display the contents of a list on the cell-phone.
    Deadline:August 15th, 11:59pm (Notice that "pm" means 23:59)
    Solution: TP1
  2. Step 2 You will develop the data structure that we will use to store names, phone numbers and labels.
    Deadline:August 29th, 11:59pm
    Solution: TP2
  3. Step 3 You will add an interface to create new entries and store them into the phone book.
    Deadline:September 12th, 11:59pm
    Solution: TP3
  4. Step 4 You will add interfaces to manipulate labels, that is, to add or remove labels from entries.
    Deadline:October 3th, 11:59pm
    Solution: TP4
  5. Step 5 You will explore techniques to avoid the duplication of data in the application.
    Deadline: October 24th, 11:59pm
    Solution: TP5
  6. Step 6 You will code a small search engine that allows you to browse the entry list by the labels they have.
    Deadline: November 7th, 11:59pm
    Solution: TP6
  7. Step 7 You will create a small testing framework to check the performance of your application.
    Deadline: November 21st, 11:59pm
    Solution: TP7
  8. Step 8 You will add a small interface to sort the list of entries according to different criteria.
    Deadline: December 5th, 11:59pm
    Solution: TP8

First part

The objective of this first part of the homework is to get the student acquainted with Android. To accomplish this, the student will have to show the contents of a list into the screen of the cell phone device. Each element of the screen is an instance of the Entry class. Entries encode a person's name, and a telephone. Entries contain, also, a list of labels, that are related to that person. Labels are strings, used to group people who are somehow related. This is similar to the labels that we can use in gmail to group our friends, and separate them from our work-mates, club-mates, theater-mates, etc, etc.

To make things easier, I have prepared some classes for you:

Now, all that you must do is to code an android activity to display the list that you obtain through the call EntryMaker.getEntries(). If you did the right job, your application will show the screen below:

Homework 1 Screenshot of the solution of the first part of the assignment.
Click on the image to see the larger version.

Second part

The objective of the second part of this homework is to develop the data structure that will be the back-bone of our application. This data structure, that you will implement in a class called NameDirectory, is a directory of entries that keeps track of the list of labels associated to each entry. This class must implement this API. In order to test your class, augment it with a simple initialization method, such as the one below:

  private void stubInit() {
    add("Person A", "12223330000");
    add("Person B", "12223330001");
    add("Person C", "12223330002");
    try {
      addLabel("Person A", "work");
      addLabel("Person B", "school");
      addLabel("Person B", "work");
      removeLabel("Person A", "work");
      addLabel("Person A", "club");
    } catch (NameNotFoundException nnf) {
      nnf.printStackTrace();
    } catch (LabelNotFoundException lnf) {
      lnf.printStackTrace();
    }
  }

One important aspect of any implementation of a data structure is how it handles errors. Our name directory should be able to handle three possible errors. Each of the following conditions should raise an exception. You are free to define any exceptions that you need:

Homework 1 You must code an activity to test your data structure. Call it TP2. If you use the method above to do the initialization, then you should get the screen on the left.
Click on the image to see the larger version.

Third part

In the third part of the assignment, you will code a way for the user to add entries into the phone book. In order to do this, you must perform a number of tasks:

Homework 1 Upon starting, your application should look more or less like this. Notice that I am not displaying the list of available names right from the start. In order to see this list, the user will have to click on the view item in the menu.
Click on the image to see the larger version.
Homework 1 This is a screenshot of the menu that you have to create. The menu appears as soon as the user click the menu button on the bottom of the cell device.
Click on the image to see the larger version.
Homework 1 This is a screenshot of the cell phone after the user hits the view item in the menu. You can add some entries on the constructor of the NameDirectory class in order to populate it with test entries.
Click on the image to see the larger version.
Homework 1 This is a screenshot of the interface to add new entries into the application. You do not have to necessarily use the same interface, but this is a good model to start. I have added two text fields and a confirm button.
Click on the image to see the larger version.
Homework 1 After inserting the data that I am showing in the previous screen, your list will have to look like this. See that I have now a new entry: Fernando: 3134960012.
Click on the image to see the larger version.

One important issue that you must sort out is how to allow communication between the two activities that make up your application. Names are collected by the activity AddrActivity, but they are shown by the TP3 activity. So, some way to pass data from one activity to the other is in order here. I recommend the following: re-code the class NameDirectory so that it will be a singleton. By making it completely static, all its methods are visible across the whole application. You can insert data directly on this class inside the AddrActivity, and you can read this class inside the TP3 activity. Of course, this is just a suggestion. You do not have to necessarily listen to me.


Fourth part

In the fourth part of the project you will have to code a way to allow the user to manipulate labels. There are two possible things that the user should be able to do:

Notice that an Entry will need to know how to manipulate labels. You will have to change its interface to something more like this.

We are going to add this new function to our application via a context menu. A context menu is that kind of menu that appears when you select some particular item of your interface. In our case, the context menu will pop up if we click on some entry in the list, and hold the mouse button for a while. Our context menu will have two items:

Homework 1 Continuing with our example, from the third release, let's assume that your phone list contains the entries shown in the left.
Click on the image to see the larger version.
Homework 1 If you click on some name in the list, and hold the mouse down, then a context menu should pop up. Notice that it will only come out if you click on the list. The context will refer to the item of list where you just clicked.
Click on the image to see the larger version.
Homework 1 Let's assume that you have clicked on the second name in the list. This entry corresponds to Person B, phone 12223330001. If you choose the menu to add a new label, then this is the screen where you will be redirected. Notice that I am writing the name on top of the screen, so that the user will be sure about the name that he is binding to a label.
Click on the image to see the larger version.
Homework 1 After adding the new label Soccer to Person B, your screen should look like this one on the left.
Click on the image to see the larger version.
Homework 1 If you click on the choose the menu to remove a label, then you must open a new screen, where you display a list of all the labels related to the item where you just clicked. In our example, let's assume that I have clicked on Person B. In this case, I have the following screen.
Click on the image to see the larger version.
Homework 1 Upon clicking in one of the list items in the screen to remove labels, you remove that label from the item. In this example, I end up clicking on the label School, that, as you can see, is no longer in the list.
Click on the image to see the larger version.

Fifth part

You know: some people have more than one telephone. The boss may have a home phone, and an office phone, plus a cell phone, and so forth. But, it would be a pain if we had to re-type all the data of someone, every time we want to add a new telephone for that person. In this part of the homework we will be fixing this. Starting from the fourth part of the project, do the following:

The main objective of this part of the homework is to be able to avoid the replication of redundant data. After the additions above, we can have many entries sharing the same address and date of birth. An efficient use of resources would avoid creating a bunch of data that represent the same information. One way of doing this is by using the flyweight design pattern. Of course, in computer science, just like in any other science, the limit is our creativity. Perhaps it is possible to avoid the replication using other strategy. Your TP5 activity should have a header comment describing what is the strategy that you have used to avoid the replication of redundant data.

Homework 5 Continuing with our example, let's assume that we start with a name directory containing the following entries, produced by this initialization code.
Click on the image to see the larger version.
Homework 1 If you click on some name in the list, and hold the mouse down, then a context menu should pop up. Notice that it will only come out if you click on the list. The context will refer to the list item where you just clicked. Now, we have two more entries in our pop-up. The entry +phone adds a new telephone to an existing entry. The entry view visualize all the data of the selected entry.
Click on the image to see the larger version.
Homework 1 Clicking the view pop-up item you will be able to see all the information related to a particular entry. If you click the ok button, then you should be taken back to the main list view, which shows all the entries.
Click on the image to see the larger version.
Homework 1 Clicking on the +phone pop-up item, you should go to an interface like this, where you can add a new telephone, as well as a new brief description to the existing entry. Notice that you must also add a new name to this entry, as names are used as keys in the implementation of NameDirectory.
Click on the image to see the larger version.
Homework 1 After clicking the Confirm button you go back to the main activity, that shows you the list of entry names. As you can see, the new entry has been added. In order to test if you did the right thing, try visualizing all the information about Person A office. You will see that, even though you did not enter any address or date of birth, the same address and DoB of the original entry (Person A) is used in the new one.
Click on the image to see the larger version.
Homework 1 Notice that we keep the same menu view as in the fourth part of the homework. The +addrs item takes you to an interface where you can create new entries, and the view item takes you to the main list view, where you can visualize an abridged version of all the entries.
Click on the image to see the larger version.
Homework 1 Differently from the fourth part, now, the creation of an entry includes many more items. In this simple interface I am assuming for the Birth item the Brazilian date format: day, month, year. Of course, you can be nicer, and label the boxes with the date divisions.
Click on the image to see the larger version.

Sixth part

If you came up to this point, then you did a good job, but there is still a lot to do! You may be wondering what are those labels good for. So, let me tell you: they help the user to organize their phone list, and, more important, they help the young Fernando Magno to play the devil in the homework. So, in this part of the project we will be coding a search engine that operates on the labels. This part of the homework does not depend on the fifth part, but it depends on the fourth. You can start from either part. I will start from the fourth. If you want to have a really functional application, start from the fifth. A modular design should make it easy to choose either approach.

Let's start by creating a GUI to read query strings. A query string is a description of the pattern of labels that must be searched for. For instance:

The main objective of this part of the homework is to code a simple and elegant program that handles the searches. It is important to keep the Single Responsibility Principle in mind in this part of the homework. The class that does the parsing, breaking a query string into some simpler, more understandable units, should not be the same class that does the search itself, and, ideally, none of these classes should have anything to do with the GUI that receives the queries.

Another question to bear in mind is how to do the search. Queries are somehow composable, e.g, we can combine two sub-queries into a bigger OR query, and then we can combine this query with another one, and so on. Notice, however, that in this case, the patter of queries is very simple: if we think in a query as a tree, we will see that it has only two levels:

Homework 5

Some use cases of this homework are listed below:

Homework 6 In this example, let's assume our old and good initial configuration of three names, with the following label set.
Homework 6 Start the homework by creating a new menu item, that I called search. This menu, upon receiving a click, will send you to the query activity.
Homework 1 The query activity has three components: a non-editable text field that shows you all the labels available in the application, a text box that let's you to enter a query, and the ok button, that fires up search.
Homework 1 If we send a query with the string work | club, then we end up getting this list of answers, given our initial configuration. To view the full list of names, just click on the view menu again.
Homework 1 Let's try, just for the sake of it, a different query. This time, we want to find all the entries that contain the label work, as well as the label school.
Homework 1 In this case, we only have one entry in our list of answers.

Seventh part

You may have realized that there are many ways to optimize queries. That is, if we try to search for a pattern of labels like L0 & L1 & L0 or if we search for L0 & L1 we are getting the same answer. In this assignment you will optimize this; however, we must implement a way to test our optimization first.

The optimization works if optimized queries run faster than the non-optimized stuff. Thus, we need a way to test the timing of our optimizations. With this purpose, you will have to implement a random entry generator. There are many ways of doing this. For instance, you can produce entries which have numbers for names, and labels chosen from a random set of pre-defined labels. A name, in this case, my be something like: "229851062 5. 7192718331", and Mr. 7192718331" might be associated to the set of labels {L0, L2, L3, L6, L8, L9}. We just need a way to produce many, many, many different entries, with the property that these entries may share a substantial amount of labels. Once you have a generator of random entries, use it to populate your name directory. Try using different quantities of entries. The Android emulator is not very robust performancewise, and you will see that as you grow the number of entries, and the size of the pool of labels, the machine will take longer and longer to start up.

Once you have coded the random entry generator, it is time to implement an optimizer. Your optimizer will perform only two kinds of optimizations:

  1. Elimination of redundancies from AND expressions: that is, given an expression such as L0 & L1 & L0, you should be able to produce L0 & L1.
  2. Elimination of simple redundancies from OR expressions: that is, given L0 & L1 | L0 & L2 | L0 & L1 | L0 & L2, you should be able to deduce L0 & L1 | L0 & L2. Notice that I want something very simple here. For instance, from L0 & L1 & L2 | L0 & L1 we could deduce L0 & L1 (but not L0 & L1 & L2!). However, you do not have to implement this kind of optimization. I mean, it will be nice if you do, but you can stick to the old and good "remove expressions only if they are the same" rule. Yet, from L0 & L1 | L1 & L0 you should be able to find L0 & L1, in spite of the different ordering of the labels.

One important question is: how to tell the application that it must optimize the query? Well, you could add something in the interface of the query activity to indicate this, but we can make it simpler. We only want to test our optimizer, so we can use a stub in this case. Thus, let's use the following convention: if the query starts with a star (*), then it should be optimized.

Use the Log class to dump time reports. You can get timing information via the System.currentTimeMillis method.

You must code your implementation with some concerns in mind:

  1. Did you have to change the implementation of TP6 too much? Was it possible to use the same data-structures that you had used to implement the search? In case you had to change those data-structures, how were these changes implemented?
  2. How much reuse happened between the AND optimization and the OR optimization? Did you notice that they are very similar?
  3. If I ask for a new optimization, how hard that will be to implement? For instance, imagine that instead of having a flat search tree, with only two levels, I add parentheses to the query language, and you might have to put up with strings like L0 & (L1 | L2). Would it be possible to reuse your optimizer to implement factorization, i.g L0 & L1 | L0 & L2 = L0 & (L1 | L2)?

Make sure that your TP7.java file contains a header comment providing answer to all the questions above. Additionally, the header comment should contain time reports for the following queries, in optimized and non-optimized mode, given labels from the set {L0, L1, L2, L3, L4, L5, L6, L7}, and a pool of 2000 random entries:

Below I have listed some use cases of this application:

Homework 7 Here you see the initial list of entries. I am using a random entry generator to produce 2000 entries with labels randomly chosen from a pool of eight labels. Notice that we must choose the number of labels of the entry randomly, and then choose that number of random labels for it.
Homework 7 You will see that the garbage collector will be doing a lot of work now. I am showing the log screen of my Eclipse. To see the log window, in Eclipse, try the following sequence of menus: Window, Show View, Other..., Android, LogCat.
Homework 7 Let's now try a non-optimized query. Notice that I am using the convention of prefixing the queries with a star symbol (*) whenever I want the optimizer to kick in. Given that I have no star in this string, it will not be optimized.
Homework 7 And now, let's see how we do with an optimized query. Your implementation should be able to apply the optimizer every time it finds the start as the first character in the query. Notice that in the end product we would not give this functionality to the user. We would simply use the optimizer every time, and there would be no need for the star. Here, as we are only testing the software, we can afford ourselves the luxury of being crude.
Homework 7 And to finish it, let's take a look into the log window. In my implementation I am printing the query before (FERNANDO - TP7) and after (Fernando - ES) the optimizer does its job. Notice that in the non-optimized case I got 604ms, and in the optimized try, I end up with 448ms.

Eighth part

So, if you came till this part, you may enjoy yourself: this is the last part of our homework. No worries, it is one of the easiest too! You will start from Part Five of the homework. So, to be done with this part, you will need the entries augmented with address, birthdate and description. In this phase, we will give the user the possibility of sorting the entries according to three criteria:

You will have to implement an interface where the user can choose one of the sorting criteria. Use a spinner to build the interface. You can learn about spinners in the tutorial. Once the user chooses a criterion, you must exhibit the list of entries according to the chosen ordering.

As you see, this part of the homework is very easy, but you must code your implementation with two concerns in mind:

  1. How hard is to add a new sorting criterion to the application? Does your code abide to the Open-Closed Principle? Which design patterns have you used to guarantee that your code is open for extension? Your TP8.java file should contain a header comment describing how to add a new sorting criterion to the application.
  2. Do you re-use the code to sort the entries? Sorting is a general enough algorithm that you could reuse. A good programmer would never code the sorting algorithm more than once in this application. Add a header comment to your TP8.java file describing the mechanisms that you have employed to reuse the sorting algorithm.

Below I have listed some use cases of this application:

Homework 8 Let's assume for the sake of this example, that we start with this list of entries.
Homework 8 Now, our front menu contains a third item, sort, which takes you to the interface where you choose a sorting criterion.
Homework 8 In the sorting window you have a spinner that let's you choose one out of three sorting options. By hitting the Confirm button you should be taken back to the front view, where the application will display the list of entries sorted according to the given criterion.
Homework 8 Here you see the spinner after we hit the option box. Normally Android pops-up the spinner options.
Homework 8 We have chosen to sort the entries alphabetically. Look how the entries come in ascending order according to the string used as a name.