1page.title=Retrieving Details for a Contact
2
3trainingnavtop=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>
191public 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