When I was looking at the official example on the Google Android Developers site for accessing content providers in Android (http://developer.android.com/guide/topics/providers/content-providers.html) I found an outdated example to query contacts which is using deprecated fields in the Android API. As I’ve seen quite some developers who are still relying on that deprecated example to implement their functionality even when using the newer API levels I’ve decided to post an example which is using the new way suggested in the Android API.

Example Android App which queries all contacts and additional specific information on a single selectable personTo demonstrate the features of the new API the example App is querying all available contacts on the phone and additionally commonly used information from the contacts content provider such as the name, phone numbers, email addresses and of course the photo.

The new approach using the ContactsContract was introduced with API level 5 (Android 2.0) and was further improved with API level 11 (Android 3.0), but I did not use any of the new features in this example so it’s compatible for any versions which are at least using API level 5.

You can browse or download the source code of the example App at our SVN repository at Google Code (http://code.google.com/p/android-contacts-contract-example/) or use svn to check the project out.

Feel free to reuse portions of this code as you wish.

As this post is written for beginners you might want to jump directly to a specific topic within this post:

Basic concept of Content Providers

Before we start let’s take a short look at the basic concept to access content providers. Content providers are providing data using a database like approach. The database of the content provider is always addressed by an unique URI e.g. “content://com.appsolut.example/exampleData”. To access a specific content provider, the first step is to create a query resulting in a Cursor which represents the returned data as an object with random access. The configuration of queries is straightforward and can be described in five steps:

  1. Identify the unique resource identifier (URI) of the desired content provider
  2. Generate a String array which is holding the names of the columns which you require from the database (e.g.  RawContacts.CONTACT_ID). This is called projection.
  3. If you want to filter the results using the query define a selection clause (e.g. to filter by contact ids: RawContacts.CONTACT_ID + “=?”). The ? operator acts as a parameter which is defined in the next step. This is called selection.
  4. Create another String array for all parameters which you’ve used in your selection clause. For the above example it could be something like new String[]{contactId}. If no parameters where used just ignore this step. This array is called selectionArgs.
  5. If you want to sort your results by table columns define a sort order like RawContacts.CONTACT_ID + ” ASC”, which will sort the results in ascending order using their contact ids. This string is called sortOrder.

Using the parameters from these five steps the query method can be called.

public final Cursor managedQuery(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)

Note: starting with API level 11 the managedQuery method will also be marked as deprecated but the new concept is quite similar.

Using the cursor rows can be selected and specific values of columns in this row can be returned using getters like getString or getInt.

For a more detailed description about content providers visit the documentation at http://developer.android.com/guide/topics/providers/content-providers.html – but don’t use the proposed way to access the contacts content provider as it is outdated and deprecated.

Querying contacts and further details of a contact

Enough basics, let’s query contacts and information related to these contacts. For that purpose the “android.provider.ContactsContract“ class and subpackages were introduced at API level 5.

Permission

The basic step before using the “ContactsContract” in your App is to add the required permission



	
...

to your projects manifest file. Otherwise your App will always crash when you access the content providers.

Querying a list of all available contacts

Now we can start accessing the “ContactsContract.RawContacts” (http://developer.android.com/reference/android/provider/ContactsContract.RawContacts.html) content provider to query all available contacts stored in the smartphone. This table represents every person as a single entry (one row). In this table an unique id is assigned to each person which is stored in the RawContacts.CONTACT_ID field (column). Unlike the default _ID column this id is used in the other tables as well, so we can later use it to query additional information about this person. To get access to the RawContacts content provider we first define a projection as shown here:

final String[] projection = new String[] {
RawContacts.CONTACT_ID,	// the contact id column
RawContacts.DELETED		// column if this contact is deleted
};

We are interested in the contact id column and the deleted column. The deleted column is important because there can be entries in the RawContacts table which have been deleted and therefore should not be displayed anymore. With the help of the projection we can now create the Cursor using a query.

final Cursor rawContacts = managedQuery(
RawContacts.CONTENT_URI,	// the URI for raw contact provider
projection
null,					// selection = null, retrieve all entries
null,					// selection is without parameters
null);					// do not order

Using this cursor we can iterate through all available contact ids. To do so we need to identify the index of the contact id and deleted column. We can use the getColumnIndex of the cursor object to get this index.

final int contactIdColumnIndex = rawContacts.getColumnIndex(RawContacts.CONTACT_ID);

final int deletedColumnIndex = rawContacts.getColumnIndex(RawContacts.DELETED);

To ensure that the cursor is pointing to the beginning and that there are valid entries we use the moveToFirst method, which will move the cursor to the first entry and return true if there are entries available. Now we can iterate over all entries using a while loop which checks the isAfterLast method of the cursor which will return true if the cursor is pointing to an non-existing entry.

if(rawContacts.moveToFirst()) {
	while(!rawContacts.isAfterLast()) {		// still a valid entry left?
		final int contactId = rawContacts.getInt(contactIdColumnIndex);
		final boolean deleted = (rawContacts.getInt(deletedColumnIndex) == 1);
		if(!deleted) {
			doSomethingWithAContactId(contactId));
		}
		rawContacts.moveToNext();			// move to the next entry
	}
}

Finally we can close the cursor to free resources:

rawContacts.close();

Querying basic information of a specific contact

Using a given contact id (for example from the previous part) we can access basic information about this person in the “ContactsContract.Contacts” (http://developer.android.com/reference/android/provider/ContactsContract.Contacts.html) table. In our example we will query the name of the contact as well as the photo id which is a reference to the photo entry in the data table and can thus later be used to query the photo as a bitmap. So first we define our projection for the columns DISPLAY_NAME and PHOTO_ID.

final String[] projection = new String[] {
	Contacts.DISPLAY_NAME,	// the name of the contact
	Contacts.PHOTO_ID		// the id of the column in the data table for the image
};

Using this selection we can create a cursor pointing to that specific contact. By using the selection and selectionArgs parameter of the query method we can filter the results of the query according to the given contact id (in our case the field contactId).

final Cursor contact = managedQuery(
				Contacts.CONTENT_URI,
				projection,
				Contacts._ID + "=?",	// filter entries on the basis of the contact id
				new String[]{String.valueOf(contactId)},	// the parameter to which the contact id column is compared to
				null);

Now we can retrieve the desired information.

if(contact.moveToFirst()) {
final String name = contact.getString(
	contact.getColumnIndex(Contacts.DISPLAY_NAME));
	final String photoId = contact.getString(
			contact.getColumnIndex(Contacts.PHOTO_ID));
	doSomethingWithAContactName(name);
	doSomethingWithAContactPhotoId(photoId);
}
contact.close();

Querying the photo of a contact

The contact photos are stored as binary large objects (blob) in the “ContactsContract.Data” table. In this table all kinds of data about a contact is stored so we need a given photo id to retrieve the correct entry (see Querying basic information of a specific contact). In our example we use the field photoId to represent this id. The column in which the blob is stored is defined in the “CommonDataKinds.Photo” class. Using this Photo.PHOTO column we can define our query as shown below.

final Cursor photo = managedQuery(
				Data.CONTENT_URI,
				new String[] {Photo.PHOTO},		// column for the blob
				Data._ID + "=?",				// select row by id
				new String[]{photoId},			// filter by photoId
				null);

If the contact has a photo linked to its entry, the cursor will return the photo blob. Using the “BitmapFactory” we can create a Bitmap using this blob.

if(photo.moveToFirst()) {
	byte[] photoBlob = photo.getBlob(
			photo.getColumnIndex(Photo.PHOTO));
	final Bitmap photoBitmap = BitmapFactory.decodeByteArray(
			photoBlob, 0, photoBlob.length);
	doSomethingWithAContactPhoto(photoBitmap);
}
photo.close();

Querying all phone numbers of a contact

The phone numbers are stored in the “ContactsContract.Data” table. Every number is represented by one entry in this table. To access the columns used for phone numbers we can use the definitions in the “ContactsContract.CommonDataKinds.Phone” class. There are three different columns available: number, type and label. The type column is used to define the type of the number e.g. work, home or other. If the type other is defined the label column can be used to get the defined name of this type. In our example we will just look at the types which are defined so our projection does not contain the label column.

final String[] projection = new String[] {
		Phone.NUMBER,
		Phone.TYPE,
};

As an URI for the phone number entries we can use the “Phone.CONTENT_URI” URI which filters the data table according to the media type of phone numbers. Because we only want phone numbers of a specific contact we filter the results on the basis of the given contactId field.

final Cursor phone = managedQuery(
				Phone.CONTENT_URI,
				projection,
				Data.CONTACT_ID + "=?",
				new String[]{String.valueOf(contactId)},
				null);

Because there can be multiple entries we use a while loop to iterate over this cursor. To get a human readable version of the phone number type we use the getTypeLabelResource method to get the resource id of the label for a specific type.

if(phone.moveToFirst()) {
	final int contactNumberColumnIndex = phone.getColumnIndex(Phone.NUMBER);
	final int contactTypeColumnIndex = phone.getColumnIndex(Phone.TYPE);

	while(!phone.isAfterLast()) {
		final String number = phone.getString(contactNumberColumnIndex);
		final int type = phone.getInt(contactTypeColumnIndex);
		final int typeLabelResource = Phone.getTypeLabelResource(type);
		doSomethingWithAContactPhoneNumber(number, typeLabelResource);
		phone.moveToNext();
	}

}
phone.close();

Querying all email addresses of a contact

The email addresses of a contact are also stored in the “ContactsContract.Data” table. To retrieve the desired information we use the defined column names and URI in the “CommonDataKinds.Email” class. With API level 11 an extra field was added to this class to represent the address of the email but as this example is designed for API level 5+ we use the old field.

final String[] projection = new String[] {
		Email.DATA,			// use Email.ADDRESS for API-Level 11+
		Email.TYPE
};

The type of the email address is implemented like the type of phone numbers and using the getTypeLabelResource method we can retrieve a human readable label resource id of the type.  So now we can create our cursor to retrieve all available email addresses.

final Cursor email = managedQuery(
		Email.CONTENT_URI,
		projection,
		Data.CONTACT_ID + "=?",
		new String[]{String.valueOf(contactId)},
		null);

Just like we did process the results of the phone query we can iterate over the email cursor to extract every address.

if(email.moveToFirst()) {
	final int contactEmailColumnIndex = email.getColumnIndex(Email.DATA);
	final int contactTypeColumnIndex = email.getColumnIndex(Email.TYPE);

	while(!email.isAfterLast()) {
		final String address = email.getString(contactEmailColumnIndex);
		final int type = email.getInt(contactTypeColumnIndex);
		final int typeLabelResource = Email.getTypeLabelResource(type);
		doSomethingWithAContactEmailAddress(address, typeLabelResource);
		email.moveToNext();
	}

}
email.close();

Summary and Look-out

The presented ways are implemented in the example App which will query the details and display the information in a GUI. Using this App you can easily try the functions out. Other information such as the address, instant messenger, notes, etc. can easily be queried analogous to the way shown above. For further data fields just look at the classes in the “android.provider.ContactsContract.CommonDataKinds” package.

Please note that the example App is not written with the best performance in mind but to show a clear and easy understandable way to access information about contacts.

by Kevin Kratzer