• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1page.title=Using the Contacts API
2@jd:body
3
4<p>Starting from Android 2.0 (API Level 5), the Android platform provides an
5improved Contacts API for managing and integrating contacts from multiple
6accounts and from other data sources. To handle overlapping data from multiple
7sources, the contacts content provider aggregates similar contacts and presents
8them to users as a single entity. This article describes how to use the new API
9to manage contacts.</p>
10
11<p>The new Contacts API is defined in the
12{@link android.provider.ContactsContract android.provider.ContactsContract}
13and related classes. The older API is still supported, although deprecated.
14If you have an existing application that uses the older API,
15see <a href="#legacy">Considerations for legacy apps</a>, below, for ideas
16on how to support the Contacts API in your app.</p>
17
18<p>If you'd like to look at an applied example of how to use the new Contacts
19API, including how to support both the new and older API in a single app,
20please see the <a
21href="{@docRoot}resources/samples/BusinessCard/index.html">Business Card
22sample application</a>.</p>
23
24<h3>Data structure of Contacts</h3>
25
26<p>In the new Contacts API, data is laid out in three primary tables:
27<em>contacts</em>, <em>raw contacts</em>, and <em>data</em>, a structure that
28is slightly different from that used in the older API. The new structure
29allows the system to more easily store and manage information for a
30specific contact from multiple contacts sources. </p>
31
32<img style="margin: 0px auto 10px; display: block; text-align: center; width: 471px; height: 255px;" src="images/contacts-2.png" alt="" border="0">
33
34<ul>
35<li><code>Data</code> is a generic table that stores all of the data points
36associated with a raw contact. Each row stores data of a specific kind &mdash;
37for example  name, photo, email addresses, phone numbers, and group memberships.
38Each row is tagged with a MIME type to identify what type of data it can
39contain, across the entire column. Columns are generic and the type of data they
40contain is determined by the kind of data stored in each row. For example, if a
41row's data kind is <code>Phone.CONTENT_ITEM_TYPE</code>, then the first column
42stores the phone number, but if the data kind is
43<code>Email.CONTENT_ITEM_TYPE</code>, then the column stores the email address.
44
45<p>The {@link android.provider.ContactsContract.CommonDataKinds ContactsContract.CommonDataKinds}
46class provides subclasses corresponding to common MIME types for contacts data.
47If needed, your application or other contacts sources can define additional MIME
48types for data rows. For more information about the Data table and examples of
49how to use it, see {@link android.provider.ContactsContract.Data android.provider.ContactsContract.Data}.</p></li>
50
51<li>A row in the <code>RawContacts</code> table represents the set of
52<code>Data</code> and other information describing a person and associated with
53a single contacts source. For example, a row might define the data associated
54with a person's Google or Exchange account or Facebook friend. For more
55information, see
56{@link android.provider.ContactsContract.RawContacts ContactsContract.RawContacts}.</p>
57
58<li>A row in the <code>Contacts</code> table represents an aggregate of one or
59more <code>RawContacts</code> describing the same person (or entity).
60
61<p>As mentioned above, the Contacts content provider automatically aggregates
62Raw Contacts into a single Contact entry, where possible, since common data
63fields (such as name or email address) are likely to be stored in each raw
64contact. Since the aggregation logic maintains the entries in the Contact rows,
65the entries can be read but should not be modified.  See the section <a
66href="#aggregation">Aggregation of contacts</a>, below, for more details,
67including and information on how to
68control aggregation.</li>
69
70</ul>
71
72<p>When displaying contacts to users, applications should typically operate on
73the Contacts level, since it provides a unified, aggregated view of contacts
74from various underlying sources. </p>
75
76<h4>Example: Inserting a Phone Number</h4>
77
78<p>To insert a phone number using the new APIs you'll need the ID of the Raw
79Contact to attach the phone number to, then you'll need to create a Data
80row:</p>
81
82<pre>import android.provider.ContactsContract.CommonDataKinds.Phone;
83...
84ContentValues values = new ContentValues();
85values.put(Phone.RAW_CONTACT_ID, rawContactId);
86values.put(Phone.NUMBER, phoneNumber);
87values.put(Phone.TYPE, Phone.TYPE_MOBILE);
88Uri uri = getContentResolver().insert(Phone.CONTENT_URI, values);</pre>
89
90
91<h3 id="aggregation">Aggregation of contacts</h3>
92
93<p>When users sync contacts from multiple sources, several contacts might refer
94to the same person or entity, but with slightly different (or overlapping) data.
95 For example, "Bob Parr" might be a user's co-worker and also his personal
96friend, so the user might have his contact information stored in both a
97corporate email account and a personal account. To provide a simplified view for
98the user, the system locates such overlapping contacts and combines them into a
99single, aggregate contact.  </p>
100
101<p>The system automatically aggregates contacts by default. However, if needed,
102your application can control how the system handles aggregation or it can
103disable aggregation altogether, as described in the sections below.</p>
104
105<h4>Automatic aggregation</h4>
106
107<p>When a raw contact is added or modified, the system looks for matching
108(overlapping) raw contacts with which to aggregate it. It may not find any
109matching raw contacts, in which case it will create an aggregate contact that
110contains just the original raw contact. If it finds a single match, it creates a
111new contact that contains the two raw contacts. And it may even find multiple
112similar raw contacts, in which case it chooses the closest match. </p>
113
114<p>Two raw contacts are considered to be a match if at least one of these
115conditions is met:</p>
116
117<ul>
118<li>They have matching names.</li>
119<li>Their names consist of the same words but in different order
120(for example, "Bob Parr" and "Parr, Bob")</li>
121<li>One of them has a common short name for the other (for example,
122"Bob Parr" and "Robert Parr")</li>
123<li>One of them has just a first or last name and it matches the other
124raw contact. This rule is less reliable, so it only applies if the two
125raw contacts are also sharing some other data like a phone number, an
126email address or a nickname (for example, Helen ["elastigirl"] = Helen
127Parr ["elastigirl"])</li>
128<li>At least one of the two raw contacts is missing the name altogether
129and they are sharing a phone number, an email address or a nickname (for
130example, Bob Parr [incredible@android.com] = incredible@android.com).</li>
131</ul>
132
133<p>When comparing names, the system ignores upper/lower case differences
134(Bob=BOB=bob) and diacritical marks (Hélène=Helene). When comparing two
135phone numbers the system ignores special characters such as "*", "#",
136"(", ")", and whitespace. Also if the only difference between two numbers
137is that one has a country code and the other does not, then the system
138considers those to be a match (except for numbers in the Japan country code).</p>
139
140<p>Automatic aggregation is not permanent; any change of a constituent raw
141contact may create a new aggregate or break up an existing one.</p>
142
143<h4>Explicit aggregation</h4>
144
145<p>In some cases, the system's automatic aggregation may not meet the
146requirements of your application or sync adapter. There are two sets of APIs you
147can use to control aggregation explicitly: <em>aggregation modes</em> allow you
148to control automatic aggregation behaviors and <em>aggregation exceptions</em>
149allow you to override automated aggregation entirely.
150
151<p><strong>Aggregation modes</strong></p>
152
153<p>You can set an aggregation mode for each raw contact individually. To do so,
154add a mode constant as the value of the <code>AGGREGATION_MODE column</code> in
155the <code>RawContact</code> row. The mode constants available include: </p>
156
157<ul>
158<li><code>AGGREGATION_MODE_DEFAULT</code> &mdash; normal mode, automatic
159aggregation is allowed.</li>
160<li><code>AGGREGATION_MODE_DISABLED</code> &mdash; automatic aggregation is not
161allowed. The raw contact will not be aggregated.</li>
162<li><code>AGGREGATION_MODE_SUSPENDED</code> &mdash; automatic aggregation is
163deactivated. If the raw contact is already a part of an aggregated contact when
164aggregation mode changes to suspended, it will remain in the aggregate, even if
165it changes in such a way that it no longer matches the other raw contacts in the
166aggregate.</li>
167</ul>
168
169<p><strong>Aggregation exceptions</strong></p>
170
171<p>To keep two raw contacts unconditionally together or unconditionally apart,
172you can add a row to the
173{@link android.provider.ContactsContract.AggregationExceptions} table. Exceptions
174defined in the table override all automatic aggregation rules. </p>
175
176
177<h3>Loookup URI</h3>
178
179<p>The new Contacts API introduces the notion of a lookup key for a contact. If
180your application needs to maintain references to contacts, you should use lookup
181keys instead of the traditional row ids. You can acquire a lookup key from the
182contact itself, it is a column on the
183{@link android.provider.ContactsContract.Contacts} table. Once you have a lookup key,
184you can construct a URI in this way:</p>
185
186<pre>Uri lookupUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey)</pre>
187
188<p>and use it like you would use a traditional content URI, for example: </p>
189
190<pre>Cursor c = getContentResolver().query(lookupUri, new String[]{Contacts.DISPLAY_NAME}, ...);
191try {
192    c.moveToFirst();
193    String displayName = c.getString(0);
194} finally {
195    c.close();
196}</pre>
197
198<p>The reason for this complication is that regular contact row IDs are
199inherently volatile. Let's say your app stored a long ID of a contact. Then the
200user goes and manually joins the contact with some other contact. Now there is a
201single contact where there used to be two, and the stored long contact ID points
202nowhere.
203
204<p>The lookup key helps resolve the contact in this case. The key is a string
205that concatenates the server-side identities of the raw contacts. Your
206application can use that string to find a contact, regardless whether the raw
207contact is aggregated with others or not. </p>
208
209<p>If performance is a concern for your application, you might want to store
210both the lookup and the long ID of a contact and construct a lookup URI out of
211both IDs, as shown here:</p>
212
213<pre>Uri lookupUri = getLookupUri(contactId, lookupKey)</pre>
214
215<p>When both IDs are present in the URI, the system will try to use the long ID
216first. That is a very quick query. If the contact is not found, or if the one
217that is found has the wrong lookup key, the content provider will parse the
218lookup key and track down the constituent raw contacts. If your app
219bulk-processes contacts, you should maintain both IDs. If your app works with a
220single contact per user action, you probably don't need to bother with storing
221the long ID.</p>
222
223Android itself uses lookup URIs whenever there is a need to reference a contact,
224such as with shortcuts or Quick Contact, and also during editing or even viewing
225a contact. The latter case is less obvious &mdash; why would a contact ID change
226while we are simply viewing the contact? It could change because there might be
227a sync going in the background, and the contact might get automatically
228aggregated with another while being viewed.</p>
229
230<p>In summary: whenever you need to reference a contact, we recommend that you
231do so by its lookup URI.</p>
232
233
234<h3 id="legacy">Considerations for legacy applications</h3>
235
236<p>If you have an existing application that uses the older Contacts API,
237you should consider upgrading it to use the new API. You have four options: </p>
238
239<ul>
240<li>Leave it as-is and rely on the Contacts compatibility mode.</li>
241<li>Upgrade the app and discontinue support of pre-Android 2.0 platforms.</li>
242<li>Build a new version of the app for the new API, while keeping the old version available.</li>
243<li>Make the app use the right set of APIs depending on the platform where it is deployed. </li>
244</ul>
245
246<p>Let's consider these options one by one.</p>
247
248<h4>Using compatibility mode</h4>
249
250<p>Compatibility mode is the easiest option because you just leave the
251application as is, and it should run on Android 2.0 as long as it only uses
252public APIs. A couple examples of the use of non-public API include the use of
253explicit table names in nested queries and the use of columns that were not
254declared as public constants in the {@link android.provider.Contacts} class.
255</p>
256
257<p>Even if the application currently runs, you don't want to leave it like this
258for long. The main reason is that it will only have access to contacts from one
259account, namely the first Google account on the device. If the user opens other
260accounts in addition to or instead of a Google account, your application will
261not be able to access those contacts.</p>
262
263
264<h4>Upgrading to the new API and dropping support for older platforms</h4>
265
266<p>If your application will no longer target platforms older than
267Android 2.0, you can upgrade to the new API in this way:</p>
268
269<ul>
270<li>Replace all usages of {@link android.provider.Contacts} with calls to new
271API. After you are done, you should not see any deprecation warnings during
272application build. The new application will be able to take full advantage of
273multiple accounts and other new features of Android 2.0. </p>
274
275<li>In the application's manifest, update (or add) the
276<code>android:minSdkVersion</code> attribute to the
277<code>&lt;uses-sdk&gt;</code> element. To use the new Contacts API, you should
278set the value of the attribute to "5" (or higher, as appropriate). For more
279information about <code>android:minSdkVersion</code>, see the documentation for
280the <a
281href="{@docRoot}guide/topics/manifest/uses-sdk-element.html"><code>&lt;uses-sdk&gt;</code></a>
282element. For more information about the value of the
283<code>minSdkVersion</code>, see <a
284href="{@docRoot}guide/appendix/api-levels.html">API Levels</a>.</li>
285</ul>
286
287<h4>Maintaining two applications</h4>
288
289<p>You may decide to have two different applications: one for pre-Android 2.0
290platforms and one for Android 2.0 and beyond. If so, here's what you'll need to do:</p>
291
292<ul>
293  <li>Clone your existing app. </li>
294  <li>Change the old application: </li>
295    <ul>
296      <li>At launch time, check the version of the SDK. The version of the SDK
297is available as {@link android.os.Build.VERSION#SDK android.os.Build.VERSION.SDK}.</li>
298      <li>If the SDK version is greater or equal to 5 (Android 2.0), show a dialog
299suggesting to the user that it's time to go to Market and find a new version of
300the app. You can even provide a link to the new app on Market (see <a
301href="{@docRoot}guide/publishing/publishing.html#marketintent">Using Intents
302to Launch Market</a>). </li>
303    </ul>
304  <li>Change the new application:</li>
305    <ul>
306      <li>Replace all usages of the older Contacts API with calls to new API.
307The new application will be able to take full advantage of multiple accounts
308and other new features of Android 2.0. </li>
309      <li>Modify that application's AndroidManifest.xml file: </li>
310        <ul>
311          <li>Give the application a new name and a new package name. Currently
312Android Market does not allow you to have two applications with the same
313name/package.</li>
314          <li>Update (or add) the <code>android:minSdkVersion</code> attribute
315to the <code>&lt;uses-sdk&gt;</code> element. To use the new Contacts API,
316you should set the value of the attribute to "5" (or higher, as appropriate).</li>
317        </ul>
318      </ul>
319    <li>Publish both apps on Market, the old app one as an upgrade and the
320other as new. Make sure to explain the difference between the apps in their
321descriptions.</li>
322</ul>
323
324<p>This plan has its disadvantages: </p>
325
326<ul>
327<li>The new application will not be able to read the old application's data.
328Application data can only be accessed by code living in the same package. So
329databases, shared preferences, and so on, will need to be populated from
330scratch.</li>
331<li>The upgrade process is too clunky for the user. Some users may choose
332to either stay with the crippled old version or uninstall altogether.</li>
333</ul>
334
335<h4>Supporting the old and new APIs in the same application</h4>
336
337<p>This is a bit tricky, but the result is worth the effort. You can
338build a single package that will work on any platform:</p>
339
340<p>Go through the existing application and factor out all access to
341{@link android.provider.Contacts} into one class, such as ContactAccessorOldApi.
342For example, if you have code like this:
343
344<pre>    protected void pickContact() {
345        startActivityForResult(new Intent(Intent.ACTION_PICK, People.CONTENT_URI), 0);
346    }</pre>
347
348<p>it will change to:</p>
349
350
351<pre>    private final ContactAccessorOldApi mContactAccessor = new ContactAccessorOldApi();
352
353    void pickContact() {
354        startActivityForResult(mContactAccessor.getContactPickerIntent(), 0);
355    }</pre>
356
357<p>The corresponding method on ContactAccessorOldApi will look like this:</p>
358
359<pre>    public Intent getContactPickerIntent() {
360        return new Intent(Intent.ACTION_PICK, People.CONTENT_URI);
361    }</pre>
362
363<p>Once you are done, you should see deprecation warnings coming only
364from ContactAccessorOldApi. </p>
365
366<p>Create a new abstract class ContactAccessor, make sure the abstract
367class has all method signatures from ContactAccessorOldApi. Make
368ContactAccessorOldApi extend ContactAccessor:</p>
369
370<pre>    public abstract class ContactAccessor {
371        public abstract Intent getContactPickerIntent();
372        ...
373    }</pre>
374
375<p>Create a new subclass of ContactAccessor, ContactAccessorNewApi and
376implement all methods using the new API:</p>
377
378<pre>    public class ContactAccessorNewApi extends ContactAccessor {
379        &#64;Override
380        public Intent getContactPickerIntent() {
381            return new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI);
382        }
383        ...
384    }</pre>
385
386<p>At this point, you have two implementations of the same API, one using the
387old API and another using the new API. Let's plug them in. Add this code to
388the ContactAccessor class:</p>
389
390<pre>    private static ContactAccessor sInstance;
391
392    public static ContactAccessor getInstance() {
393        if (sInstance == null) {
394            String className;
395            int sdkVersion = Integer.parseInt(Build.VERSION.SDK);
396            if (sdkVersion &lt; Build.VERSION_CODES.ECLAIR) {
397                className = "ContactAccessorOldApi";
398            } else {
399                className = "ContactAccessorNewApi";
400            }
401            try {
402                Class&lt;? extends ContactAccessor&gt; clazz =
403                        Class.forName(ContactAccessor.class.getPackage() + "." + className)
404                                .asSubclass(ContactAccessor.class);
405                sInstance = clazz.newInstance();
406            } catch (Exception e) {
407                throw new IllegalStateException(e);
408            }
409        }
410        return sInstance;
411    }</pre>
412
413<p>Now replace references to ContactsAccessorOldApi with references to
414ContactsAccessor:</p>
415
416<pre>    private final ContactAccessor mContactAccessor = ContactAccessor.getInstance();</pre>
417
418<p>You are done! Now you will want to test on Android 2.0, 1.6 and 1.5.</p>
419
420<p>We hope you like the new features and APIs we've added to Contacts in
421Android 2.0, and we can't wait to see what cool things developers do with
422the new APIs.</p>
423