Android Contacts Explained

April 20, 2012 Greg Burgoon

The introduction of apps and data plans have opened the doors for contacts to be used in new and unexpected ways. For example, you can use contacts to instant message friends using a data plan, or you can use contacts as a way to video conference with colleagues. A contact list is a list of people, so it should expose all the possible ways that you can communicate with those people.

The Android platform gives you amazing control over a device’s contact list, but with great power, comes great responsibility. The power of Android Contacts is hidden/muffled by the poorly documented and confusing Contacts API. It is quite easy to implement a feature 5 different ways with the Contacts API, and have them all do the same thing (or sometimes….appear to do the same thing). If you have ever been plagued by the mysteries of the Contacts API, and have been looking for that Rosetta Stone, look no further. This is it.

Taming the beast

The key to taming this monster of an API is the use of content providers. Content providers allows you to organize data in a way that makes the most sense in the context of your application. Content providers serve 3 core purposes when working with contacts.

1. Perform aggregation on contacts data. This is important when you want to aggregate contact information with other information (such as a phone event). It will simplify the access of multiple data sources for your application.

For example, lets say you have a CustomContactsContentProvider that looks like this:

public class CustomContactsContentProvider {

public static final String PATH = "com.company.CustomContactsContentProvider";
public static final Uri CONTENT_URI = Uri.parse("content://" + PATH + "/");
public static final String CONTACTS_URI_SUFFIX = "users";
public static final String MIMETYPE = "vnd.android.cursor.item/customcontact";

public static class Columns implements BaseColumns {
public static final String TYPE = ContactsContract.Data.MIMETYPE;
public static final String UID = ContactsContract.Data.DATA1;
public static final String PIC_URL = ContactsContract.Data.DATA2;
public static final String NAME = ContactsContract.Data.DATA3;
public static final String PHONE = ContactsContract.Data.DATA4;
public static final String EMAIL = ContactsContract.Data.DATA5;
}

public static interface Query {
public static String[] PROJECTION = { Columns._ID, Columns.TYPE, Columns.UID, Columns.PIC_URL, Columns.NAME,  Columns.PHONE, Columns.EMAIL };

public static final String FILTER = Columns.NAME + " IS NOT NULL";
}
}

Lets say you also have a content provider that looks like this:

public class CustomEventsContentProvider {

public static final String PATH = "com.company.CustomEventsContentProvider";
public static final Uri CONTENT_URI = Uri.parse("content://" + PATH + "/");
public static final String EVENTS_URI_SUFFIX = "events";
public static final String MIMETYPE = "vnd.android.cursor.item/event";

public static class Columns implements BaseColumns {
public static final String PHONE_NUMBER = "phone_number";
public static final String VIEWED = "viewed";
public static final String EVENT_TYPE = "event_type";
public static final String DATE = "date";
public static final String DATA = "data";
}
public static interface Query {
public static String[] PROJECTION = { Columns._ID, Columns.PHONE_NUMBER, Columns.VIEWED, Columns.EVENT_TYPE, Columns.DATE,  Columns.DATA};

public static final String FILTER = Columns.PHONE_NUMBER + " IS NOT NULL";
}
}

With content providers, can have multiple tables in your database to manage aggregation. Lets say we want to display an event from the CustomEventsContentProvider, but we want to display it along with the image/data from the CustomContactsContentProvider. You can have an aggregation table (lets call it “events_with_contacts”), in the CustomEventsContentProvider. All the CustomEventsContentProvider has to do it listen to the CustomContactsContentProvider for changes, in order to stay in sync. Which can be done like so:


contentObserver = new ContentObserver(mHandler) {

@Override

public void onChange(boolean selfChange) {

//update aggregation table with new contacts

}

};
getContext().getContentResolver().registerContentObserver(CustomContactsContentProvider.CONTENT_URI, true, contentProvider);

2. Improve readability of code. With content providers, you can create aliases to the horrid DATA fields. This will make your cursor queries easier to read and understand. (ie. ContactsContract.DataColumns.DATA3 can be aliased to CustomContentProvider.USER_PROFILE_PIC).

3. Easy integration with Android Syncing. Android has the concept of a sync adapter. Sync adapters are used to sync content from the web, to your phone. It is highly likely that you will want to keep your custom contacts up to date with data on your server. If you have a content provider to house your content, it will make interacting with the sync adapter much easier, as sync adapters were designed to work with content providers.

Overall, Android Contacts are extremely powerful. They improve the capabilities of communication on your phone. Just make sure to keep your code maintainable via content providers.

About the Author

Biography

Previous
Cloud Foundry at the OpenStack Design Summit
Cloud Foundry at the OpenStack Design Summit

The Cloud Foundry development team presented Cloud Foundry BOSH at the OpenStack Design Summit on Wednesday...

Next
Protected: Using JRuby for Rails Applications on Cloud Foundry
Protected: Using JRuby for Rails Applications on Cloud Foundry

There is no excerpt because this is a protected post.