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 the Android Java API for accessing and manipulating contacts in an Android device.
Adding Contact
On ListPage.html
, pressing on the Add button in the header bar (Part 1, Figure 2) invokes a JavaScript function named addContact()
. Related sections of the HTML code and addContact()
function are listed below. Observe that the function shows the Progress screen immediately and calls the ContactsActivity.addContact()
by passing DetailPage.html
as the callback page to display.
... <!-- Contact List --> <div data-role="header" id="hdrList" data-nobackbtn="true"> <h1><img align="top" src="img/contacts.png"> Contacts</h1> <a id="buttonAddContact" data-icon="plus" class="ui-btn-right" href="<B>javascript:addContact();</B>return false;" data-role="button" data-inline="true">Add</a> </div> <div data-role="content" id="contentList" data-theme="c"> <ul data-role="listview" data-dividertheme="c" id="contactSelections"></ul> </div> <div data-role="footer" id="ftrList"></div> ... <script> ... function <B>addContact()</B>{ showProgress(); contactSupport.addContact('DetailPage.html'); } ... </script>
Related code in ContactsActivity
class is shown below.
public void addContact(String displayPage){ showContact(<B>""</B>, displayPage); } public void showContact(String contactId, String displayPage){ loadPage(displayPage + "?" + contactId); }
We had reviewed the showContact()
method in 'Existing Contact' section above. Adding a contact is processed very similarly to saving an existing contact with the exception that the contactId
parameter is passed as an empty string from the addContact()
method. From the discussion in 'Existing Contact' section, the following sequence of method calls takes place for an existing contact (where id
is a non-empty string).
ContactsActivity.loadPage('DetailPage.html?<em>id</em>') -> DetailPage.html:$(document).ready() -> ContactsActivity.getContact(<em>id</em>, 'setCurrentContact') -> DetailPage.html:setCurrentContact(json)
When adding a contact, the same sequence will take place, however, id
is now an empty string. Let us look at the listing for ContactsActivity.getContact()
method again.
public void getContact(String contactId, String contactCallback){ String json = <B>ContactUtility.getContactJSON(contactId, ...); </B> final String callbackFunction = "javascript:" + contactCallback + "('" + json + "')"; loadURL(callbackFunction); }
The ContactUtility.getContactJSON()
returns a special, 'empty', JSON string if the contactId
parameter is empty. That special JSON string is shown below.
{ "contactId":"", "firstName":"", "lastName":"", "note":{"rowId":"","text":""}, "ims":[], "phones":[], "emails":[], "organizations":[], "addresses":[] }
In Part 2 of this tutorial, we had reviewed the callback JavaScript function setCurrentContact()
in DetailPage.html
. It will process the above JSON string in such a way that
- The UI elements represented by variables
contactIdVar
,firstNameVar
,lastNameVar
andnoteVar
are set to empty string. - Other UI elements, e.g. phones, are constructed with predefined types and empty input fields. For example, four different types of phone input fields are constructed with empty values for the following types: Home, Work, Mobile and Other.
When saving the contact, the same steps in section 'Saving A Contact' are executed. The ContactUtility.saveOrUpdateContact()
method could differentiate between an existing and newly added contact by inspecting its id field. For a newly added contact the id field is empty; as a result, it is saved as a new contact.
Cancel Action On Empty Contact And Existing Contact Screens
Consider the Cancel button in Figure 3 (Part 1), displayed in 'Empty Contact' And 'Existing Contact' screens. When that button is pressed, user is taken back to 'Contact List' screen with no changes on currently edited contact. The HTML listing in DetailPage.html
is given below where the button action is defined as showListPage()
function.
... <div align="CENTER" <B>data-role="controlgroup" data-type="horizontal"</B>> <a href="javascript:generateJson();return false;" data-role="button" ><h5>Save</h5></a> <a id="deleteButton" href="javascript:showDialog();return false;" data-role="button" ><h5>Delete</h5></a> <a href="<B>javascript:showListPage();</B>return false;" data-role="button" ><h5>Cancel</h5></a> </div> </div>
Below is the listing for showListPage()
function. It shows the Progress screen and calls ContactsActivity.showAllContacts()
method by passing ListPage.html
as the callback page to display.
function showListPage(){ showProgress(); contactSupport.showAllContacts('ListPage.html'); }
The ContactsActivity.showAllContacts()
method is shown below. It simply loads the callback page without any data processing.
public void showAllContacts(String displayPage){ loadPage(displayPage); }
Reading Contacts Via ContactUtility
Class
Let us start looking at com.jquerymobile.demo.contact.ContactUtility
class that provides utility methods to read and manipulate contacts in contacts database of the Android device.
Getting JSON Formatted List Of Contacts
Recall from Part 1 of this tutorial that the JSON formatted list of contacts is in the following format.
{"contacts":[ {"key":"A","values":[ {"contactId":"257","displayName":"Aach Herb","key":"A"}, ..., {"contactId":"256","displayName":"Aaker David","key":"A"} ] }, {"key":"B","values":[ {"contactId":"259","displayName":"Belanger Andre","key":"B"}, ..., {"contactId":"260","displayName":"Bohme Jacob","key":"B"} ] }, ... ] }
Contacts are grouped by the first initial of the contact's display name, which is value of the key
attribute. The display name is constructed by concatenating contact's first and last names. When displayed on UI, value of the key
attribute will be used as value of the list divider, which groups contacts whose first initial matching that value. The Jackson JSON Processor creates a JSON document in above format from Java objects inferring field names and types, such as arrays, by inspecting those objects. There are three Java classes that represent the contact list above. Those are
-
ContactDisplay
, which represents the leaf level data, namelycontactId
,displayName
andkey
-
ContactGroup
, which has a collection ofContactDisplay
objects for a specifickey
, e.g. 'A' -
ContactList
, which is the root level class with a collection ofContactGroup
objects
Those classes have field names exactly matching the field names in the JSON formatted list above. The Jackson JSON framework provides a variety of options for generating a JSON formatted text from Java objects, including customization of field names. The particular API we use maintains the field names when generating the JSON formatted string from a Java object.
ContactDisplay
As shown in the listing below, ContactDisplay
provides contactId
, displayName
and key
attributes for a contact, where key
is derived from displayName
. What is notable in this class is that it implements the java.lang.Comparable
interface to allow sorting of contacts based on displayName
attribute. The compareTo()
method is required by that interface and it returns one of -1, 0 or 1 depending on whether the first initial of the displayName
attribute of the contact is less than, equal to, or greater than that of the contact it is compared with.
package com.jquerymobile.demo.contact; public class ContactDisplay implements Comparable<Object>{ private String contactId; private String displayName; public String getContactId() { return contactId; } public void setContactId(String contactId) { this.contactId = contactId; } public String getDisplayName() { return displayName; } public void setDisplayName(String displayName) { this.displayName = displayName; } public String getKey(){ if(displayName == null || "".equals(displayName)){ return ""; } else{ return (String.valueOf(displayName.charAt(0))).toUpperCase(); } } public int compareTo(Object arg0) { if(arg0 == null || !this.getClass().equals(arg0.getClass())){ return 1; } String otherDisplayName = ((ContactDisplay)arg0).getDisplayName(); if(displayName == null){ if(otherDisplayName == null){ return 0; }else{ return -1; } }else{ if(otherDisplayName == null){ return 1; }else{ return displayName.compareToIgnoreCase(otherDisplayName); } } } }
ContactGroup
ContactGroup
consists of a collection of ContactDisplay
objects that all have the same key. ContactGroup
's key
attribute has the same value as the key
attribute the ContactDisplay
objects it contains.
package com.jquerymobile.demo.contact; import java.util.Collection; public class ContactGroup { private String key; private Collection<ContactDisplay> values; public Collection<ContactDisplay> getValues() { return values; } public void setValues(Collection<ContactDisplay> values) { this.values = values; } public String getKey() { return key; } public void setKey(String key) { this.key = key; } }
ContactList
Listing for ContactList
class is given below. This is a very simple data structure that contains a collection of ContactGroup
objects.
package com.jquerymobile.demo.contact; import java.util.ArrayList; import java.util.Collection; public class ContactList { public ContactList() { super(); contacts = new ArrayList<ContactGroup>(); } private Collection<ContactGroup> contacts; public Collection<ContactGroup> getContacts() { return contacts; } }
Generating JSON String
The JSON formatted string for list of contacts is generated in ContactUtility.getAllContactDisplaysJSON()
method as shown below.
import java.util.ArrayList; import java.util.Collections; import org.codehaus.jackson.map.ObjectMapper; import java.io.StringWriter; public class ContactUtility { public static final String EMPTY_CONTACT_LIST = "{\"contacts\":[]}"; ... public static String getAllContactDisplaysJSON(...){ // Obtain all ContactDisplay objects from database and sort them. ArrayList<ContactDisplay> list = getAllContactDisplays(...); Collections.sort(list); // Populate the data structure consisting of // ContactList-ContactGroup-ContactDisplay objects. // Start with initializing some variables. ContactList contactList = new ContactList(); String key = ""; ArrayList<ContactDisplay> values = new ArrayList<ContactDisplay>(); ContactGroup group = null; StringWriter writer = new StringWriter(); // Process the list of ContactDisplay objects to construct the data structure. if(list != null && !list.isEmpty()){ for(ContactDisplay display:list){ if(!display.getKey().equals(key)){ if(values.size() > 0){ group = new ContactGroup(); group.setKey(key); group.setValues(values); contactList.getContacts().add(group); } key = display.getKey(); values = new ArrayList<ContactDisplay>(); } values.add(display); } // Add the last group if(values.size() > 0){ group = new ContactGroup(); group.setKey(key); group.setValues(values); contactList.getContacts().add(group); } }else{ return EMPTY_CONTACT_LIST; } // We have the data structure of ContactList-ContactGroup-ContactDisplay objects where // contactList is the root level element. Write it to a JSON formatted string using // org.codehaus.jackson.map.ObjectMapper. try{ ObjectMapper mapper = new ObjectMapper(); mapper.writeValue(writer, contactList); }catch(Exception e){ return EMPTY_CONTACT_LIST; } return writer.toString(); } ... }
- The first step is to get a list of all contacts from the database via
getAllContactDisplays()
method, details of which we will review later. The contacts are in the form of a list, however they are not sorted yet. - The
java.util.Collections.sort()
method sorts the contact list, using thecompareTo()
method inContactDisplay
object. - In the trivial case of an empty list, a JSON string is returned with no elements in it (notice the constant named
EMPTY_CONTACT_LIST
). Otherwise, the sorted list is iterated through to establish groups based on keys of theContactDisplay
objects in the list. For each unique key, aContactGroup
object is created and allContactDisplay
objects with that key are stored in thatContactGroup
. - Each
ContactGroup
is placed in the root levelContactList
is object. - Finally, an instance of
org.codehaus.jackson.map.ObjectMapper
class is created and itswrite()
method is called by passing the root levelContactList
object and aStringWriter
. TheObjectMapper
visits through all fields and objects in theContactList
recursively and generates a JSON text based on field names of those objects. The method callStringWriter.toString()
generates the JSON String object.
Getting JSON Formatted String For Contact Details
Recall from Part 2 of this tutorial that the following JSON document represents details for a contact.
{ "contactId":"265", "firstName":"Aafjes", "lastName":"Bertus", "note":{"rowId":"2265","text":"Author"}, "ims":[ {"rowId":"2274","protocol":"-1","value":""}, {"rowId":"2275","protocol":"0","value":"bertus@aim"}, {"rowId":"2276","protocol":"5","value":"bertus@google"}, {"rowId":"2277","protocol":"6","value":""}, {"rowId":"2278","protocol":"7","value":""}, {"rowId":"2279","protocol":"1","value":"bertus@msn"}, {"rowId":"2280","protocol":"8","value":""}, {"rowId":"2281","protocol":"4","value":""}, {"rowId":"2282","protocol":"3","value":""}, {"rowId":"2283","protocol":"2","value":""} ], "phones":[ {"rowId":"2284","type":"1","no":"111-222-3333"}, {"rowId":"2285","type":"2","no":"222-000-9999"}, {"rowId":"2286","type":"3","no":"444-787-9900"}, {"rowId":"2287","type":"7","no":"555-744-9999"} ], "emails":[ {"rowId":"2271","type":"1","value":"[email protected]"}, {"rowId":"2272","type":"2","value":"[email protected]"}, {"rowId":"2273","type":"3","value":"[email protected]"} ], "organizations":[ {"rowId":"2269","type":"1","name":"Publications Inc.","title":"CEO"}, {"rowId":"2270","type":"2","name":"Volunteers Corp.","title":"Member"} ], "addresses":[ {"rowId":"2266","type":"1","street":"Alhambra st.","city":"Alhambra","state":"MI", "country":"USA","zip":"48100","poBox":""}, {"rowId":"2267","type":"2","street":"1 Corporation st","city":"Alhambra","state":"MI", "country":"USA","zip":"48000","poBox":"44456"}, {"rowId":"2268","type":"3","street":"","city":"","state":"", "country":"","zip":"","poBox":""} ] }
We use the Jackson JSON framework to generate that JSON string from the following Java classes.
-
Contact
represents the root level data. It contains-
contactId
,firstName
,lastName
, which are String types -
note
, which is an object type -
ims
,phones
,emails
,organizations
,addresses
, which are collections of objects
-
-
Note
represents anote
and hasrowId
andtext
fields. -
IM
represents a member ofims
collection and hasrowId
,protocol
andvalue
fields. -
Phone
represents a member ofphones
collection and hasrowId
,type
andno
fields. -
Email
represents a member ofemails
collection and hasrowId
,type
andvalue
fields. -
Organization
represents a member oforganizations
collection and hasrowId
,type
,name
andtitle
fields. -
Address
represents a member ofaddresses
collection and hasrowId
,type
,street
,city
,state
,country
,zip
andpoBox
fields.
Note that all those classes are simple data holders that provide nothing but getter/setter methods. For brevity, we give a listing of only the root level class Contact
below.
package com.jquerymobile.demo.contact; import java.util.Collection; public class Contact { private String contactId; private String firstName; private String lastName; private Collection<Phone> phones; private Collection<Email> emails; private Collection<Address> addresses; private Collection<Organization> organizations; private Note note; private Collection<Im> ims; public String getContactId() { return contactId; } public void setContactId(String contactId) { this.contactId = contactId; } public String getFirstName() { return ContactUtility.replaceNull(firstName); } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return ContactUtility.replaceNull(lastName); } public void setLastName(String lastName) { this.lastName = lastName; } public Collection<Im> getIms() { return ims; } public void setIms(Collection<Im> ims) { this.ims = ims; } public Collection<Phone> getPhones() { return phones; } public void setPhones(Collection<Phone> phones) { this.phones = phones; } public Collection<Email> getEmails() { return emails; } public void setEmails(Collection<Email> emails) { this.emails = emails; } public Collection<Address> getAddresses() { return addresses; } public void setAddresses(Collection<Address> addresses) { this.addresses = addresses; } public Note getNote() { return note; } public void setNote(Note note) { this.note = note; } public Collection<Organization> getOrganizations() { return organizations; } public void setOrganizations(Collection<Organization> organizations) { this.organizations = organizations; } }
Generating The JSON String
The JSON formatted string for contact details is generated in ContactUtility.getContactJSON()
method as shown below.
import org.codehaus.jackson.map.ObjectMapper; import java.io.IOException; import java.io.StringWriter; ... public class ContactUtility { ... private static final String EMPTY_JSON = "{\"contactId\":\"\",\"firstName\":\"\",\"lastName\":\"\", \"note\":{\"rowId\":\"\",\"text\":\"\"},\"ims\":[],\"phones\":[],\"emails\":[], \"organizations\":[],\"addresses\":[]}"; public static final String EMPTY = ""; ... public static String getContactJSON(String id, ContentResolver contentResolver){ // if id is EMPTY, immediately return an empty json if(EMPTY.equals(id)){ return EMPTY_JSON; } Contact contact = getContact(id,contentResolver); StringWriter writer = new StringWriter(); if(contact != null){ try { ObjectMapper mapper = new ObjectMapper(); mapper.writeValue(writer, contact); } catch (IOException e) { return EMPTY_JSON; } } String val = writer.toString(); return val; } ... }
- If the id of contact is an empty string,
getContactJSON()
method returns a bare bones JSON string,EMPTY_JSON
. (As discussed previously, this is the scenario when a new contact is to be added via the application and we do not have a contact id yet.) - If the id is not empty, the
getContact()
method is called to get an instance of theContact
class from the contacts database in the device. (We will review this method later on.) - Then, using the Jackson JSON framework class
org.codehaus.jackson.map.ObjectMapper
, theContact
object is converted to a JSON formatted string representation and that string is returned to the caller of the method. As discussed in the previous section of contact list generation, that particular technique of creating the JSON string maintains the field names in the Java objects.
Contact Database Table Structure
In Part 4 of this tutorial (next installment), we will look at the application code that deals with the Android contacts API. Before this, we want to give an overview of how contact information is stored in database in an Android device. Various entities related to a contact are stored in database tables that can be accessed via SQL like queries. The Android contacts API provides various classes and methods to represent tables and table columns to store contacts, as well as specific queries that can be run against those tables. Below, we provide information on the Android contacts API as utilized by this tutorial application.
-
android.content.ContentResolver
class is used to perform read/write queries in contacts database. -
android.database.Cursor
class is used to iterate through a result set returned from a read query and read individual columns from a particular row. -
android.content.ContentProviderOperation
class is used to define an insert operation in database. -
android.provider.ContactsContract
class and its fields represent contacts related database tables. (See Android documentation onContactsContract
for details.)
For getting information on a contact related entity, one essentially needs three elements.
- a URI based representation of the table to fetch the data from
- a 'where' clause for the query; this is constructed using constants in the Android contacts API and parameters created by the application
- column names to fetch from a row of the result set; these are constants in the Android contacts API
Below, we give a summary of those three elements for the particular read queries used in the tutorial application. For all where clauses, except the one in the query named 'Contacts Associated With An Account', the application provides contactId
as the parameter. For the query named 'Contacts Associated With An Account', the application passes the constant com.jquerymobile.demo.contact
as the query parameter in the where clause. For the query named 'Contact Id, Contact Display Name', there is no where clause because we fetch all records.
- Query: Contact Id, Contact Display Name
- URI:
ContactsContract.Contacts.CONTENT_URI
- Where clause: none
- Column names:
-
contactId
:ContactsContract.Contacts._ID
-
displayName
:ContactsContract.Contacts.DISPLAY_NAME
-
- URI:
- Query: First Name, Last Name
- URI:
ContactsContract.Data.CONTENT_URI
- Where clause:
ContactsContract.RawContactsEntity.CONTACT_ID = ? AND
ContactsContract.Data.MIMETYPE = ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE - Column names:
-
firstName
:ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME
-
lastName
:ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME
-
- URI:
- Query: Address
- URI:
ContactsContract.Data.CONTENT_URI
- Where clause:
ContactsContract.Data.CONTACT_ID = ? AND ContactsContract.Data.MIMETYPE =
ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE - Column names:
-
street
:ContactsContract.CommonDataKinds.StructuredPostal.STREET
-
city
:ContactsContract.CommonDataKinds.StructuredPostal.CITY
-
state
:ContactsContract.CommonDataKinds.StructuredPostal.REGION
-
poBox
:ContactsContract.CommonDataKinds.StructuredPostal.POBOX
-
zip
:ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE
-
country
:ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY
-
rowId
:ContactsContract.CommonDataKinds.StructuredPostal._ID
-
- URI:
- Query: Email
- URI:
ContactsContract.CommonDataKinds.Email.CONTENT_URI
- Where clause:
ContactsContract.CommonDataKinds.Email.CONTACT_ID = ?
- Column names:
-
value
:ContactsContract.CommonDataKinds.Email.DATA
-
type
:ContactsContract.CommonDataKinds.Email.TYPE
-
rowId
:ContactsContract.CommonDataKinds.Email._ID
-
- URI:
- Query: IM
- URI:
ContactsContract.Data.CONTENT_URI
- Where clause:
ContactsContract.Data.CONTACT_ID = ? AND ContactsContract.Data.MIMETYPE =
ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE - Column names:
-
value
:ContactsContract.CommonDataKinds.Im.DATA
-
protocol
:ContactsContract.CommonDataKinds.Im.PROTOCOL
-
rowId
:ContactsContract.CommonDataKinds.Im._ID
-
- URI:
- Query: Note
- URI:
ContactsContract.Data.CONTENT_URI
- Where clause:
ContactsContract.Data.CONTACT_ID = ? AND ContactsContract.Data.MIMETYPE =
ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE - Column names:
-
text
:ContactsContract.CommonDataKinds.Note.NOTE
-
rowId
:ContactsContract.CommonDataKinds.Note._ID
-
- URI:
- Query: Organization
- URI:
ContactsContract.Data.CONTENT_URI
- Where clause:
ContactsContract.Data.CONTACT_ID = ? AND ContactsContract.Data.MIMETYPE =
ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE - Column names:
-
name
:ContactsContract.CommonDataKinds.Organization.DATA
-
title
:ContactsContract.CommonDataKinds.Organization.TITLE
-
type
:ContactsContract.CommonDataKinds.Organization.TYPE
-
rowId
:ContactsContract.CommonDataKinds.Organization._ID
-
- URI:
- Query: Phone
- URI:
ContactsContract.CommonDataKinds.Phone.CONTENT_URI
- Where clause:
ContactsContract.CommonDataKinds.Phone.CONTACT_ID = ?
- Column names:
-
no
:ContactsContract.CommonDataKinds.Phone.NUMBER
-
type
:ContactsContract.CommonDataKinds.Phone.TYPE
-
rowId
:ContactsContract.CommonDataKinds.Phone._ID
-
- URI:
- Query: Contacts Associated With An Account
- URI:
ContactsContract.RawContacts.CONTENT_URI
- Where clause:
ContactsContract.RawContacts.ACCOUNT_TYPE = ?
- Column name:
-
contactId
:ContactsContract.RawContacts.CONTACT_ID
-
- URI:
With the above information in mind we can now look at individual methods that query the contacts database.
Getting All Contact Displays
The following method in ContactUtility
gets a list of all contacts in the form of a ContactDisplay
collection. We had seen that this method was called from ContactUtility.getAllContactDisplaysJSON()
. The method uses the 'Contact Id, Contact Display Name' query just discussed above.
private static ArrayList<ContactDisplay> getAllContactDisplays(ContentResolver contentResolver){ Cursor cursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null); ArrayList<ContactDisplay> list = new ArrayList<ContactDisplay>(); if (cursor.getCount() > 0) { while (cursor.moveToNext()) { String id = cursor.getString(cursor .getColumnIndex(ContactsContract.Contacts._ID)); String displayName = cursor .getString(cursor .getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)); ContactDisplay display = new ContactDisplay(); display.setContactId(id); display.setDisplayName(displayName); list.add(display); } cursor.close(); } return list; }
- The
ContentResolver
object is used to perform a query where a URI representation for the queried table is passed as a parameter. - The result is returned as a
Cursor
instance. Iterating through the cursor, we get the desired fields using constants for column names. - At each row of the cursor, a corresponding
ContactDisplay
object is created from those fields and is placed in a collection. - At the end, we close the cursor and return the collection.
Getting First Name, Last Name
The following method in ContactUtility
returns a string array where the first element is a contact's first name and the second element is the contact's last name. The method uses the 'First Name, Last Name' query just discussed above.
private static String[] getFirstNameLastName(String id, ContentResolver contentResolver){ String[] retValue = new String[2]; retValue[0] = ""; retValue[1] = ""; Cursor nameCursor = contentResolver.query(ContactsContract.Data.CONTENT_URI, null, ContactsContract.Data.MIMETYPE + " = ? AND " + ContactsContract.RawContactsEntity.CONTACT_ID + " = ? ", new String[] { ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE ,id},null); while (nameCursor.moveToNext()) { String given = nameCursor.getString(nameCursor.getColumnIndex( ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME)); String family = nameCursor.getString(nameCursor.getColumnIndex( ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME)); if(given != null){ retValue[0] = given; } if(family != null){ retValue[1] = family; } } nameCursor.close(); return retValue; }
Construction of the query and processing of the result set are very similar to getAllContactDisplays()
method. What is different is the additional parameters passed to the query()
method.
- First parameter to
query()
is the URI for table. - Second parameter to
query()
is null, requesting all columns. (For additional performance improvement, one may restrict the columns to be returned. For the purposes of the tutorial application, we simply requested all columns from the query although not all columns were used.) - Third parameter to
query()
is the where clause, discussed previously. - Fourth parameter to
query()
is a string array where each element of the array corresponds to the respective parameter, identified by '?', in the where clause. - The fifth parameter to
query()
is null. That parameter could be used to sort the result set. However, we are getting a single result, matching a unique id. Therefore, the sort column is not specified.
Getting Contact Details
The following method in ContactUtility
returns a Contact
object with details populated from the database where id of the contact is the input parameter. We had seen that this method is called from ContactUtility.getContactJSON()
.
private static Contact getContact(String id, ContentResolver contentResolver){ Contact contact = null; Cursor cursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, ContactsContract.Contacts._ID + " = ? ", new String[]{id}, null); if (cursor.getCount() > 0) { contact = new Contact(); while (cursor.moveToNext()) { String contactId = cursor.getString(cursor .getColumnIndex(ContactsContract.Contacts._ID)); contact.setContactId(contactId); String[] names = getFirstNameLastName(contactId, contentResolver); contact.setFirstName(names[0]); contact.setLastName(names[1]); contact.setPhones(getPhones(id, contentResolver)); contact.setEmails(getEmails(contactId, contentResolver)); contact.setNote(getNote(contactId,contentResolver)); contact.setAddresses(getAddresses(contactId,contentResolver)); contact.setIms(getIms(contactId, contentResolver)); contact.setOrganizations(getOrganizations(contactId, contentResolver)); } } cursor.close(); return contact; }
The method uses various inner query methods. Of those, getFirstNameLastName()
has been reviewed just above. The other operations, namely getPhones()
, getEmails()
, getNote()
, getAddresses()
, getIms()
and getOrganizations()
are very similar to getFirstNameLastName()
and therefore we omit their reviews for the sake of brevity. Interested reader is referred to the source code of the tutorial. Note that those methods use the previously discussed tables, where clauses and column names, summarized in 'Contacts Database Queries' as follows:
-
getPhones()
uses Phone query -
getEmails()
uses Email query -
getNote()
uses Note query -
getAddresses()
uses Address query -
getIms()
uses IM query -
getOrganizations()
uses Organization query
Closing Remarks For Part 3 Of This Tutorial
In Part 3, we continued the tutorial by explaining how to add a brand new contact. We also discussed how to use Android Java API for accessing and manipulating contacts in an Android device. Part 4, the last installment of the tutorial, will explain deleting and saving a contact using Android Java API. In Part 4, we will also describe the development environment for the application, discuss the configuration files for the project, and give individual steps for importing the project into Eclipse IDE.
Comments