Home | History | Annotate | Download | only in contacts-provider
      1 page.title=Retrieving Details for a Contact
      2 
      3 trainingnavtop=true
      4 @jd:body
      5 
      6 <div id="tb-wrapper">
      7 <div id="tb">
      8 
      9 <!-- table of contents -->
     10 <h2>This lesson teaches you to</h2>
     11 <ol>
     12   <li><a href="#RetrieveAll">Retrieve All Details for a Contact</a></li>
     13   <li><a href="#RetrieveSpecific">Retrieve Specific Details for a Contact</a></li>
     14 </ol>
     15 
     16 <!-- other docs (NOT javadocs) -->
     17 <h2>You should also read</h2>
     18 <ul>
     19     <li>
     20         <a href="{@docRoot}guide/topics/providers/content-provider-basics.html">
     21         Content Provider Basics</a>
     22     </li>
     23     <li>
     24         <a href="{@docRoot}guide/topics/providers/contacts-provider.html">
     25         Contacts Provider</a>
     26     </li>
     27     <li>
     28         <a href="{@docRoot}guide/components/loaders.html">Loaders</a>
     29 </ul>
     30 
     31 <h2>Try it out</h2>
     32 
     33 <div class="download-box">
     34     <a href="http://developer.android.com/shareables/training/ContactsList.zip" class="button">
     35     Download the sample
     36     </a>
     37  <p class="filename">ContactsList.zip</p>
     38 </div>
     39 
     40 </div>
     41 </div>
     42 <p>
     43     This lesson shows how to retrieve detail data for a contact, such as email addresses, phone
     44     numbers, and so forth. It's the details that users are looking for when they retrieve a contact.
     45     You can give them all the details for a contact, or only display details of a particular type,
     46     such as email addresses.
     47 </p>
     48 <p>
     49     The steps in this lesson assume that you already have a
     50     {@link android.provider.ContactsContract.Contacts} row for a contact the user has picked.
     51     The <a href="retrieve-names.html">Retrieving Contact Names</a> lesson shows how to
     52     retrieve a list of contacts.
     53 </p>
     54 <h2 id="RetrieveAll">Retrieve All Details for a Contact</h2>
     55 <p>
     56     To retrieve all the details for a contact, search the
     57     {@link android.provider.ContactsContract.Data} table for any rows that contain the contact's
     58     {@link android.provider.ContactsContract.Data#LOOKUP_KEY}. This column is available in
     59     the {@link android.provider.ContactsContract.Data} table, because the Contacts
     60     Provider makes an implicit join between the {@link android.provider.ContactsContract.Contacts}
     61     table and the {@link android.provider.ContactsContract.Data} table. The
     62     {@link android.provider.ContactsContract.Contacts#LOOKUP_KEY} column is described
     63     in more detail in the <a href="retrieve-names.html">Retrieving Contact Names</a> lesson.
     64 </p>
     65 <p class="note">
     66     <strong>Note:</strong> Retrieving all the details for a contact reduces the performance of a
     67     device, because it needs to retrieve all of the columns in the
     68     {@link android.provider.ContactsContract.Data} table. Consider the performance impact before
     69     you use this technique.
     70 </p>
     71 <h3>Request permissions</h3>
     72 <p>
     73     To read from the Contacts Provider, your app must have
     74     {@link android.Manifest.permission#READ_CONTACTS READ_CONTACTS} permission.
     75     To request this permission, add the following child element of
     76     <code><a href="{@docRoot}guide/topics/manifest/manifest-element.html">
     77     &lt;manifest&gt;</a></code> to your manifest file:
     78 </p>
     79 <pre>
     80     &lt;uses-permission android:name="android.permission.READ_CONTACTS" /&gt;
     81 </pre>
     82 <h3>Set up a projection</h3>
     83 <p>
     84     Depending on the data type a row contains, it may use only a few columns or many. In addition,
     85     the data is in different columns depending on the data type.
     86     To ensure you get all the possible columns for all possible data types, you need to add all the
     87     column names to your projection. Always retrieve
     88     {@link android.provider.ContactsContract.Data#_ID Data._ID} if you're binding the result
     89     {@link android.database.Cursor} to a {@link android.widget.ListView}; otherwise, the binding
     90     won't work. Also retrieve {@link android.provider.ContactsContract.Data#MIMETYPE Data.MIMETYPE}
     91     so you can identify the data type of each row you retrieve. For example:
     92 </p>
     93 <pre>
     94     private static final String PROJECTION =
     95             {
     96                 Data._ID,
     97                 Data.MIMETYPE,
     98                 Data.DATA1,
     99                 Data.DATA2,
    100                 Data.DATA3,
    101                 Data.DATA4,
    102                 Data.DATA5,
    103                 Data.DATA6,
    104                 Data.DATA7,
    105                 Data.DATA8,
    106                 Data.DATA9,
    107                 Data.DATA10,
    108                 Data.DATA11,
    109                 Data.DATA12,
    110                 Data.DATA13,
    111                 Data.DATA14,
    112                 Data.DATA15
    113             };
    114 </pre>
    115 <p>
    116     This projection retrieves all the columns for a row in the
    117     {@link android.provider.ContactsContract.Data} table, using the column names defined in
    118     the {@link android.provider.ContactsContract.Data} class.
    119 </p>
    120 <p>
    121     Optionally, you can also use any other column constants defined in or inherited by the
    122     {@link android.provider.ContactsContract.Data} class. Notice, however, that the columns
    123     {@link android.provider.ContactsContract.DataColumns#SYNC1} through
    124     {@link android.provider.ContactsContract.DataColumns#SYNC4} are meant to be used by sync
    125     adapters, so their data is not useful.
    126 </p>
    127 <h3>Define the selection criteria</h3>
    128 <p>
    129     Define a constant for your selection clause, an array to hold selection arguments, and a
    130     variable to hold the selection value. Use
    131     the {@link android.provider.ContactsContract.Contacts#LOOKUP_KEY Contacts.LOOKUP_KEY} column to
    132     find the contact. For example:
    133 </p>
    134 <pre>
    135     // Defines the selection clause
    136     private static final String SELECTION = Data.LOOKUP_KEY + " = ?";
    137     // Defines the array to hold the search criteria
    138     private String[] mSelectionArgs = { "" };
    139     /*
    140      * Defines a variable to contain the selection value. Once you
    141      * have the Cursor from the Contacts table, and you've selected
    142      * the desired row, move the row's LOOKUP_KEY value into this
    143      * variable.
    144      */
    145     private String mLookupKey;
    146 </pre>
    147 <p>
    148     Using "?" as a placeholder in your selection text expression ensures that the resulting search
    149     is generated by binding rather than SQL compilation. This approach eliminates the
    150     possibility of malicious SQL injection.
    151 </p>
    152 <h3>Define the sort order</h3>
    153 <p>
    154     Define the sort order you want in the resulting {@link android.database.Cursor}. To
    155     keep all rows for a particular data type together, sort by
    156     {@link android.provider.ContactsContract.Data#MIMETYPE Data.MIMETYPE}. This query argument
    157     groups all email rows together, all phone rows together, and so forth. For example:
    158 </p>
    159 <pre>
    160     /*
    161      * Defines a string that specifies a sort order of MIME type
    162      */
    163     private static final String SORT_ORDER = Data.MIMETYPE;
    164 </pre>
    165 <p class="note">
    166     <strong>Note:</strong> Some data types don't use a subtype, so you can't sort on subtype.
    167     Instead, you have to iterate through the returned {@link android.database.Cursor},
    168     determine the data type of the current row, and store data for rows that use a subtype. When
    169     you finish reading the cursor, you can then sort each data type by subtype and display the
    170     results.
    171 </p>
    172 <h3>Initialize the Loader</h3>
    173 <p>
    174    Always do retrievals from the Contacts Provider (and all other content providers) in a
    175    background thread. Use the Loader framework defined by the
    176    {@link android.support.v4.app.LoaderManager} class and the
    177    {@link android.support.v4.app.LoaderManager.LoaderCallbacks} interface to do background
    178    retrievals.
    179 </p>
    180 <p>
    181     When you're ready to retrieve the rows, initialize the loader framework by
    182     calling {@link android.support.v4.app.LoaderManager#initLoader initLoader()}. Pass an
    183     integer identifier to the method; this identifier is passed to
    184     {@link android.support.v4.app.LoaderManager.LoaderCallbacks} methods. The identifier helps you
    185     use multiple loaders in an app by allowing you to differentiate between them.
    186 </p>
    187 <p>
    188     The following snippet shows how to initialize the loader framework:
    189 </p>
    190 <pre>
    191 public class DetailsFragment extends Fragment implements
    192         LoaderManager.LoaderCallbacks&lt;Cursor&gt; {
    193     ...
    194     // Defines a constant that identifies the loader
    195     DETAILS_QUERY_ID = 0;
    196     ...
    197     /*
    198      * Invoked when the parent Activity is instantiated
    199      * and the Fragment's UI is ready. Put final initialization
    200      * steps here.
    201      */
    202     &#64;Override
    203     onActivityCreated(Bundle savedInstanceState) {
    204         ...
    205         // Initializes the loader framework
    206         getLoaderManager().initLoader(DETAILS_QUERY_ID, null, this);
    207 </pre>
    208 <h3>Implement onCreateLoader()</h3>
    209 <p>
    210     Implement the {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader
    211     onCreateLoader()} method, which is called by the loader framework immediately after you call
    212     {@link android.support.v4.app.LoaderManager#initLoader initLoader()}. Return a
    213     {@link android.support.v4.content.CursorLoader} from this method. Since you're searching
    214     the {@link android.provider.ContactsContract.Data} table, use the constant
    215     {@link android.provider.ContactsContract.Data#CONTENT_URI Data.CONTENT_URI} as the content URI.
    216     For example:
    217 </p>
    218 <pre>
    219     &#64;Override
    220     public Loader&lt;Cursor&gt; onCreateLoader(int loaderId, Bundle args) {
    221         // Choose the proper action
    222         switch (loaderId) {
    223             case DETAILS_QUERY_ID:
    224             // Assigns the selection parameter
    225             mSelectionArgs[0] = mLookupKey;
    226             // Starts the query
    227             CursorLoader mLoader =
    228                     new CursorLoader(
    229                             getActivity(),
    230                             Data.CONTENT_URI,
    231                             PROJECTION,
    232                             SELECTION,
    233                             mSelectionArgs,
    234                             SORT_ORDER
    235                     );
    236             ...
    237     }
    238 </pre>
    239 <h3>Implement onLoadFinished() and onLoaderReset()</h3>
    240 <p>
    241     Implement the
    242     {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()}
    243     method. The loader framework calls
    244     {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()}
    245     when the Contacts Provider returns the results of the query. For example:
    246 </p>
    247 <pre>
    248     public void onLoadFinished(Loader&lt;Cursor&gt; loader, Cursor cursor) {
    249         switch (loader.getId()) {
    250             case DETAILS_QUERY_ID:
    251                     /*
    252                      * Process the resulting Cursor here.
    253                      */
    254                 }
    255                 break;
    256             ...
    257         }
    258     }
    259 </pre>
    260 <p>
    261 <p>
    262     The method {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoaderReset
    263     onLoaderReset()} is invoked when the loader framework detects that the data backing the result
    264     {@link android.database.Cursor} has changed. At this point, remove any existing references
    265     to the {@link android.database.Cursor} by setting them to null. If you don't, the loader
    266     framework won't destroy the old {@link android.database.Cursor}, and you'll get a memory
    267     leak. For example:
    268 <pre>
    269     &#64;Override
    270     public void onLoaderReset(Loader&lt;Cursor&gt; loader) {
    271         switch (loader.getId()) {
    272             case DETAILS_QUERY_ID:
    273                 /*
    274                  * If you have current references to the Cursor,
    275                  * remove them here.
    276                  */
    277                 }
    278                 break;
    279     }
    280 </pre>
    281 <h2 id="RetrieveSpecific">Retrieve Specific Details for a Contact</h2>
    282 <p>
    283     Retrieving a specific data type for a contact, such as all the emails, follows the same pattern
    284     as retrieving all details. These are the only changes you need to make to the code
    285     listed in <a href="#RetrieveAll">Retrieve All Details for a Contact</a>:
    286 </p>
    287 <dl>
    288     <dt>
    289         Projection
    290     </dt>
    291     <dd>
    292         Modify your projection to retrieve the columns that are specific to the
    293         data type. Also modify the projection to use the column name constants defined in the
    294         {@link android.provider.ContactsContract.CommonDataKinds} subclass corresponding to the
    295         data type.
    296     </dd>
    297     <dt>
    298         Selection
    299     </dt>
    300     <dd>
    301         Modify the selection text to search for the
    302         {@link android.provider.ContactsContract.Data#MIMETYPE MIMETYPE} value that's specific to
    303         your data type.
    304     </dd>
    305     <dt>
    306         Sort order
    307     </dt>
    308     <dd>
    309         Since you're only selecting a single detail type, don't group the returned
    310         {@link android.database.Cursor} by {@link android.provider.ContactsContract.Data#MIMETYPE
    311         Data.MIMETYPE}.
    312     </dd>
    313 </dl>
    314 <p>
    315     These modifications are described in the following sections.
    316 </p>
    317 <h3>Define a projection</h3>
    318 <p>
    319     Define the columns you want to retrieve, using the column name constants in the subclass
    320     of {@link android.provider.ContactsContract.CommonDataKinds} for the data type.
    321     If you plan to bind your {@link android.database.Cursor} to a {@link android.widget.ListView},
    322     be sure to retrieve the <code>_ID</code> column. For example, to retrieve email data, define the
    323     following projection:
    324 </p>
    325 <pre>
    326     private static final String[] PROJECTION =
    327             {
    328                 Email._ID,
    329                 Email.ADDRESS,
    330                 Email.TYPE,
    331                 Email.LABEL
    332             };
    333 </pre>
    334 <p>
    335     Notice that this projection uses the column names defined in the class
    336     {@link android.provider.ContactsContract.CommonDataKinds.Email}, instead of the column names
    337     defined in the class {@link android.provider.ContactsContract.Data}. Using the email-specific
    338     column names makes the code more readable.
    339 </p>
    340 <p>
    341     In the projection, you can also use any of the other columns defined in the
    342     {@link android.provider.ContactsContract.CommonDataKinds} subclass.
    343 </p>
    344 <h3>Define selection criteria</h3>
    345 <p>
    346     Define a search text expression that retrieves rows for a specific contact's
    347     {@link android.provider.ContactsContract.Data#LOOKUP_KEY} and the
    348     {@link android.provider.ContactsContract.Data#MIMETYPE Data.MIMETYPE} of the details you
    349     want. Enclose the {@link android.provider.ContactsContract.Data#MIMETYPE MIMETYPE} value in
    350     single quotes by concatenating a "<code>'</code>" (single-quote) character to the start and end
    351     of the constant; otherwise, the provider interprets the constant as a variable name rather
    352     than as a string value. You don't need to use a placeholder for this value, because you're
    353     using a constant rather than a user-supplied value. For example:
    354 </p>
    355 <pre>
    356     /*
    357      * Defines the selection clause. Search for a lookup key
    358      * and the Email MIME type
    359      */
    360     private static final String SELECTION =
    361             Data.LOOKUP_KEY + " = ?" +
    362             " AND " +
    363             Data.MIMETYPE + " = " +
    364             "'" + Email.CONTENT_ITEM_TYPE + "'";
    365     // Defines the array to hold the search criteria
    366     private String[] mSelectionArgs = { "" };
    367 </pre>
    368 <h3>Define a sort order</h3>
    369 <p>
    370     Define a sort order for the returned {@link android.database.Cursor}. Since you're retrieving a
    371     specific data type, omit the sort on {@link android.provider.ContactsContract.Data#MIMETYPE}.
    372     Instead, if the type of detail data you're searching includes a subtype, sort on it.
    373     For example, for email data you can sort on
    374     {@link android.provider.ContactsContract.CommonDataKinds.Email#TYPE Email.TYPE}:
    375 </p>
    376 <pre>
    377     private static final String SORT_ORDER = Email.TYPE + " ASC ";
    378 </pre>
    379