Advertisement
  1. Code
  2. Mobile Web Apps
Code

Build a Contacts Application With jQuery Mobile & the Android SDK – Part 2

by
Difficulty:AdvancedLength:MediumLanguages:

This tutorial picks up where we left off in part 1 by demonstrating how to create an account for the newly created contacts. We will also describe how to edit and delete an existing contact.

In Part 1 of this tutorial, we introduced the sample application that will be implemented. We also gave an overview of page elements in jQuery Mobile framework and explained how the screens in the sample application are constructed using those elements. We also reviewed basic concepts in integrating UI, composed of HTML and JavaScript, with the back-end Java classes that implement the business functionality.


Create Account

We will now look at how to create an account to associate with the tutorial application. The 'Create Account' screen is shown in Figure 1. User enters name of the account in the input field and presses the Save button. The HTML and JavaScript code for the 'Create Account' screen is part of ListPage.html. The related sections are shown below.

  • Observe that 'Create Account' is represented by a content page with its own header, content and footer sections, placed inside a container page.
  • When user presses the Save button, the createAccount() function is called.
  • The createAccount() gets value of the account name, as entered by user into the input field accountName, and passes it to the ContactsActivity.createAccount() method as the first parameter. The second parameter passed to the method is the callback HTML file to load, ListPage.html.

ContactsActivity Responds

The ContactsActivity.createAccount() method is listed below.

  • Actual account creation is done by android.accounts.AccountManager which is one of the entry points in Android Java API to manage user accounts in the device. We first acquire an instance of the AccountManager by calling AccountManager.get() and then invoke addAccountExplicitly() to create a user account of the desired name and type.
  • After account is created, the callback HTML file, ListPage.html, is loaded via loadPage(displayPage).
  • Recall from Part 1, 'Initial Load Of ListPage.html', loading of ListPage.html will trigger a call to ContactACtivity.getAllContacts() method. Since an account has been found, that method will generate a JSON formatted string of the contacts list and pass that string to the JavaScript function setContactsList() in ListPage.html. We had reviewed that function in Part 1, 'Contact List Screen'.

To be able to create an account, we have to implement an Android service that has the following intent in AndroidManifest.xmlfile.

In addition, that service should have the following meta-data description in AndroidManifest.xml.

Finally, the res/xml/authenticator.xml configuration file (which is value of the android:resource attribute above) should have an element named account-authenticator where value of android:accountType attribute is set to com.jquerymobile.demo.contact. Note that this is value of the accountType in ContactsActivity. Summarizing the discussion, first look at the highlighted section in AndroidManifest.xml.

Relative to value of the package attribute, we define the service class as .authentication.AuthenticationService. Hence, the fully qualified class name is com.jquerymobile.demo.contact.authentication.AuthenticationService. We then provide the required intent-filter element and android:resource attribute in meta-data element. The res/xml/authenticator.xml configuration file is listed below. What is important in that file is the fact that the value of android:accountType attribute is same as the account type ContactsActivity uses to create the account.

Finally, the service implementation is given below. It is a trivial, bare bones implementation with no valuable functionality. The only implemented method is the abstract onBind() method from Service. If you need more functionality, for example, ability to link this account with an online service, you need to provide the required business logic by extending various other methods of the android.app.Service class. You may also need to implement an authenticator class extending android.accounts.AbstractAccountAuthenticator. For the purposes of the tutorial application, this service implementation is sufficient.

After account is created, the account name can be viewed in 'Accounts and sync settings' screen for contacts in the Android device. This is shown in the figure below.

Account Name

Figure 11. Account Name.

Existing Contact

To display details of an existing contact, user presses on the list item for that contact on 'Contact List' screen (Part 1, Figure 2). Recall from the section named 'Contact List Screen', the following code constructs the list items for contacts. What is of interest is the JavaScript showContact() function in the <a> tag definition for each particular contact. When user presses a list item, id of the contact in that list item is passed to showContact(). (The id of a contact is unique identifier for that contact in contacts database of the Android device.)

Listing of JavaScript showContact() function is given below. It shows the Progress screen and calls ContactsActivity.showContact() method by passing to it the id of the contact to show and the callback display page, 'DetailPage.html'.

Related code in ContactsActivity class is shown below.

The showContact() method displays the callback page, value of displayPage parameter, appending contactId parameter as an HTTP query string. For example, if value of contactId is 23 and the value of displayPage is DetailPage.html then the URL to load is DetailPage.html?23.

DetailPage.html

The DetailPage.html has three content pages in it. One of them represents contact details for 'Existing Contact' and 'Empty Contact' screens in Figure 3, another one is for 'Confirm Delete' screen (Figure 4) and the last one is for Progress screen (Figure 5). Those figures are in Part 1 of the tutorial.

Contact Details

Let us look at fragments of HTML code to review the content page for contact details. The first fragment is shown below.

  • The header bar has an image in it. (This is an icon image generated from the Android GUI elements in http://www.matcheck.cz/androidguipsd/.)
  • The content page contains a form to store all the contact fields. There are input fields for first and last names and notes for the contact.
  • The contactId is a hidden form input. It corresponds to the unique identifier for the contact in contacts database of the Android device.
  • The phone numbers are stored in a table structure within a jQuery Mobile collapsible block. A collapsible block serves as a container for logically related UI elements. User can expand or collapse the block to display/hide the elements it wraps. Initially the block is collapsed.

The following figure shows the 'Existing Contact' screen, with the UI elements corresponding to the code fragment above. The collapsible block for phones is expanded, showing four different types of phone numbers for the contact. Please note that those phone numbers have been dynamically added to the phonesTable as part of loading contact details. We will review the related JavaScript code later on.

Existing Contact - Part 1

Figure 12. Existing Contact - Part 1.

We continue review of the content page for contact details with the following listing.

  • Another collapsible block is defined, for the emails, which is very similar to the one for phone numbers. It contains a table with id emailsTable which will be dynamically populated later on with emails of the contact.
  • We see an example of nested collapsible blocks. A top level collapsible block with title 'More' contains three nested collapsible blocks for addresses, organizations and IMs (instant messaging addresses) of the contact.
  • Each of the collapsible blocks for addresses, organizations and IMs contains a table for the related content. Those tables are populated dynamically when the content is loaded.

Figures 13 - 16 show the 'Existing Contact' screen, with the UI elements corresponding to the code fragment above. In Figure 13, the collapsible block for the email is expanded, showing three different types of emails for the contact.

Existing Contact - Part 2

Figure 13. Existing Contact - Part 2.

The following figure shows the email collapsible block is collapsed whereas the 'More' collapsible block is expanded. The collapsible blocks under 'More' are all in collapsed form.

Existing Contact - Part 3

Figure 14. Existing Contact - Part 3.

In the following figure, the block for addresses under 'More' has been expanded. There are three types of addresses listed: Home, Work, Other.

Existing Contact - Part 4

Figure 15. Existing Contact - Part 4.

In the following figure, the blocks for organizations (left) and IMs (right) under 'More' have been expanded. There are two types of organizations (Work, Other) and ten types of IMs for the contact.

Existing Contact - Part 4

Figure 16. Existing Contact - Part 5.

The last code fragment for the contact details content page is listed below. Those are the three buttons shown in Figure 14 for Save, Delete and Cancel operations.

  • Notice the data-role="controlgroup" attribute in <div> tag. This allows us to group three buttons together. The attribute definition data-type="horizontal" states that the buttons should be laid out horizontally. (Grouped Buttons details in jQuery Mobile documentation.)
  • Each button activates a corresponding JavaScript method.

Progress Screen

The HTML code defining the Progress screen in DetailPage.html is given below.

As seen above, the Progress screen has a simple implementation with a descriptive text and a spinning wheel image, wait.gif.

Confirm Delete

The HTML code for the third content page in DetailPage.html, corresponding to 'Confirm Delete' screen, is shown below. This screen is displayed when user presses the Delete button on Figure 14 to delete the currently displayed contact.

  • Similar to the Save, Delete, Cancel buttons discussed above, we are using grouped buttons for Delete and Cancel.
  • If user presses on Cancel, delete operation is canceled and user is displayed the 'Existing Contact' screen.
  • If user presses on Delete, delete operation is carried out and user is taken to 'Contact List' screen with updated list of contacts.

Showing/Hiding Content Pages

At any given time, only one of the content pages for contact details, 'Confirm Delete' and Progress screens must be shown. The following code listing in DetailPage.html describes how showing/hiding of those content pages is done.

  • We define JavaScript variables for header, content and footer sections and initialize them in jQuery $(document).ready() function using id selectors.
  • Then, we define the respective hidePage(), showPage() functions. Invoking the showPage() function for one content page, shows the header, content and footer sections of the page and hides the header, content and footer sections of the other pages.
  • The 'Confirm Delete' screen does not need to be shown for an 'Empty Contact' screen, which is displayed when a new contact to be added. When 'Empty Contact' screen is shown, the contact id is an empty string. The showDialog() function returns immediately without displaying the 'Confirm Delete' screen if the currently processed contact id is an empty string.

Populating Contact Details

Having reviewed the structure of content pages in DetailPage.html, let us look into how to populate the contact details for an existing contact. Recall that the ContactsActivity.showContact() method displays DetailPage.html appending id of the contact as an HTTP query string, e.g. DetailPage.html?23. Let us see below how JavaScript code in DetailPage.html will process that information. The related section in jQuery $(document).ready() function is given below.

  • When the page loads, the Progress screen is shown immediately.
  • Then, the value of the contactIdVar variable is set to id of the contact from the query string. For example, if the page is loaded as DetailPage.html?23, the value of contactIdVar variable is set to 23. Parsing the query string is done via window.location.search.substring(1).
  • Finally, ContactsActivity.getContact() is called by passing two parameters: (1) the contact id and (2) the callback function setCurrentContact. As a result, ContactsActivity will get details of the contact for the specified id and pass the results back to the JavaScript function setCurrentContact() in DetailPage.html.

The ContactsActivity.getContact() method is shown below. It gets a JSON formatted string of contact details and passes it back to the requested JavaScript function.

JSON Format For Contact Details

The JSON format for details of a contact is shown below.

JSON Formatted String For Contact Details
  • The fields contactId, firstName, lastName are simple strings, note is an object, and each of ims (instant messenger addresses), phones, emails, organizations and addresses are arrays of objects.
  • For any object, the rowId field is an integer. The rowId is unique across all objects within a particular array. For example, no two objects in ims array have the same rowId. The uniqueness of rowId helps us define unique UI elements in JavaScript code.
  • Objects in ims array have a protocol attribute, which signifies the kind of IM each object represents. The protocol is integer valued. Android contacts API defines the following constants for IM protocols (see documentation).
    • protocol=-1, Custom
    • protocol=0, AIM
    • protocol=1, MSN
    • protocol=2, Yahoo
    • protocol=3, Skype
    • protocol=4, QQ
    • protocol=5, Google
    • protocol=6, ICQ
    • protocol=7, Jabber
  • Objects in phones array have a type attribute, which signifies the type of phone each object represents. The type is integer valued. Android contacts API defines the following constants for phone types (see documentation).
    • type=1, Home
    • type=2, Mobile
    • type=3, Work
    • type=7, Other
  • Objects in emails array have a type attribute, which signifies the type of email each object represents. The type is integer valued. Android contacts API defines the following constants for email types (see documentation).
    • type=1, Home
    • type=2, Work
    • type=3, Other
  • Objects in organizations array have a type attribute, which signifies the type of organization each object represents. The type is integer valued. Android contacts API defines the following constants for organization types (see documentation).
    • type=1, Work
    • type=2, Other
  • Objects in addresses array have a type attribute, which signifies the type of address each object represents. The type is integer valued. Android contacts API defines the following constants for address types (see documentation).
    • type=1, Home
    • type=2, Work
    • type=3, Other

Parsing JSON Via JavaScript

The implementation of the JavaScript setCurrentContact() function in DetailPage.html is given below. That function is responsible for parsing the JSON text above and dynamically populating various UI elements we had discussed before. For the sake of brevity, we will review the portion of the code that sets values of the first name, last name, notes and phone numbers. Other UI elements are parsed and populated in a similar manner.

Definition Of Constants

Due to the fact that the UI elements are created based on various HTML fragments used as templates, we start with defining constants for those fragments as shown below.

What is notable above is the variable named STYLED_IN where we add jQuery Mobile specific styles explicitly. The jQuery Mobile framework allows us to dynamically add rows into a table. However, we found that it does not automatically apply the style information from a parent level <div> tag into the <input> elements in table rows dynamically added to a table. As a result, when we dynamically add rows into a table definition, we apply the style explicitly to <input> elements in those rows.

Definition Of Variables

Following the definition of constants, we define several variables below that are initialized when the page loads.

  • firstNameVar represents the input element for first name.
  • lastNameVar represents the input element for last name.
  • noteVar represents the input element for notes.
  • contactIdVar represents the contact id.
  • phonesTableVar represents the table inside the collapsible block element for phone numbers.
Parsing Simple Object Types

We use jQuery parseJSON() function to parse the JSON formatted string of the contact details. The currentContact variable is set to the JavaScript object that contains the parsed JSON formatted string. The contactId, firstName, lastName and note fields are extracted and respective UI variables are set to those values, as shown below.

Parsing Object Of Arrays

Android contacts database defines various phone types. Of those, this tutorial is focuses on the following ones: Home, Mobile, Work and Other. Each phone is identified by three variables, type (type of the phone, as reviewed previously), rowId (id of the phone in contacts database in the device), and no (the phone number). In our application, the phone numbers are stored in an array named phonesArr. Please consider the code listing below. We iterate through the array and for each phone extract the type, rowId, and no for the phone. Then, we set temporary variables tmpType, tmpRowId, tmpNo to those values.

Depending on type of each phone, we construct an HTML fragment to define a table row with information about that phone. For example, for home phone number, if value of tmpRowId is 15 and value of tmpNo is 2223334444 then the fragment

HOME_FRAGMENT_PRX + tmpRowId + HOME_PHONE_FRAGMENT_MID + tmpNo + FRAGMENT_SFX
becomes

Above, observe that id of the input element is composed as 15_1_No where 15 corresponds to the id of the phone number in contact database, 1 signifies type of the phone and No implies that this id is for a phone number. The uniqueness of the rowId variable for the phones assures that the id attribute is unique within the document. The fragment is added to phonesTableVar via jQuery append(). Details are shown below.

It is important that we create input fields for all four types of phone numbers. If the contact has at least one number of a particular type, the code above will create the input field for that phone type. If contact does not have a phone number of a particular type, we record that information, and create the input field for that phone type after executing the code above. This is done in the code listing below.

To interpret the above code, let us consider the work phone number.
If contact does not have a work phone number, the value of workPhoneSet is false. The above code
appends WORK_PHONE_FRAGMENT_LOCAL to phonesTableVar. Via variable substitution observe that the value of WORK_PHONE_FRAGMENT_LOCAL is as follows.

This will simply create an empty input field where id attribute has -1 instead of an actual database id defined via rowId field.

Display Contact Detail

Finally, having all UI elements populated with details of the contact, we display the content page for contact details. This is end of JavaScript function setCurrentContact().

To summarize the discussion above, the following sequence of method calls takes place to display an existing contact.

Deleting A Contact

In order to delete a contact, user presses on the Delete button shown in Figure 14. This invokes the JavaScript showDialog() function which, as discussed previously, shows the 'Confirm Delete' screen in Figure 4. If user presses Cancel, no action is taken and the JavaScript showDetail() function is invoked. This displays the 'Existing Contact' screen.

On 'Confirm Delete' screen, if user presses the Delete button then the JavaScript deleteContact() function is called, a listing for which is given below. It is seen that user is displayed the Progress screen and the ContactsActivity.deleteContact() method is called with two parameters. The first one is id of the contact to delete and the second one is the callback display page, ListPage.html, to be shown after contact is deleted. That is, after the contact is deleted we would like the 'Contact List' screen to be shown to user.

The corresponding code for ContactsActivity.deleteContact() method is simple. It calls the ContactUtility.deleteContact() method by passing id of the contact to be deleted and then loads the desired callback page in WebView. (The ContactUtility.deleteContact() method will be reviewed later.)

Saving A Contact

In order to save a contact, user presses the Save button shown in Figure 14. This invokes the JavaScript generateJson() function, for which a partial listing is shown below. The main purpose of the function is to produce a JSON formatted string for the currently edited contact, in the same format as the one shown in 'Populating Contact Details' above, and pass it to Java back-end for saving in contacts database. For brevity, we will look at generating contactId, firstName, lastName, note and phones fields as rest of the code is very similar.

  • The contactId, firstName, and lastName fields are generated as simple string types by reading values of the respective UI elements.
  • The note field is generated as an object type where the rowId is passed as empty, because that field is not important on Java back-end when saving a contact. The text field is set to value of the respective UI element.

The phones field is created as an array of object types (shown below).

  • For each object in the array the rowId field is passed as empty, because that field is not important on Java back-end when saving a contact.
  • The value of the type field is determined by parsing the id attribute of the <input> element corresponding to the phone. For example, if id="15_1_No" then value of type is 1 (Home). The value of no attribute is simply value of the <input> element.
  • The addClosingBracket() function simply strips off the latest comma from the phones object array and adds a closing brace '}'.
  • When the JSON formatted string for the contact is generated, ContactsActivity.saveContact() method is called by passing that string and the callback page to display, ListPage.html, to be shown after contact is saved. That is, after the contact is saved we would like the 'Contact List' screen to be shown to user.

The ContactsActivity.saveContact() method is shown below.

The ObjectMapper class is the entry point in Jackson JSON Processor to convert a JSON formatted string to an object representation. As we will show later, the Contact class is a complex data structure compatible with the JSON format representing a contact. In other words, its fields and objects match the field names and data types in the JSON format representing a contact. With that compatibility in place, ObjectMapper.readValue() method converts the JSON text to an instance of the Contact class and returns that instance. Then, ContactsActivity.saveContact() method passes that Contact instance to ContactUtility.saveOrUpdateContact() method for saving the contact in database. Finally, the desired callback page is loaded in WebView, which is the 'Contact List' screen.


Closing Remarks For Part 2 Of This Tutorial

In Part 2 of the tutorial series, we demonstrated how to create an account for the newly created contacts. We also described how to edit and delete an existing contact. In Part 3, we will continue the tutorial by explaining how to add a brand new contact. We will also discuss how to use Android Java API for accessing and manipulating contacts in an Android device.


Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.