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