1page.title=Displaying the Quick Contact Badge
2
3trainingnavtop=true
4@jd:body
5
6
7<div id="tb-wrapper">
8<div id="tb">
9
10<!-- table of contents -->
11<h2>This lesson teaches you to</h2>
12<ol>
13    <li>
14        <a href="#AddView">Add a QuickContactBadge View</a>
15    </li>
16    <li>
17        <a href="#SetURIThumbnail">Set the Contact URI and Thumbnail</a>
18    </li>
19    <li>
20        <a href="#ListView">
21            Add a QuickContactBadge to a ListView
22        </a>
23    </li>
24</ol>
25
26<!-- other docs (NOT javadocs) -->
27<h2>You should also read</h2>
28<ul>
29    <li>
30        <a href="{@docRoot}guide/topics/providers/content-provider-basics.html">
31        Content Provider Basics
32        </a>
33    </li>
34    <li>
35        <a href="{@docRoot}guide/topics/providers/contacts-provider.html">
36        Contacts Provider
37        </a>
38    </li>
39</ul>
40
41<h2>Try it out</h2>
42
43<div class="download-box">
44    <a href="http://developer.android.com/shareables/training/ContactsList.zip" class="button">
45    Download the sample
46    </a>
47 <p class="filename">ContactsList.zip</p>
48</div>
49
50</div>
51</div>
52<p>
53    This lesson shows you how to add a {@link android.widget.QuickContactBadge} to your UI
54    and how to bind data to it. A {@link android.widget.QuickContactBadge} is a widget that
55    initially appears as a thumbnail image. Although you can use any {@link android.graphics.Bitmap}
56    for the thumbnail image, you usually use a {@link android.graphics.Bitmap} decoded from the
57    contact's photo thumbnail image.
58</p>
59<p>
60    The small image acts as a control; when users click on the image, the
61    {@link android.widget.QuickContactBadge} expands into a dialog containing the following:
62</p>
63<dl>
64    <dt>A large image</dt>
65    <dd>
66        The large image associated with the contact, or no image is available, a placeholder
67        graphic.
68    </dd>
69    <dt>
70        App icons
71    </dt>
72    <dd>
73        An app icon for each piece of detail data that can be handled by a built-in app. For
74        example, if the contact's details include one or more email addresses, an email icon
75        appears. When users click the icon, all of the contact's email addresses appear. When users
76        click one of the addresses, the email app displays a screen for composing a message to the
77        selected email address.
78    </dd>
79</dl>
80<p>
81    The {@link android.widget.QuickContactBadge} view provides instant access to a contact's
82    details, as well as a fast way of communicating with the contact. Users don't have to look up
83    a contact, find and copy information, and then paste it into the appropriate app. Instead, they
84    can click on the {@link android.widget.QuickContactBadge}, choose the communication method they
85    want to use, and send the information for that method directly to the appropriate app.
86</p>
87<h2 id="AddView">Add a QuickContactBadge View</h2>
88<p>
89    To add a {@link android.widget.QuickContactBadge}, insert a
90    <code>&lt;QuickContactBadge&gt;</code> element in your layout. For example:
91</p>
92<pre>
93&lt;RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
94                android:layout_width="match_parent"
95                android:layout_height="match_parent"&gt;
96...
97    &lt;QuickContactBadge
98               android:id=&#64;+id/quickbadge
99               android:layout_height="wrap_content"
100               android:layout_width="wrap_content"
101               android:scaleType="centerCrop"/&gt;
102    ...
103&lt;/RelativeLayout&gt;
104</pre>
105<h2 id="">Retrieve provider data</h2>
106<p>
107    To display a contact in the {@link android.widget.QuickContactBadge}, you need a content URI
108    for the contact and a {@link android.graphics.Bitmap} for the small image. You generate
109    both the content URI and the {@link android.graphics.Bitmap} from columns retrieved from the
110    Contacts Provider. Specify these columns as part of the projection you use to load data into
111    your {@link android.database.Cursor}.
112</p>
113<p>
114    For Android 3.0 (API level 11) and later, include the following columns in your projection:</p>
115<ul>
116    <li>{@link android.provider.ContactsContract.Contacts#_ID Contacts._ID}</li>
117    <li>{@link android.provider.ContactsContract.Contacts#LOOKUP_KEY Contacts.LOOKUP_KEY}</li>
118    <li>
119        {@link android.provider.ContactsContract.Contacts#PHOTO_THUMBNAIL_URI
120        Contacts.PHOTO_THUMBNAIL_URI}
121    </li>
122</ul>
123<p>
124    For Android 2.3.3 (API level 10) and earlier, use the following columns:
125</p>
126<ul>
127    <li>{@link android.provider.ContactsContract.Contacts#_ID Contacts._ID}</li>
128    <li>{@link android.provider.ContactsContract.Contacts#LOOKUP_KEY Contacts.LOOKUP_KEY}</li>
129</ul>
130<p>
131    The remainder of this lesson assumes that you've already loaded a
132    {@link android.database.Cursor} that contains these columns as well as others you may have
133    chosen. To learn how to retrieve this columns in a {@link android.database.Cursor}, read the
134    lesson <a href="retrieve-names.html">Retrieving a List of Contacts</a>.
135</p>
136<h2 id="SetURIThumbnail">Set the Contact URI and Thumbnail</h2>
137<p>
138    Once you have the necessary columns, you can bind data to the
139    {@link android.widget.QuickContactBadge}.
140</p>
141<h3>Set the Contact URI</h3>
142<p>
143    To set the content URI for the contact, call
144    {@link android.provider.ContactsContract.Contacts#getLookupUri getLookupUri(id,lookupKey)} to
145    get a {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}, then
146    call {@link android.widget.QuickContactBadge#assignContactUri assignContactUri()} to set the
147    contact. For example:
148</p>
149<pre>
150    // The Cursor that contains contact rows
151    Cursor mCursor;
152    // The index of the _ID column in the Cursor
153    int mIdColumn;
154    // The index of the LOOKUP_KEY column in the Cursor
155    int mLookupKeyColumn;
156    // A content URI for the desired contact
157    Uri mContactUri;
158    // A handle to the QuickContactBadge view
159    QuickContactBadge mBadge;
160    ...
161    mBadge = (QuickContactBadge) findViewById(R.id.quickbadge);
162    /*
163     * Insert code here to move to the desired cursor row
164     */
165    // Gets the _ID column index
166    mIdColumn = mCursor.getColumnIndex(Contacts._ID);
167    // Gets the LOOKUP_KEY index
168    mLookupKeyColumn = mCursor.getColumnIndex(Contacts.LOOKUP_KEY);
169    // Gets a content URI for the contact
170    mContactUri =
171            Contacts.getLookupUri(
172                mCursor.getLong(mIdColumn),
173                mCursor.getString(mLookupKeyColumn)
174            );
175    mBadge.assignContactUri(mContactUri);
176</pre>
177<p>
178    When users click the {@link android.widget.QuickContactBadge} icon, the contact's
179    details automatically appear in the dialog.
180</p>
181<h3>Set the photo thumbnail</h3>
182<p>
183    Setting the contact URI for the {@link android.widget.QuickContactBadge} does not automatically
184    load the contact's thumbnail photo. To load the photo, get a URI for the photo from the
185    contact's {@link android.database.Cursor} row, use it to open the file containing the compressed
186    thumbnail photo, and read the file into a {@link android.graphics.Bitmap}.
187</p>
188<p class="note">
189    <strong>Note:</strong> The
190    {@link android.provider.ContactsContract.Contacts#PHOTO_THUMBNAIL_URI} column isn't available
191    in platform versions prior to 3.0. For those versions, you must retrieve the URI
192    from the {@link android.provider.ContactsContract.Contacts.Photo Contacts.Photo} subtable.
193</p>
194<p>
195    First, set up variables for accessing the {@link android.database.Cursor} containing the
196    {@link android.provider.ContactsContract.Contacts#_ID Contacts._ID} and
197    {@link android.provider.ContactsContract.Contacts#LOOKUP_KEY Contacts.LOOKUP_KEY} columns, as
198    described previously:
199</p>
200<pre>
201    // The column in which to find the thumbnail ID
202    int mThumbnailColumn;
203    /*
204     * The thumbnail URI, expressed as a String.
205     * Contacts Provider stores URIs as String values.
206     */
207    String mThumbnailUri;
208    ...
209    /*
210     * Gets the photo thumbnail column index if
211     * platform version &gt;= Honeycomb
212     */
213    if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.HONEYCOMB) {
214        mThumbnailColumn =
215                mCursor.getColumnIndex(Contacts.PHOTO_THUMBNAIL_URI);
216    // Otherwise, sets the thumbnail column to the _ID column
217    } else {
218        mThumbnailColumn = mIdColumn;
219    }
220    /*
221     * Assuming the current Cursor position is the contact you want,
222     * gets the thumbnail ID
223     */
224    mThumbnailUri = mCursor.getString(mThumbnailColumn);
225    ...
226</pre>
227<p>
228    Define a method that takes photo-related data for the contact and dimensions for the
229    destination view,  and returns the properly-sized thumbnail in a
230    {@link android.graphics.Bitmap}. Start by constructing a URI that points to the
231    thumbnail:
232<p>
233<pre>
234    /**
235     * Load a contact photo thumbnail and return it as a Bitmap,
236     * resizing the image to the provided image dimensions as needed.
237     * @param photoData photo ID Prior to Honeycomb, the contact's _ID value.
238     * For Honeycomb and later, the value of PHOTO_THUMBNAIL_URI.
239     * @return A thumbnail Bitmap, sized to the provided width and height.
240     * Returns null if the thumbnail is not found.
241     */
242    private Bitmap loadContactPhotoThumbnail(String photoData) {
243        // Creates an asset file descriptor for the thumbnail file.
244        AssetFileDescriptor afd = null;
245        // try-catch block for file not found
246        try {
247            // Creates a holder for the URI.
248            Uri thumbUri;
249            // If Android 3.0 or later
250            if (Build.VERSION.SDK_INT
251                    &gt;=
252                Build.VERSION_CODES.HONEYCOMB) {
253                // Sets the URI from the incoming PHOTO_THUMBNAIL_URI
254                thumbUri = Uri.parse(photoData);
255            } else {
256            // Prior to Android 3.0, constructs a photo Uri using _ID
257                /*
258                 * Creates a contact URI from the Contacts content URI
259                 * incoming photoData (_ID)
260                 */
261                final Uri contactUri = Uri.withAppendedPath(
262                        Contacts.CONTENT_URI, photoData);
263                /*
264                 * Creates a photo URI by appending the content URI of
265                 * Contacts.Photo.
266                 */
267                thumbUri =
268                        Uri.withAppendedPath(
269                                contactUri, Photo.CONTENT_DIRECTORY);
270            }
271
272        /*
273         * Retrieves an AssetFileDescriptor object for the thumbnail
274         * URI
275         * using ContentResolver.openAssetFileDescriptor
276         */
277        afd = getActivity().getContentResolver().
278                openAssetFileDescriptor(thumbUri, "r");
279        /*
280         * Gets a file descriptor from the asset file descriptor.
281         * This object can be used across processes.
282         */
283        FileDescriptor fileDescriptor = afd.getFileDescriptor();
284        // Decode the photo file and return the result as a Bitmap
285        // If the file descriptor is valid
286        if (fileDescriptor != null) {
287            // Decodes the bitmap
288            return BitmapFactory.decodeFileDescriptor(
289                    fileDescriptor, null, null);
290            }
291        // If the file isn't found
292        } catch (FileNotFoundException e) {
293            /*
294             * Handle file not found errors
295             */
296        }
297        // In all cases, close the asset file descriptor
298        } finally {
299            if (afd != null) {
300                try {
301                    afd.close();
302                } catch (IOException e) {}
303            }
304        }
305        return null;
306    }
307</pre>
308<p>
309    Call the <code>loadContactPhotoThumbnail()</code> method in your code to get the
310    thumbnail {@link android.graphics.Bitmap}, and use the result to set the photo thumbnail in
311    your {@link android.widget.QuickContactBadge}:
312</p>
313<pre>
314    ...
315    /*
316     * Decodes the thumbnail file to a Bitmap.
317     */
318    Bitmap mThumbnail =
319            loadContactPhotoThumbnail(mThumbnailUri);
320    /*
321     * Sets the image in the QuickContactBadge
322     * QuickContactBadge inherits from ImageView, so
323     */
324    mBadge.setImageBitmap(mThumbnail);
325</pre>
326<h2 id="ListView">Add a QuickContactBadge to a ListView</h2>
327<p>
328    A {@link android.widget.QuickContactBadge} is a useful addition to a
329    {@link android.widget.ListView} that displays a list of contacts. Use the
330    {@link android.widget.QuickContactBadge} to display a thumbnail photo for each contact; when
331    users click the thumbnail, the {@link android.widget.QuickContactBadge} dialog appears.
332</p>
333<h3>Add the QuickContactBadge element</h3>
334<p>
335    To start, add a {@link android.widget.QuickContactBadge} view element to your item layout
336    For example, if you want to display a {@link android.widget.QuickContactBadge} and a name for
337    each contact you retrieve, put the following XML into a layout file:
338</p>
339<pre>
340&lt;RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
341                android:layout_width="match_parent"
342                android:layout_height="wrap_content"&gt;
343    &lt;QuickContactBadge
344        android:id="&#64;+id/quickcontact"
345        android:layout_height="wrap_content"
346        android:layout_width="wrap_content"
347        android:scaleType="centerCrop"/&gt;
348    &lt;TextView android:id="&#64;+id/displayname"
349              android:layout_width="match_parent"
350              android:layout_height="wrap_content"
351              android:layout_toRightOf="&#64;+id/quickcontact"
352              android:gravity="center_vertical"
353              android:layout_alignParentRight="true"
354              android:layout_alignParentTop="true"/&gt;
355&lt;/RelativeLayout&gt;
356</pre>
357<p>
358    In the following sections, this file is referred to as <code>contact_item_layout.xml</code>.
359</p>
360<h3>Set up a custom CursorAdapter</h3>
361<p>
362    To bind a {@link android.support.v4.widget.CursorAdapter} to a {@link android.widget.ListView}
363    containing a {@link android.widget.QuickContactBadge}, define a custom adapter that
364    extends {@link android.support.v4.widget.CursorAdapter}. This approach allows you to process the
365    data in the {@link android.database.Cursor} before you bind it to the
366    {@link android.widget.QuickContactBadge}. This approach also allows you to bind multiple
367    {@link android.database.Cursor} columns to the {@link android.widget.QuickContactBadge}. Neither
368    of these operations is possible in a regular {@link android.support.v4.widget.CursorAdapter}.
369</p>
370<p>
371    The subclass of {@link android.support.v4.widget.CursorAdapter} that you define must
372    override the following methods:
373</p>
374<dl>
375    <dt>{@link android.support.v4.widget.CursorAdapter#newView CursorAdapter.newView()}</dt>
376    <dd>
377        Inflates a new {@link android.view.View} object to hold the item layout. In the override
378        of this method, store handles to the child {@link android.view.View} objects of the layout,
379        including the child {@link android.widget.QuickContactBadge}. By taking this approach, you
380        avoid having to get handles to the child {@link android.view.View} objects each time you
381        inflate a new layout.
382        <p>
383            You must override this method so you can get handles to the individual child
384            {@link android.view.View} objects. This technique allows you to control their binding in
385            {@link android.support.v4.widget.CursorAdapter#bindView CursorAdapter.bindView()}.
386        </p>
387    </dd>
388    <dt>{@link android.support.v4.widget.CursorAdapter#bindView CursorAdapter.bindView()}</dt>
389    <dd>
390        Moves data from the current {@link android.database.Cursor} row to the child
391        {@link android.view.View} objects of the item layout. You must override this method so
392        you can bind both the contact's URI and thumbnail to the
393        {@link android.widget.QuickContactBadge}. The default implementation only allows a 1-to-1
394        mapping between a column and a {@link android.view.View}
395    </dd>
396</dl>
397<p>
398    The following code snippet contains an example of a custom subclass of
399    {@link android.support.v4.widget.CursorAdapter}:
400</p>
401<h3>Define the custom list adapter</h3>
402<p>
403    Define the subclass of {@link android.support.v4.widget.CursorAdapter} including its
404    constructor, and override
405    {@link android.support.v4.widget.CursorAdapter#newView newView()} and
406    {@link android.support.v4.widget.CursorAdapter#bindView bindView()}:
407</p>
408<pre>
409    /**
410     *
411     *
412     */
413    private class ContactsAdapter extends CursorAdapter {
414        private LayoutInflater mInflater;
415        ...
416        public ContactsAdapter(Context context) {
417            super(context, null, 0);
418
419            /*
420             * Gets an inflater that can instantiate
421             * the ListView layout from the file.
422             */
423            mInflater = LayoutInflater.from(context);
424            ...
425        }
426        ...
427        /**
428         * Defines a class that hold resource IDs of each item layout
429         * row to prevent having to look them up each time data is
430         * bound to a row.
431         */
432        private class ViewHolder {
433            TextView displayname;
434            QuickContactBadge quickcontact;
435        }
436        ..
437        &#64;Override
438        public View newView(
439                Context context,
440                Cursor cursor,
441                ViewGroup viewGroup) {
442            /* Inflates the item layout. Stores resource IDs in a
443             * in a ViewHolder class to prevent having to look
444             * them up each time bindView() is called.
445             */
446            final View itemView =
447                    mInflater.inflate(
448                            R.layout.contact_list_layout,
449                            viewGroup,
450                            false
451                    );
452            final ViewHolder holder = new ViewHolder();
453            holder.displayname =
454                    (TextView) view.findViewById(R.id.displayname);
455            holder.quickcontact =
456                    (QuickContactBadge)
457                            view.findViewById(R.id.quickcontact);
458            view.setTag(holder);
459            return view;
460        }
461        ...
462        &#64;Override
463        public void bindView(
464                View view,
465                Context context,
466                Cursor cursor) {
467            final ViewHolder holder = (ViewHolder) view.getTag();
468            final String photoData =
469                    cursor.getString(mPhotoDataIndex);
470            final String displayName =
471                    cursor.getString(mDisplayNameIndex);
472            ...
473            // Sets the display name in the layout
474            holder.displayname = cursor.getString(mDisplayNameIndex);
475            ...
476            /*
477             * Generates a contact URI for the QuickContactBadge.
478             */
479            final Uri contactUri = Contacts.getLookupUri(
480                    cursor.getLong(mIdIndex),
481                    cursor.getString(mLookupKeyIndex));
482            holder.quickcontact.assignContactUri(contactUri);
483            String photoData = cursor.getString(mPhotoDataIndex);
484            /*
485             * Decodes the thumbnail file to a Bitmap.
486             * The method loadContactPhotoThumbnail() is defined
487             * in the section "Set the Contact URI and Thumbnail"
488             */
489            Bitmap thumbnailBitmap =
490                    loadContactPhotoThumbnail(photoData);
491            /*
492             * Sets the image in the QuickContactBadge
493             * QuickContactBadge inherits from ImageView
494             */
495            holder.quickcontact.setImageBitmap(thumbnailBitmap);
496    }
497</pre>
498
499<h3>Set up variables</h3>
500<p>
501    In your code, set up variables, including a {@link android.database.Cursor} projection that
502    includes the necessary columns.
503</p>
504<p class="note">
505    <strong>Note:</strong> The following code snippets use the method
506    <code>loadContactPhotoThumbnail()</code>, which is defined in the section
507    <a href="#SetURIThumbnail">Set the Contact URI and Thumbnail</a>
508</p>
509<p>
510    For example:
511</p>
512<pre>
513public class ContactsFragment extends Fragment implements
514        LoaderManager.LoaderCallbacks&lt;Cursor&gt; {
515...
516    // Defines a ListView
517    private ListView mListView;
518    // Defines a ContactsAdapter
519    private ContactsAdapter mAdapter;
520    ...
521    // Defines a Cursor to contain the retrieved data
522    private Cursor mCursor;
523    /*
524     * Defines a projection based on platform version. This ensures
525     * that you retrieve the correct columns.
526     */
527    private static final String[] PROJECTION =
528            {
529                Contacts._ID,
530                Contacts.LOOKUP_KEY,
531                (Build.VERSION.SDK_INT &gt;=
532                 Build.VERSION_CODES.HONEYCOMB) ?
533                        Contacts.DISPLAY_NAME_PRIMARY :
534                        Contacts.DISPLAY_NAME
535                (Build.VERSION.SDK_INT &gt;=
536                 Build.VERSION_CODES.HONEYCOMB) ?
537                        Contacts.PHOTO_THUMBNAIL_ID :
538                        /*
539                         * Although it's not necessary to include the
540                         * column twice, this keeps the number of
541                         * columns the same regardless of version
542                         */
543                        Contacts_ID
544                ...
545            };
546    /*
547     * As a shortcut, defines constants for the
548     * column indexes in the Cursor. The index is
549     * 0-based and always matches the column order
550     * in the projection.
551     */
552    // Column index of the _ID column
553    private int mIdIndex = 0;
554    // Column index of the LOOKUP_KEY column
555    private int mLookupKeyIndex = 1;
556    // Column index of the display name column
557    private int mDisplayNameIndex = 3;
558    /*
559     * Column index of the photo data column.
560     * It's PHOTO_THUMBNAIL_URI for Honeycomb and later,
561     * and _ID for previous versions.
562     */
563    private int mPhotoDataIndex =
564            Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.HONEYCOMB ?
565            3 :
566            0;
567    ...
568</pre>
569<h3>Set up the ListView</h3>
570<p>
571    In {@link android.support.v4.app.Fragment#onCreate Fragment.onCreate()}, instantiate the custom
572    cursor adapter and get a handle to the {@link android.widget.ListView}:
573</p>
574<pre>
575    &#64;Override
576    public void onCreate(Bundle savedInstanceState) {
577        ...
578        /*
579         * Instantiates the subclass of
580         * CursorAdapter
581         */
582        ContactsAdapter mContactsAdapter =
583                new ContactsAdapter(getActivity());
584        /*
585         * Gets a handle to the ListView in the file
586         * contact_list_layout.xml
587         */
588        mListView = (ListView) findViewById(R.layout.contact_list_layout);
589        ...
590    }
591    ...
592</pre>
593<p>
594    In {@link android.support.v4.app.Fragment#onActivityCreated onActivityCreated()}, bind the
595    <code>ContactsAdapter</code> to the {@link android.widget.ListView}:
596</p>
597<pre>
598    &#64;Override
599    public void onActivityCreated(Bundle savedInstanceState) {
600        ...
601        // Sets up the adapter for the ListView
602        mListView.setAdapter(mAdapter);
603        ...
604    }
605    ...
606</pre>
607<p>
608    When you get back a {@link android.database.Cursor} containing the contacts data, usually in
609    {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()},
610    call {@link android.support.v4.widget.CursorAdapter#swapCursor swapCursor()} to move the
611    {@link android.database.Cursor} data to the {@link android.widget.ListView}. This displays the
612    {@link android.widget.QuickContactBadge} for each entry in the list of contacts:
613</p>
614<pre>
615    public void onLoadFinished(Loader&lt;Cursor&gt; loader, Cursor cursor) {
616        // When the loader has completed, swap the cursor into the adapter.
617        mContactsAdapter.swapCursor(cursor);
618    }
619</pre>
620<p>
621    When you bind a {@link android.database.Cursor} to a
622    {@link android.widget.ListView} with a {@link android.support.v4.widget.CursorAdapter}
623    (or subclass), and you use a {@link android.support.v4.content.CursorLoader} to load the
624    {@link android.database.Cursor}, always clear references to the {@link android.database.Cursor}
625    in your implementation of
626    {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoaderReset onLoaderReset()}.
627    For example:
628</p>
629<pre>
630    &#64;Override
631    public void onLoaderReset(Loader&lt;Cursor&gt; loader) {
632        // Removes remaining reference to the previous Cursor
633        mContactsAdapter.swapCursor(null);
634    }
635</pre>
636