Using the Contacts Provider

Access to the contact manager is particularly useful on a communications device. Android does the right thing by exposing all the information available from the contacts database to any application granted the READ_CONTACTS permission.

Android 2.0 (API level 5) introduced the ContactsContract class, which superceded the Contacts class that had previously been used to store and manage the contacts stored on the device.

The new contact Content Provider extends the scope of contacts management in Android by providing an extensible database of contact-related information. This allows users to specify multiple sources for their contact information. More importantly for us, it allows developers to arbitrarily extend the data stored against each contact, or even become an alternative provider for contacts and contact details.

Introducing the Contacts Contract Content Provider

The Contacts Contract Content Provider is an extensible database of contact-related information.

Rather than using a single well-defined table of contact detail columns, the Contacts Contract provider uses a three-tier data model to store data, associate it with a contact, and aggregate it to a single person using the following ContactsContract subclasses:

> Data In the underlying table, each row defines a set of personal data (e.g., phone numbers, e-mail addresses, etc.), separated by MIME type. While there is a predefined set of common column names for each personal data-type (available, along with the appropriate MIME types from subclasses within ContactsContract.CommonDataKinds), this table can be used to store any value.

Importantly, the kind of data stored in a particular row is determined by the MIME type specified for that row. A series of generic columns is used to store up to 15 different pieces of data varying by data type.

When adding new data to the Data table, you specify a Raw Contact to which a set of data will be associated.

> RawContacts From Android 2.0 onwards, users can specify multiple contact accounts (e.g., Gmail, Facebook, etc.). Each row in the Raw Contacts table defines an account to which a set of Data values is associated.

> Contacts The Contacts table aggregates rows from Raw Contacts that all describe the same person.

Typically you will use the Data table to add, delete, or modify data stored against an existing contact account, the Raw Contacts table to create and manage accounts, and both the Contact and Data tables to query the database and extract contact details.

Reading Contact Details

You can use the Content Resolver to query any of the three Contact Contracts tables described above using the CONTENT_URI static constant available from each class. Each class includes a number of static properties that describe the column names included in the underlying tables.

In order to access any contact details you need to include the READ_CONTACTS uses-permission in your application manifest:

<uses-permission android:name="android.permission.READ_CONTACTS"/>

Listing 7-19 queries the Contacts table for a Cursor to every person in the address book, creating an array of strings that holds each contact's name and unique ID.

LISTING 7-19: Accessing the contact Content Provider

Available for download on Wrox.com

// Get a cursor over every aggregated contact. Cursor cursor =

getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);

// Let the activity manage the cursor lifecycle. startManagingCursor(cursor);

// Use the convenience properties to get the index of the columns int nameldx =

cursor.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME); int idldx = cursor. getColumnIndexOrThrow(ContactsContract.Contacts._ID);

String[] result = new String[cursor.getCount()]; if (cursor.moveToFirst()) do {

// Extract the name.

String name = cursor.getString(nameldx);

// Extract the phone number.

String id = cursor.getString(idldx);

result[cursor.getPosition()] = name + " (" + id + ")"; } while(cursor.moveToNext());

stopManagingCursor(cursor);

The ContactsContract.Data Content Provider is used to store all the contact details — such as addresses, phone numbers, and e-mail addresses — making it the best approach when searching for one of these details.

The Data table is also used for finding details for a given contact. In most cases, you will likely be querying for contact details based on a full or partial contact name.

To simplify this lookup, Android provides the ContactsContract.Contacts.CONTENT_FILTER_URI query URI. Append the full or partial name to lookup as an additional path segment to the URI. To extract the associated contact details, find the _ID value from the returned Cursor and use it to create a query on the Data table.

The content of each column with a row in the Data table depends on the MIME type specified for that row. As a result, any query on the Data table must filter the rows by MIME-type in order to meaningfully extract data.

Listing 7-20 shows how to use the contact-detail column names available in the CommonDataKinds subclasses to extract the display name and mobile phone number from the Data table for a particular contact.

LISTING 7-20: Finding contact details after finding a contact

Available for download on // Find a contact using a partial name match Wrox.com uri lookupUri =

Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_FILTER_URI, "kristy");

Cursor idCursor = getContentResolver().query(lookupUri, null, null, null, null);

String id = null; if (idCursor.moveToFirst()) {

int idIdx = idCursor.getColumnIndexOrThrow(ContactsContract.Contacts._ID); id = idCursor.getString(idIdx);

// Return all the contact details of type PHONE for the contact we found String where = ContactsContract.Data.CONTACT_ID + " = " + id + " AND " + ContactsContract.Data.MIMETYPE +"='"+

ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE +

Cursor dataCursor = getContentResolver().query(ContactsContract.Data.CONTENT_URI, null, where, null, null);

// Use the convenience properties to get the index of the columns int nameIdx =

dataCursor.getColumnIndexOrThrow(ContactsContract.Data.DISPLAY_NAME); int phoneIdx =

dataCursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER)

String[] result = new String[dataCursor.getCount()]; if (dataCursor.moveToFirst()) do {

// Extract the name.

String name = dataCursor.getString(nameldx); // Extract the phone number.

String number = dataCursor.getString(phoneldx);

result[dataCursor.getPosition()] = name + " (" + number + ")"; } while(dataCursor.moveToNext()); dataCursor.close();

The Contacts sub-class also offers a phone number lookup URI to help find a contact associated with a particular phone number. This query is highly optimized to return fast results for incoming caller-ID notification.

Use ContactsContract.PhoneLookup.CONTENT_FILTER_URI, appending the number to find as an additional path segment, as shown in Listing 7-21.

STING 7-21: Performing a caller-ID lookup

String incomingNumber = "5551234"; Uri lookupUri =

Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, incomingNumber);

Cursor idCursor = getContentResolver().query(lookupUri, null, null, null, null);

if (idCursor.moveToFirst()) { int nameIdx =

idCursor.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME); String caller = idCursor.getString(nameldx);

Toast.makeText(getApplicationContext(), caller, Toast.LENGTH_LONG).show();

idCursor.close();

In addition to the static contact details described above, the ContactsContract.StatusUpdates table contains social status updates and instant messenger availability. Using this table you can look up or modify the status, and presence, of any contact who has an associated social networking and/or instant messaging account.

Modifying and Augmenting Contact Details

As well as querying the contacts database, you can use these Content Providers to modify, delete, or insert contact records after adding the write_contacts uses-permission to your application manifest.

The extensible nature of the Contacts Contract provider allows you to add arbitrary Data table rows to any account stored as a Raw Contact. In practice it is poor form to extend a third-party account with custom data as it will be unable to synchronize your custom data with its online server.

Better practice is to create your own syncing contact adapter that will be aggregated with the other third-party account details.

The process for creating your own syncing contact account adapter is beyond the scope of this book. However, in general terms, by creating a record in the Raw Contacts provider it's possible for you to create a contacts account type for your own custom data.

You can add new records into the contacts Data provider that are associated with your custom contact account. Once added, your custom contact data will be aggregated with the details provided by native and other third-party contact information adapters and made available when developers query the Contacts Content Provider as described in the previous section.

Available for download on Wrox.com

SUMMARY

In this chapter you learned how to add a robust persistence layer to your applications and access native and third-party Content Providers.

Android provides a fully featured SQLite RDBMS to all applications. This small, efficient, and robust database library lets you create relational databases to persist application data. Using Content Providers, you learned how to share private data, particularly databases, across application boundaries.

All database and Content Provider queries are returned as Cursors; you learned how to perform queries and extract data from the resulting Cursor objects.

Along the way you also learned to:

> Create new SQLite databases

> Interact with databases to insert, update, and delete rows

> Use the native Content Providers included with Android to access and manage native data like media and contacts

Now that you have a solid foundation in the fundamentals of Android development, the remainder of this book will investigate some of the more interesting optional Android features.

Starting in the next chapter you'll be introduced to the geographic APIs. Android offers a rich suite of geographical functionality, including location-based services (such as GPS) and forward and reverse geocoding, as well as a fully integrated Google maps implementation. Using Google maps you can create map-based Activities that feature annotations to develop native map-mashups.

Mobile Apps Made Easy

Mobile Apps Made Easy

Quick start guide to skyrocket your offline and online business success with mobile apps. If you know anything about mobile devices, you’ve probably heard that famous phrase coined by one of the mobile device’s most prolific creators proclaiming that there’s an app for pretty much everything.

Get My Free Training Guide


Responses

  • osman
    How to listen contact inserted/updated/deleted in address book?
    2 years ago
  • bisrat
    What is contacts provider?
    1 year ago

Post a comment