379 lines
15 KiB
Plaintext
379 lines
15 KiB
Plaintext
page.title=Retrieving Details for a Contact
|
|
|
|
trainingnavtop=true
|
|
@jd:body
|
|
|
|
<div id="tb-wrapper">
|
|
<div id="tb">
|
|
|
|
<!-- table of contents -->
|
|
<h2>This lesson teaches you to</h2>
|
|
<ol>
|
|
<li><a href="#RetrieveAll">Retrieve All Details for a Contact</a></li>
|
|
<li><a href="#RetrieveSpecific">Retrieve Specific Details for a Contact</a></li>
|
|
</ol>
|
|
|
|
<!-- other docs (NOT javadocs) -->
|
|
<h2>You should also read</h2>
|
|
<ul>
|
|
<li>
|
|
<a href="{@docRoot}guide/topics/providers/content-provider-basics.html">
|
|
Content Provider Basics</a>
|
|
</li>
|
|
<li>
|
|
<a href="{@docRoot}guide/topics/providers/contacts-provider.html">
|
|
Contacts Provider</a>
|
|
</li>
|
|
<li>
|
|
<a href="{@docRoot}guide/components/loaders.html">Loaders</a>
|
|
</ul>
|
|
|
|
<h2>Try it out</h2>
|
|
|
|
<div class="download-box">
|
|
<a href="http://developer.android.com/shareables/training/ContactsList.zip" class="button">
|
|
Download the sample
|
|
</a>
|
|
<p class="filename">ContactsList.zip</p>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
<p>
|
|
This lesson shows how to retrieve detail data for a contact, such as email addresses, phone
|
|
numbers, and so forth. It's the details that users are looking for when they retrieve a contact.
|
|
You can give them all the details for a contact, or only display details of a particular type,
|
|
such as email addresses.
|
|
</p>
|
|
<p>
|
|
The steps in this lesson assume that you already have a
|
|
{@link android.provider.ContactsContract.Contacts} row for a contact the user has picked.
|
|
The <a href="retrieve-names.html">Retrieving Contact Names</a> lesson shows how to
|
|
retrieve a list of contacts.
|
|
</p>
|
|
<h2 id="RetrieveAll">Retrieve All Details for a Contact</h2>
|
|
<p>
|
|
To retrieve all the details for a contact, search the
|
|
{@link android.provider.ContactsContract.Data} table for any rows that contain the contact's
|
|
{@link android.provider.ContactsContract.Data#LOOKUP_KEY}. This column is available in
|
|
the {@link android.provider.ContactsContract.Data} table, because the Contacts
|
|
Provider makes an implicit join between the {@link android.provider.ContactsContract.Contacts}
|
|
table and the {@link android.provider.ContactsContract.Data} table. The
|
|
{@link android.provider.ContactsContract.Contacts#LOOKUP_KEY} column is described
|
|
in more detail in the <a href="retrieve-names.html">Retrieving Contact Names</a> lesson.
|
|
</p>
|
|
<p class="note">
|
|
<strong>Note:</strong> Retrieving all the details for a contact reduces the performance of a
|
|
device, because it needs to retrieve all of the columns in the
|
|
{@link android.provider.ContactsContract.Data} table. Consider the performance impact before
|
|
you use this technique.
|
|
</p>
|
|
<h3>Request permissions</h3>
|
|
<p>
|
|
To read from the Contacts Provider, your app must have
|
|
{@link android.Manifest.permission#READ_CONTACTS READ_CONTACTS} permission.
|
|
To request this permission, add the following child element of
|
|
<code><a href="{@docRoot}guide/topics/manifest/manifest-element.html">
|
|
<manifest></a></code> to your manifest file:
|
|
</p>
|
|
<pre>
|
|
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
|
</pre>
|
|
<h3>Set up a projection</h3>
|
|
<p>
|
|
Depending on the data type a row contains, it may use only a few columns or many. In addition,
|
|
the data is in different columns depending on the data type.
|
|
To ensure you get all the possible columns for all possible data types, you need to add all the
|
|
column names to your projection. Always retrieve
|
|
{@link android.provider.ContactsContract.Data#_ID Data._ID} if you're binding the result
|
|
{@link android.database.Cursor} to a {@link android.widget.ListView}; otherwise, the binding
|
|
won't work. Also retrieve {@link android.provider.ContactsContract.Data#MIMETYPE Data.MIMETYPE}
|
|
so you can identify the data type of each row you retrieve. For example:
|
|
</p>
|
|
<pre>
|
|
private static final String PROJECTION =
|
|
{
|
|
Data._ID,
|
|
Data.MIMETYPE,
|
|
Data.DATA1,
|
|
Data.DATA2,
|
|
Data.DATA3,
|
|
Data.DATA4,
|
|
Data.DATA5,
|
|
Data.DATA6,
|
|
Data.DATA7,
|
|
Data.DATA8,
|
|
Data.DATA9,
|
|
Data.DATA10,
|
|
Data.DATA11,
|
|
Data.DATA12,
|
|
Data.DATA13,
|
|
Data.DATA14,
|
|
Data.DATA15
|
|
};
|
|
</pre>
|
|
<p>
|
|
This projection retrieves all the columns for a row in the
|
|
{@link android.provider.ContactsContract.Data} table, using the column names defined in
|
|
the {@link android.provider.ContactsContract.Data} class.
|
|
</p>
|
|
<p>
|
|
Optionally, you can also use any other column constants defined in or inherited by the
|
|
{@link android.provider.ContactsContract.Data} class. Notice, however, that the columns
|
|
{@link android.provider.ContactsContract.DataColumns#SYNC1} through
|
|
{@link android.provider.ContactsContract.DataColumns#SYNC4} are meant to be used by sync
|
|
adapters, so their data is not useful.
|
|
</p>
|
|
<h3>Define the selection criteria</h3>
|
|
<p>
|
|
Define a constant for your selection clause, an array to hold selection arguments, and a
|
|
variable to hold the selection value. Use
|
|
the {@link android.provider.ContactsContract.Contacts#LOOKUP_KEY Contacts.LOOKUP_KEY} column to
|
|
find the contact. For example:
|
|
</p>
|
|
<pre>
|
|
// Defines the selection clause
|
|
private static final String SELECTION = Data.LOOKUP_KEY + " = ?";
|
|
// Defines the array to hold the search criteria
|
|
private String[] mSelectionArgs = { "" };
|
|
/*
|
|
* Defines a variable to contain the selection value. Once you
|
|
* have the Cursor from the Contacts table, and you've selected
|
|
* the desired row, move the row's LOOKUP_KEY value into this
|
|
* variable.
|
|
*/
|
|
private String mLookupKey;
|
|
</pre>
|
|
<p>
|
|
Using "?" as a placeholder in your selection text expression ensures that the resulting search
|
|
is generated by binding rather than SQL compilation. This approach eliminates the
|
|
possibility of malicious SQL injection.
|
|
</p>
|
|
<h3>Define the sort order</h3>
|
|
<p>
|
|
Define the sort order you want in the resulting {@link android.database.Cursor}. To
|
|
keep all rows for a particular data type together, sort by
|
|
{@link android.provider.ContactsContract.Data#MIMETYPE Data.MIMETYPE}. This query argument
|
|
groups all email rows together, all phone rows together, and so forth. For example:
|
|
</p>
|
|
<pre>
|
|
/*
|
|
* Defines a string that specifies a sort order of MIME type
|
|
*/
|
|
private static final String SORT_ORDER = Data.MIMETYPE;
|
|
</pre>
|
|
<p class="note">
|
|
<strong>Note:</strong> Some data types don't use a subtype, so you can't sort on subtype.
|
|
Instead, you have to iterate through the returned {@link android.database.Cursor},
|
|
determine the data type of the current row, and store data for rows that use a subtype. When
|
|
you finish reading the cursor, you can then sort each data type by subtype and display the
|
|
results.
|
|
</p>
|
|
<h3>Initialize the Loader</h3>
|
|
<p>
|
|
Always do retrievals from the Contacts Provider (and all other content providers) in a
|
|
background thread. Use the Loader framework defined by the
|
|
{@link android.support.v4.app.LoaderManager} class and the
|
|
{@link android.support.v4.app.LoaderManager.LoaderCallbacks} interface to do background
|
|
retrievals.
|
|
</p>
|
|
<p>
|
|
When you're ready to retrieve the rows, initialize the loader framework by
|
|
calling {@link android.support.v4.app.LoaderManager#initLoader initLoader()}. Pass an
|
|
integer identifier to the method; this identifier is passed to
|
|
{@link android.support.v4.app.LoaderManager.LoaderCallbacks} methods. The identifier helps you
|
|
use multiple loaders in an app by allowing you to differentiate between them.
|
|
</p>
|
|
<p>
|
|
The following snippet shows how to initialize the loader framework:
|
|
</p>
|
|
<pre>
|
|
public class DetailsFragment extends Fragment implements
|
|
LoaderManager.LoaderCallbacks<Cursor> {
|
|
...
|
|
// Defines a constant that identifies the loader
|
|
DETAILS_QUERY_ID = 0;
|
|
...
|
|
/*
|
|
* Invoked when the parent Activity is instantiated
|
|
* and the Fragment's UI is ready. Put final initialization
|
|
* steps here.
|
|
*/
|
|
@Override
|
|
onActivityCreated(Bundle savedInstanceState) {
|
|
...
|
|
// Initializes the loader framework
|
|
getLoaderManager().initLoader(DETAILS_QUERY_ID, null, this);
|
|
</pre>
|
|
<h3>Implement onCreateLoader()</h3>
|
|
<p>
|
|
Implement the {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader
|
|
onCreateLoader()} method, which is called by the loader framework immediately after you call
|
|
{@link android.support.v4.app.LoaderManager#initLoader initLoader()}. Return a
|
|
{@link android.support.v4.content.CursorLoader} from this method. Since you're searching
|
|
the {@link android.provider.ContactsContract.Data} table, use the constant
|
|
{@link android.provider.ContactsContract.Data#CONTENT_URI Data.CONTENT_URI} as the content URI.
|
|
For example:
|
|
</p>
|
|
<pre>
|
|
@Override
|
|
public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) {
|
|
// Choose the proper action
|
|
switch (loaderId) {
|
|
case DETAILS_QUERY_ID:
|
|
// Assigns the selection parameter
|
|
mSelectionArgs[0] = mLookupKey;
|
|
// Starts the query
|
|
CursorLoader mLoader =
|
|
new CursorLoader(
|
|
getActivity(),
|
|
Data.CONTENT_URI,
|
|
PROJECTION,
|
|
SELECTION,
|
|
mSelectionArgs,
|
|
SORT_ORDER
|
|
);
|
|
...
|
|
}
|
|
</pre>
|
|
<h3>Implement onLoadFinished() and onLoaderReset()</h3>
|
|
<p>
|
|
Implement the
|
|
{@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()}
|
|
method. The loader framework calls
|
|
{@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()}
|
|
when the Contacts Provider returns the results of the query. For example:
|
|
</p>
|
|
<pre>
|
|
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
|
|
switch (loader.getId()) {
|
|
case DETAILS_QUERY_ID:
|
|
/*
|
|
* Process the resulting Cursor here.
|
|
*/
|
|
}
|
|
break;
|
|
...
|
|
}
|
|
}
|
|
</pre>
|
|
<p>
|
|
<p>
|
|
The method {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoaderReset
|
|
onLoaderReset()} is invoked when the loader framework detects that the data backing the result
|
|
{@link android.database.Cursor} has changed. At this point, remove any existing references
|
|
to the {@link android.database.Cursor} by setting them to null. If you don't, the loader
|
|
framework won't destroy the old {@link android.database.Cursor}, and you'll get a memory
|
|
leak. For example:
|
|
<pre>
|
|
@Override
|
|
public void onLoaderReset(Loader<Cursor> loader) {
|
|
switch (loader.getId()) {
|
|
case DETAILS_QUERY_ID:
|
|
/*
|
|
* If you have current references to the Cursor,
|
|
* remove them here.
|
|
*/
|
|
}
|
|
break;
|
|
}
|
|
</pre>
|
|
<h2 id="RetrieveSpecific">Retrieve Specific Details for a Contact</h2>
|
|
<p>
|
|
Retrieving a specific data type for a contact, such as all the emails, follows the same pattern
|
|
as retrieving all details. These are the only changes you need to make to the code
|
|
listed in <a href="#RetrieveAll">Retrieve All Details for a Contact</a>:
|
|
</p>
|
|
<dl>
|
|
<dt>
|
|
Projection
|
|
</dt>
|
|
<dd>
|
|
Modify your projection to retrieve the columns that are specific to the
|
|
data type. Also modify the projection to use the column name constants defined in the
|
|
{@link android.provider.ContactsContract.CommonDataKinds} subclass corresponding to the
|
|
data type.
|
|
</dd>
|
|
<dt>
|
|
Selection
|
|
</dt>
|
|
<dd>
|
|
Modify the selection text to search for the
|
|
{@link android.provider.ContactsContract.Data#MIMETYPE MIMETYPE} value that's specific to
|
|
your data type.
|
|
</dd>
|
|
<dt>
|
|
Sort order
|
|
</dt>
|
|
<dd>
|
|
Since you're only selecting a single detail type, don't group the returned
|
|
{@link android.database.Cursor} by {@link android.provider.ContactsContract.Data#MIMETYPE
|
|
Data.MIMETYPE}.
|
|
</dd>
|
|
</dl>
|
|
<p>
|
|
These modifications are described in the following sections.
|
|
</p>
|
|
<h3>Define a projection</h3>
|
|
<p>
|
|
Define the columns you want to retrieve, using the column name constants in the subclass
|
|
of {@link android.provider.ContactsContract.CommonDataKinds} for the data type.
|
|
If you plan to bind your {@link android.database.Cursor} to a {@link android.widget.ListView},
|
|
be sure to retrieve the <code>_ID</code> column. For example, to retrieve email data, define the
|
|
following projection:
|
|
</p>
|
|
<pre>
|
|
private static final String[] PROJECTION =
|
|
{
|
|
Email._ID,
|
|
Email.ADDRESS,
|
|
Email.TYPE,
|
|
Email.LABEL
|
|
};
|
|
</pre>
|
|
<p>
|
|
Notice that this projection uses the column names defined in the class
|
|
{@link android.provider.ContactsContract.CommonDataKinds.Email}, instead of the column names
|
|
defined in the class {@link android.provider.ContactsContract.Data}. Using the email-specific
|
|
column names makes the code more readable.
|
|
</p>
|
|
<p>
|
|
In the projection, you can also use any of the other columns defined in the
|
|
{@link android.provider.ContactsContract.CommonDataKinds} subclass.
|
|
</p>
|
|
<h3>Define selection criteria</h3>
|
|
<p>
|
|
Define a search text expression that retrieves rows for a specific contact's
|
|
{@link android.provider.ContactsContract.Data#LOOKUP_KEY} and the
|
|
{@link android.provider.ContactsContract.Data#MIMETYPE Data.MIMETYPE} of the details you
|
|
want. Enclose the {@link android.provider.ContactsContract.Data#MIMETYPE MIMETYPE} value in
|
|
single quotes by concatenating a "<code>'</code>" (single-quote) character to the start and end
|
|
of the constant; otherwise, the provider interprets the constant as a variable name rather
|
|
than as a string value. You don't need to use a placeholder for this value, because you're
|
|
using a constant rather than a user-supplied value. For example:
|
|
</p>
|
|
<pre>
|
|
/*
|
|
* Defines the selection clause. Search for a lookup key
|
|
* and the Email MIME type
|
|
*/
|
|
private static final String SELECTION =
|
|
Data.LOOKUP_KEY + " = ?" +
|
|
" AND " +
|
|
Data.MIMETYPE + " = " +
|
|
"'" + Email.CONTENT_ITEM_TYPE + "'";
|
|
// Defines the array to hold the search criteria
|
|
private String[] mSelectionArgs = { "" };
|
|
</pre>
|
|
<h3>Define a sort order</h3>
|
|
<p>
|
|
Define a sort order for the returned {@link android.database.Cursor}. Since you're retrieving a
|
|
specific data type, omit the sort on {@link android.provider.ContactsContract.Data#MIMETYPE}.
|
|
Instead, if the type of detail data you're searching includes a subtype, sort on it.
|
|
For example, for email data you can sort on
|
|
{@link android.provider.ContactsContract.CommonDataKinds.Email#TYPE Email.TYPE}:
|
|
</p>
|
|
<pre>
|
|
private static final String SORT_ORDER = Email.TYPE + " ASC ";
|
|
</pre>
|