1 page.title=Using the Contacts API 2 @jd:body 3 4 <p>Starting from Android 2.0 (API Level 5), the Android platform provides an 5 improved Contacts API for managing and integrating contacts from multiple 6 accounts and from other data sources. To handle overlapping data from multiple 7 sources, the contacts content provider aggregates similar contacts and presents 8 them to users as a single entity. This article describes how to use the new API 9 to manage contacts.</p> 10 11 <p>The new Contacts API is defined in the 12 {@link android.provider.ContactsContract android.provider.ContactsContract} 13 and related classes. The older API is still supported, although deprecated. 14 If you have an existing application that uses the older API, 15 see <a href="#legacy">Considerations for legacy apps</a>, below, for ideas 16 on 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 19 API, including how to support both the new and older API in a single app, 20 please see the <a 21 href="{@docRoot}resources/samples/BusinessCard/index.html">Business Card 22 sample 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 28 is slightly different from that used in the older API. The new structure 29 allows the system to more easily store and manage information for a 30 specific 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 36 associated with a raw contact. Each row stores data of a specific kind — 37 for example name, photo, email addresses, phone numbers, and group memberships. 38 Each row is tagged with a MIME type to identify what type of data it can 39 contain, across the entire column. Columns are generic and the type of data they 40 contain is determined by the kind of data stored in each row. For example, if a 41 row's data kind is <code>Phone.CONTENT_ITEM_TYPE</code>, then the first column 42 stores 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} 46 class provides subclasses corresponding to common MIME types for contacts data. 47 If needed, your application or other contacts sources can define additional MIME 48 types for data rows. For more information about the Data table and examples of 49 how 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 53 a single contacts source. For example, a row might define the data associated 54 with a person's Google or Exchange account or Facebook friend. For more 55 information, 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 59 more <code>RawContacts</code> describing the same person (or entity). 60 61 <p>As mentioned above, the Contacts content provider automatically aggregates 62 Raw Contacts into a single Contact entry, where possible, since common data 63 fields (such as name or email address) are likely to be stored in each raw 64 contact. Since the aggregation logic maintains the entries in the Contact rows, 65 the entries can be read but should not be modified. See the section <a 66 href="#aggregation">Aggregation of contacts</a>, below, for more details, 67 including and information on how to 68 control aggregation.</li> 69 70 </ul> 71 72 <p>When displaying contacts to users, applications should typically operate on 73 the Contacts level, since it provides a unified, aggregated view of contacts 74 from 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 79 Contact to attach the phone number to, then you'll need to create a Data 80 row:</p> 81 82 <pre>import android.provider.ContactsContract.CommonDataKinds.Phone; 83 ... 84 ContentValues values = new ContentValues(); 85 values.put(Phone.RAW_CONTACT_ID, rawContactId); 86 values.put(Phone.NUMBER, phoneNumber); 87 values.put(Phone.TYPE, Phone.TYPE_MOBILE); 88 Uri 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 94 to 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 96 friend, so the user might have his contact information stored in both a 97 corporate email account and a personal account. To provide a simplified view for 98 the user, the system locates such overlapping contacts and combines them into a 99 single, aggregate contact. </p> 100 101 <p>The system automatically aggregates contacts by default. However, if needed, 102 your application can control how the system handles aggregation or it can 103 disable 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 109 matching raw contacts, in which case it will create an aggregate contact that 110 contains just the original raw contact. If it finds a single match,it creates a 111 new contact that contains the two raw contacts. And it may even find multiple 112 similar 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 115 conditions 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 124 raw contact. This rule is less reliable, so it only applies if the two 125 raw contacts are also sharing some other data like a phone number, an 126 email address or a nickname (for example, Helen ["elastigirl"] = Helen 127 Parr ["elastigirl"])</li> 128 <li>At least one of the two raw contacts is missing the name altogether 129 and they are sharing a phone number, an email address or a nickname (for 130 example, Bob Parr [incredible (a] android.com] = incredible (a] 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 (Hlne=Helene). When comparing two 135 phone numbers the system ignores special characters such as "*", "#", 136 "(", ")", and whitespace. Also if the only difference between two numbers 137 is that one has a country code and the other does not, then the system 138 considers 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 141 contact 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 146 requirements of your application or sync adapter. There are two sets of APIs you 147 can use to control aggregation explicitly: <em>aggregation modes</em> allow you 148 to control automatic aggregation behaviors and <em>aggregation exceptions</em> 149 allow 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, 154 add a mode constant as the value of the <code>AGGREGATION_MODE column</code> in 155 the <code>RawContact</code> row. The mode constants available include: </p> 156 157 <ul> 158 <li><code>AGGREGATION_MODE_DEFAULT</code> — normal mode, automatic 159 aggregation is allowed.</li> 160 <li><code>AGGREGATION_MODE_DISABLED</code> — automatic aggregation is not 161 allowed. The raw contact will not be aggregated.</li> 162 <li><code>AGGREGATION_MODE_SUSPENDED</code> — automatic aggregation is 163 deactivated. If the raw contact is already a part of an aggregated contact when 164 aggregation mode changes to suspended, it will remain in the aggregate, even if 165 it changes in such a way that it no longer matches the other raw contacts in the 166 aggregate.</li> 167 </ul> 168 169 <p><strong>Aggregation exceptions</strong></p> 170 171 <p>To keep two raw contacts unconditionally together or unconditionally apart, 172 you can add a row to the 173 {@link android.provider.ContactsContract.AggregationExceptions} table. Exceptions 174 defined 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 180 your application needs to maintain references to contacts, you should use lookup 181 keys instead of the traditional row ids. You can acquire a lookup key from the 182 contact itself, it is a column on the 183 {@link android.provider.ContactsContract.Contacts} table. Once you have a lookup key, 184 you 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}, ...); 191 try { 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 199 inherently volatile. Let's say your app stored a long ID of a contact. Then the 200 user goes and manually joins the contact with some other contact. Now there is a 201 single contact where there used to be two, and the stored long contact ID points 202 nowhere. 203 204 <p>The lookup key helps resolve the contact in this case. The key is a string 205 that concatenates the server-side identities of the raw contacts. Your 206 application can use that string to find a contact, regardless whether the raw 207 contact is aggregated with others or not. </p> 208 209 <p>If performance is a concern for your application, you might want to store 210 both the lookup and the long ID of a contact and construct a lookup URI out of 211 both 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 216 first. That is a very quick query. If the contact is not found, or if the one 217 that is found has the wrong lookup key, the content provider will parse the 218 lookup key and track down the constituent raw contacts. If your app 219 bulk-processes contacts, you should maintain both IDs. If your app works with a 220 single contact per user action, you probably don't need to bother with storing 221 the long ID.</p> 222 223 Android itself uses lookup URIs whenever there is a need to reference a contact, 224 such as with shortcuts or Quick Contact, and also during editing or even viewing 225 a contact. The latter case is less obvious — why would a contact ID change 226 while we are simply viewing the contact? It could change because there might be 227 a sync going in the background, and the contact might get automatically 228 aggregated with another while being viewed.</p> 229 230 <p>In summary: whenever you need to reference a contact, we recommend that you 231 do 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, 237 you 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 251 application as is, and it should run on Android 2.0 as long as it only uses 252 public APIs. A couple examples of the use of non-public API include the use of 253 explicit table names in nested queries and the use of columns that were not 254 declared 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 258 for long. The main reason is that it will only have access to contacts from one 259 account, namely the first Google account on the device. If the user opens other 260 accounts in addition to or instead of a Google account, your application will 261 not 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 267 Android 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 271 API. After you are done, you should not see any deprecation warnings during 272 application build. The new application will be able to take full advantage of 273 multiple 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><uses-sdk></code> element. To use the new Contacts API, you should 278 set the value of the attribute to "5" (or higher, as appropriate). For more 279 information about <code>android:minSdkVersion</code>, see the documentation for 280 the <a 281 href="{@docRoot}guide/topics/manifest/uses-sdk-element.html"><code><uses-sdk></code></a> 282 element. For more information about the value of the 283 <code>minSdkVersion</code>, see <a 284 href="{@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 290 platforms 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 297 is 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 299 suggesting to the user that it's time to go to Market and find a new version of 300 the app. You can even provide a link to the new app on Market (see <a 301 href="{@docRoot}guide/publishing/publishing.html#marketintent">Using Intents 302 to 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. 307 The new application will be able to take full advantage of multiple accounts 308 and 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 312 Android Market does not allow you to have two applications with the same 313 name/package.</li> 314 <li>Update (or add) the <code>android:minSdkVersion</code> attribute 315 to the <code><uses-sdk></code> element. To use the new Contacts API, 316 you 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 320 other as new. Make sure to explain the difference between the apps in their 321 descriptions.</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. 328 Application data can only be accessed by code living in the same package. So 329 databases, shared preferences, and so on, will need to be populated from 330 scratch.</li> 331 <li>The upgrade process is too clunky for the user. Some users may choose 332 to 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 338 build 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. 342 For 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 364 from ContactAccessorOldApi. </p> 365 366 <p>Create a new abstract class ContactAccessor, make sure the abstract 367 class has all method signatures from ContactAccessorOldApi. Make 368 ContactAccessorOldApi 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 376 implement all methods using the new API:</p> 377 378 <pre> public class ContactAccessorNewApi extends ContactAccessor { 379 @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 387 old API and another using the new API. Let's plug them in. Add this code to 388 the 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 < Build.VERSION_CODES.ECLAIR) { 397 className = "ContactAccessorOldApi"; 398 } else { 399 className = "ContactAccessorNewApi"; 400 } 401 try { 402 Class<? extends ContactAccessor> 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 414 ContactsAccessor:</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 421 Android 2.0, and we can't wait to see what cool things developers do with 422 the new APIs.</p> 423