| page.title=Using the Contacts API |
| parent.title=Articles |
| parent.link=../browser.html?tag=article |
| @jd:body |
| |
| <p>Starting from Android 2.0 (API Level 5), the Android platform provides an |
| improved Contacts API for managing and integrating contacts from multiple |
| accounts and from other data sources. To handle overlapping data from multiple |
| sources, the contacts content provider aggregates similar contacts and presents |
| them to users as a single entity. This article describes how to use the new API |
| to manage contacts.</p> |
| |
| <p>The new Contacts API is defined in the |
| {@link android.provider.ContactsContract android.provider.ContactsContract} |
| and related classes. The older API is still supported, although deprecated. |
| If you have an existing application that uses the older API, |
| see <a href="#legacy">Considerations for legacy apps</a>, below, for ideas |
| on how to support the Contacts API in your app.</p> |
| |
| <p>If you'd like to look at an applied example of how to use the new Contacts |
| API, including how to support both the new and older API in a single app, |
| please see the <a |
| href="{@docRoot}resources/samples/BusinessCard/index.html">Business Card |
| sample application</a>.</p> |
| |
| <h3>Data structure of Contacts</h3> |
| |
| <p>In the new Contacts API, data is laid out in three primary tables: |
| <em>contacts</em>, <em>raw contacts</em>, and <em>data</em>, a structure that |
| is slightly different from that used in the older API. The new structure |
| allows the system to more easily store and manage information for a |
| specific contact from multiple contacts sources. </p> |
| |
| <img style="margin: 0px auto 10px; display: block; text-align: center; width: 471px; height: 255px;" src="images/contacts-2.png" alt="" border="0"> |
| |
| <ul> |
| <li><code>Data</code> is a generic table that stores all of the data points |
| associated with a raw contact. Each row stores data of a specific kind — |
| for example name, photo, email addresses, phone numbers, and group memberships. |
| Each row is tagged with a MIME type to identify what type of data it can |
| contain, across the entire column. Columns are generic and the type of data they |
| contain is determined by the kind of data stored in each row. For example, if a |
| row's data kind is <code>Phone.CONTENT_ITEM_TYPE</code>, then the first column |
| stores the phone number, but if the data kind is |
| <code>Email.CONTENT_ITEM_TYPE</code>, then the column stores the email address. |
| |
| <p>The {@link android.provider.ContactsContract.CommonDataKinds ContactsContract.CommonDataKinds} |
| class provides subclasses corresponding to common MIME types for contacts data. |
| If needed, your application or other contacts sources can define additional MIME |
| types for data rows. For more information about the Data table and examples of |
| how to use it, see {@link android.provider.ContactsContract.Data android.provider.ContactsContract.Data}.</p></li> |
| |
| <li>A row in the <code>RawContacts</code> table represents the set of |
| <code>Data</code> and other information describing a person and associated with |
| a single contacts source. For example, a row might define the data associated |
| with a person's Google or Exchange account or Facebook friend. For more |
| information, see |
| {@link android.provider.ContactsContract.RawContacts ContactsContract.RawContacts}.</p> |
| |
| <li>A row in the <code>Contacts</code> table represents an aggregate of one or |
| more <code>RawContacts</code> describing the same person (or entity). |
| |
| <p>As mentioned above, the Contacts content provider automatically aggregates |
| Raw Contacts into a single Contact entry, where possible, since common data |
| fields (such as name or email address) are likely to be stored in each raw |
| contact. Since the aggregation logic maintains the entries in the Contact rows, |
| the entries can be read but should not be modified. See the section <a |
| href="#aggregation">Aggregation of contacts</a>, below, for more details, |
| including and information on how to |
| control aggregation.</li> |
| |
| </ul> |
| |
| <p>When displaying contacts to users, applications should typically operate on |
| the Contacts level, since it provides a unified, aggregated view of contacts |
| from various underlying sources. </p> |
| |
| <h4>Example: Inserting a Phone Number</h4> |
| |
| <p>To insert a phone number using the new APIs you'll need the ID of the Raw |
| Contact to attach the phone number to, then you'll need to create a Data |
| row:</p> |
| |
| <pre>import android.provider.ContactsContract.CommonDataKinds.Phone; |
| ... |
| ContentValues values = new ContentValues(); |
| values.put(Phone.RAW_CONTACT_ID, rawContactId); |
| values.put(Phone.NUMBER, phoneNumber); |
| values.put(Phone.TYPE, Phone.TYPE_MOBILE); |
| Uri uri = getContentResolver().insert(Phone.CONTENT_URI, values);</pre> |
| |
| |
| <h3 id="aggregation">Aggregation of contacts</h3> |
| |
| <p>When users sync contacts from multiple sources, several contacts might refer |
| to the same person or entity, but with slightly different (or overlapping) data. |
| For example, "Bob Parr" might be a user's co-worker and also his personal |
| friend, so the user might have his contact information stored in both a |
| corporate email account and a personal account. To provide a simplified view for |
| the user, the system locates such overlapping contacts and combines them into a |
| single, aggregate contact. </p> |
| |
| <p>The system automatically aggregates contacts by default. However, if needed, |
| your application can control how the system handles aggregation or it can |
| disable aggregation altogether, as described in the sections below.</p> |
| |
| <h4>Automatic aggregation</h4> |
| |
| <p>When a raw contact is added or modified, the system looks for matching |
| (overlapping) raw contacts with which to aggregate it. It may not find any |
| matching raw contacts, in which case it will create an aggregate contact that |
| contains just the original raw contact. If it finds a single match, it creates a |
| new contact that contains the two raw contacts. And it may even find multiple |
| similar raw contacts, in which case it chooses the closest match. </p> |
| |
| <p>Two raw contacts are considered to be a match if at least one of these |
| conditions is met:</p> |
| |
| <ul> |
| <li>They have matching names.</li> |
| <li>Their names consist of the same words but in different order |
| (for example, "Bob Parr" and "Parr, Bob")</li> |
| <li>One of them has a common short name for the other (for example, |
| "Bob Parr" and "Robert Parr")</li> |
| <li>One of them has just a first or last name and it matches the other |
| raw contact. This rule is less reliable, so it only applies if the two |
| raw contacts are also sharing some other data like a phone number, an |
| email address or a nickname (for example, Helen ["elastigirl"] = Helen |
| Parr ["elastigirl"])</li> |
| <li>At least one of the two raw contacts is missing the name altogether |
| and they are sharing a phone number, an email address or a nickname (for |
| example, Bob Parr [incredible@android.com] = incredible@android.com).</li> |
| </ul> |
| |
| <p>When comparing names, the system ignores upper/lower case differences |
| (Bob=BOB=bob) and diacritical marks (Hélène=Helene). When comparing two |
| phone numbers the system ignores special characters such as "*", "#", |
| "(", ")", and whitespace. Also if the only difference between two numbers |
| is that one has a country code and the other does not, then the system |
| considers those to be a match (except for numbers in the Japan country code).</p> |
| |
| <p>Automatic aggregation is not permanent; any change of a constituent raw |
| contact may create a new aggregate or break up an existing one.</p> |
| |
| <h4>Explicit aggregation</h4> |
| |
| <p>In some cases, the system's automatic aggregation may not meet the |
| requirements of your application or sync adapter. There are two sets of APIs you |
| can use to control aggregation explicitly: <em>aggregation modes</em> allow you |
| to control automatic aggregation behaviors and <em>aggregation exceptions</em> |
| allow you to override automated aggregation entirely. |
| |
| <p><strong>Aggregation modes</strong></p> |
| |
| <p>You can set an aggregation mode for each raw contact individually. To do so, |
| add a mode constant as the value of the <code>AGGREGATION_MODE column</code> in |
| the <code>RawContact</code> row. The mode constants available include: </p> |
| |
| <ul> |
| <li><code>AGGREGATION_MODE_DEFAULT</code> — normal mode, automatic |
| aggregation is allowed.</li> |
| <li><code>AGGREGATION_MODE_DISABLED</code> — automatic aggregation is not |
| allowed. The raw contact will not be aggregated.</li> |
| <li><code>AGGREGATION_MODE_SUSPENDED</code> — automatic aggregation is |
| deactivated. If the raw contact is already a part of an aggregated contact when |
| aggregation mode changes to suspended, it will remain in the aggregate, even if |
| it changes in such a way that it no longer matches the other raw contacts in the |
| aggregate.</li> |
| </ul> |
| |
| <p><strong>Aggregation exceptions</strong></p> |
| |
| <p>To keep two raw contacts unconditionally together or unconditionally apart, |
| you can add a row to the |
| {@link android.provider.ContactsContract.AggregationExceptions} table. Exceptions |
| defined in the table override all automatic aggregation rules. </p> |
| |
| |
| <h3>Loookup URI</h3> |
| |
| <p>The new Contacts API introduces the notion of a lookup key for a contact. If |
| your application needs to maintain references to contacts, you should use lookup |
| keys instead of the traditional row ids. You can acquire a lookup key from the |
| contact itself, it is a column on the |
| {@link android.provider.ContactsContract.Contacts} table. Once you have a lookup key, |
| you can construct a URI in this way:</p> |
| |
| <pre>Uri lookupUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey)</pre> |
| |
| <p>and use it like you would use a traditional content URI, for example: </p> |
| |
| <pre>Cursor c = getContentResolver().query(lookupUri, new String[]{Contacts.DISPLAY_NAME}, ...); |
| try { |
| c.moveToFirst(); |
| String displayName = c.getString(0); |
| } finally { |
| c.close(); |
| }</pre> |
| |
| <p>The reason for this complication is that regular contact row IDs are |
| inherently volatile. Let's say your app stored a long ID of a contact. Then the |
| user goes and manually joins the contact with some other contact. Now there is a |
| single contact where there used to be two, and the stored long contact ID points |
| nowhere. |
| |
| <p>The lookup key helps resolve the contact in this case. The key is a string |
| that concatenates the server-side identities of the raw contacts. Your |
| application can use that string to find a contact, regardless whether the raw |
| contact is aggregated with others or not. </p> |
| |
| <p>If performance is a concern for your application, you might want to store |
| both the lookup and the long ID of a contact and construct a lookup URI out of |
| both IDs, as shown here:</p> |
| |
| <pre>Uri lookupUri = getLookupUri(contactId, lookupKey)</pre> |
| |
| <p>When both IDs are present in the URI, the system will try to use the long ID |
| first. That is a very quick query. If the contact is not found, or if the one |
| that is found has the wrong lookup key, the content provider will parse the |
| lookup key and track down the constituent raw contacts. If your app |
| bulk-processes contacts, you should maintain both IDs. If your app works with a |
| single contact per user action, you probably don't need to bother with storing |
| the long ID.</p> |
| |
| Android itself uses lookup URIs whenever there is a need to reference a contact, |
| such as with shortcuts or Quick Contact, and also during editing or even viewing |
| a contact. The latter case is less obvious — why would a contact ID change |
| while we are simply viewing the contact? It could change because there might be |
| a sync going in the background, and the contact might get automatically |
| aggregated with another while being viewed.</p> |
| |
| <p>In summary: whenever you need to reference a contact, we recommend that you |
| do so by its lookup URI.</p> |
| |
| |
| <h3 id="legacy">Considerations for legacy applications</h3> |
| |
| <p>If you have an existing application that uses the older Contacts API, |
| you should consider upgrading it to use the new API. You have four options: </p> |
| |
| <ul> |
| <li>Leave it as-is and rely on the Contacts compatibility mode.</li> |
| <li>Upgrade the app and discontinue support of pre-Android 2.0 platforms.</li> |
| <li>Build a new version of the app for the new API, while keeping the old version available.</li> |
| <li>Make the app use the right set of APIs depending on the platform where it is deployed. </li> |
| </ul> |
| |
| <p>Let's consider these options one by one.</p> |
| |
| <h4>Using compatibility mode</h4> |
| |
| <p>Compatibility mode is the easiest option because you just leave the |
| application as is, and it should run on Android 2.0 as long as it only uses |
| public APIs. A couple examples of the use of non-public API include the use of |
| explicit table names in nested queries and the use of columns that were not |
| declared as public constants in the {@link android.provider.Contacts} class. |
| </p> |
| |
| <p>Even if the application currently runs, you don't want to leave it like this |
| for long. The main reason is that it will only have access to contacts from one |
| account, namely the first Google account on the device. If the user opens other |
| accounts in addition to or instead of a Google account, your application will |
| not be able to access those contacts.</p> |
| |
| |
| <h4>Upgrading to the new API and dropping support for older platforms</h4> |
| |
| <p>If your application will no longer target platforms older than |
| Android 2.0, you can upgrade to the new API in this way:</p> |
| |
| <ul> |
| <li>Replace all usages of {@link android.provider.Contacts} with calls to new |
| API. After you are done, you should not see any deprecation warnings during |
| application build. The new application will be able to take full advantage of |
| multiple accounts and other new features of Android 2.0. </p> |
| |
| <li>In the application's manifest, update (or add) the |
| <code>android:minSdkVersion</code> attribute to the |
| <code><uses-sdk></code> element. To use the new Contacts API, you should |
| set the value of the attribute to "5" (or higher, as appropriate). For more |
| information about <code>android:minSdkVersion</code>, see the documentation for |
| the <a |
| href="{@docRoot}guide/topics/manifest/uses-sdk-element.html"><code><uses-sdk></code></a> |
| element. For more information about the value of the |
| <code>minSdkVersion</code>, see <a |
| href="{@docRoot}guide/appendix/api-levels.html">API Levels</a>.</li> |
| </ul> |
| |
| <h4>Maintaining two applications</h4> |
| |
| <p>You may decide to have two different applications: one for pre-Android 2.0 |
| platforms and one for Android 2.0 and beyond. If so, here's what you'll need to do:</p> |
| |
| <ul> |
| <li>Clone your existing app. </li> |
| <li>Change the old application: </li> |
| <ul> |
| <li>At launch time, check the version of the SDK. The version of the SDK |
| is available as {@link android.os.Build.VERSION#SDK android.os.Build.VERSION.SDK}.</li> |
| <li>If the SDK version is greater or equal to 5 (Android 2.0), show a dialog |
| suggesting to the user that it's time to go to Google Play and find a new version of |
| the app. You can even provide a link to the new app on Google Play (see <a |
| href="{@docRoot}guide/publishing/publishing.html#marketintent">Using Intents |
| to Launch Google Play</a>). </li> |
| </ul> |
| <li>Change the new application:</li> |
| <ul> |
| <li>Replace all usages of the older Contacts API with calls to new API. |
| The new application will be able to take full advantage of multiple accounts |
| and other new features of Android 2.0. </li> |
| <li>Modify that application's AndroidManifest.xml file: </li> |
| <ul> |
| <li>Give the application a new name and a new package name. Currently |
| Google Play does not allow you to have two applications with the same |
| name/package.</li> |
| <li>Update (or add) the <code>android:minSdkVersion</code> attribute |
| to the <code><uses-sdk></code> element. To use the new Contacts API, |
| you should set the value of the attribute to "5" (or higher, as appropriate).</li> |
| </ul> |
| </ul> |
| <li>Publish both apps on Google Play, the old app one as an upgrade and the |
| other as new. Make sure to explain the difference between the apps in their |
| descriptions.</li> |
| </ul> |
| |
| <p>This plan has its disadvantages: </p> |
| |
| <ul> |
| <li>The new application will not be able to read the old application's data. |
| Application data can only be accessed by code living in the same package. So |
| databases, shared preferences, and so on, will need to be populated from |
| scratch.</li> |
| <li>The upgrade process is too clunky for the user. Some users may choose |
| to either stay with the crippled old version or uninstall altogether.</li> |
| </ul> |
| |
| <h4>Supporting the old and new APIs in the same application</h4> |
| |
| <p>This is a bit tricky, but the result is worth the effort. You can |
| build a single package that will work on any platform:</p> |
| |
| <p>Go through the existing application and factor out all access to |
| {@link android.provider.Contacts} into one class, such as ContactAccessorOldApi. |
| For example, if you have code like this: |
| |
| <pre> protected void pickContact() { |
| startActivityForResult(new Intent(Intent.ACTION_PICK, People.CONTENT_URI), 0); |
| }</pre> |
| |
| <p>it will change to:</p> |
| |
| |
| <pre> private final ContactAccessorOldApi mContactAccessor = new ContactAccessorOldApi(); |
| |
| void pickContact() { |
| startActivityForResult(mContactAccessor.getContactPickerIntent(), 0); |
| }</pre> |
| |
| <p>The corresponding method on ContactAccessorOldApi will look like this:</p> |
| |
| <pre> public Intent getContactPickerIntent() { |
| return new Intent(Intent.ACTION_PICK, People.CONTENT_URI); |
| }</pre> |
| |
| <p>Once you are done, you should see deprecation warnings coming only |
| from ContactAccessorOldApi. </p> |
| |
| <p>Create a new abstract class ContactAccessor, make sure the abstract |
| class has all method signatures from ContactAccessorOldApi. Make |
| ContactAccessorOldApi extend ContactAccessor:</p> |
| |
| <pre> public abstract class ContactAccessor { |
| public abstract Intent getContactPickerIntent(); |
| ... |
| }</pre> |
| |
| <p>Create a new subclass of ContactAccessor, ContactAccessorNewApi and |
| implement all methods using the new API:</p> |
| |
| <pre> public class ContactAccessorNewApi extends ContactAccessor { |
| @Override |
| public Intent getContactPickerIntent() { |
| return new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI); |
| } |
| ... |
| }</pre> |
| |
| <p>At this point, you have two implementations of the same API, one using the |
| old API and another using the new API. Let's plug them in. Add this code to |
| the ContactAccessor class:</p> |
| |
| <pre> private static ContactAccessor sInstance; |
| |
| public static ContactAccessor getInstance() { |
| if (sInstance == null) { |
| String className; |
| int sdkVersion = Integer.parseInt(Build.VERSION.SDK); |
| if (sdkVersion < Build.VERSION_CODES.ECLAIR) { |
| className = "ContactAccessorOldApi"; |
| } else { |
| className = "ContactAccessorNewApi"; |
| } |
| try { |
| Class<? extends ContactAccessor> clazz = |
| Class.forName(ContactAccessor.class.getPackage() + "." + className) |
| .asSubclass(ContactAccessor.class); |
| sInstance = clazz.newInstance(); |
| } catch (Exception e) { |
| throw new IllegalStateException(e); |
| } |
| } |
| return sInstance; |
| }</pre> |
| |
| <p>Now replace references to ContactsAccessorOldApi with references to |
| ContactsAccessor:</p> |
| |
| <pre> private final ContactAccessor mContactAccessor = ContactAccessor.getInstance();</pre> |
| |
| <p>You are done! Now you will want to test on Android 2.0, 1.6 and 1.5.</p> |
| |
| <p>We hope you like the new features and APIs we've added to Contacts in |
| Android 2.0, and we can't wait to see what cool things developers do with |
| the new APIs.</p> |