The code sample didn't match the sample project. Also rewrote one paragraph to clarify that you *create* a particular XML file (external bug report said the file was missing from the sample project). Also fixed formatting in a different line sample (overlong line forced code box to scroll). Doc is staged to: http://asolovay.mtv:9098/training/contacts-provider/retrieve-names.html#NameMatch bug: 15377898 Change-Id: I0898956255aa9174b12811e6f6ca826434b28c20
818 lines
33 KiB
Plaintext
818 lines
33 KiB
Plaintext
page.title=Retrieving a List of Contacts
|
|
|
|
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="#Permissions">Request Permission to Read the Provider</a>
|
|
<li><a href="#NameMatch">Match a Contact by Name and List the Results</a></li>
|
|
<li><a href="#TypeMatch">Match a Contact By a Specific Type of Data</a></li>
|
|
<li><a href="#GeneralMatch">Match a Contact By Any Type of Data</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>
|
|
</li>
|
|
<li>
|
|
<a href="{@docRoot}guide/topics/search/search-dialog.html">Creating a Search Interface</a>
|
|
</li>
|
|
</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 you how to retrieve a list of contacts whose data matches all or part of a
|
|
search string, using the following techniques:
|
|
</p>
|
|
<dl>
|
|
<dt>Match contact names</dt>
|
|
<dd>
|
|
Retrieve a list of contacts by matching the search string to all or part of the contact
|
|
name data. The Contacts Provider allows multiple instances of the same name, so this
|
|
technique can return a list of matches.
|
|
</dd>
|
|
<dt>Match a specific type of data, such as a phone number</dt>
|
|
<dd>
|
|
Retrieve a list of contacts by matching the search string to a particular type of detail
|
|
data such as an email address. For example, this technique allows you to list all of the
|
|
contacts whose email address matches the search string.
|
|
</dd>
|
|
<dt>Match any type of data</dt>
|
|
<dd>
|
|
Retrieve a list of contacts by matching the search string to any type of detail data,
|
|
including name, phone number, street address, email address, and so forth. For example,
|
|
this technique allows you to accept any type of data for a search string and then list the
|
|
contacts for which the data matches the string.
|
|
</dd>
|
|
</dl>
|
|
<p class="note">
|
|
<strong>Note:</strong> All the examples in this lesson use a
|
|
{@link android.support.v4.content.CursorLoader} to retrieve data from the Contacts
|
|
Provider. A {@link android.support.v4.content.CursorLoader} runs its query on a
|
|
thread that's separate from the UI thread. This ensures that the query doesn't slow down UI
|
|
response times and cause a poor user experience. For more information, see the Android
|
|
training class <a href="{@docRoot}training/load-data-background/index.html">
|
|
Loading Data in the Background</a>.
|
|
</p>
|
|
<h2 id="Permissions">Request Permission to Read the Provider</h2>
|
|
<p>
|
|
To do any type of search of the Contacts Provider, your app must have
|
|
{@link android.Manifest.permission#READ_CONTACTS READ_CONTACTS} permission.
|
|
To request this, add this
|
|
<code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html"><uses-permission></a></code>
|
|
element to your manifest file as a child element of
|
|
<code><a href="{@docRoot}guide/topics/manifest/manifest-element.html"><manifest></a></code>:
|
|
</p>
|
|
<pre>
|
|
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
|
</pre>
|
|
<h2 id="NameMatch">Match a Contact by Name and List the Results</h2>
|
|
<p>
|
|
This technique tries to match a search string to the name of a contact or contacts in the
|
|
Contact Provider's {@link android.provider.ContactsContract.Contacts} table. You usually want
|
|
to display the results in a {@link android.widget.ListView}, to allow the user to choose among
|
|
the matched contacts.
|
|
</p>
|
|
<h3 id="DefineListView">Define ListView and item layouts</h3>
|
|
<p>
|
|
To display the search results in a {@link android.widget.ListView}, you need a main layout file
|
|
that defines the entire UI including the {@link android.widget.ListView}, and an item layout
|
|
file that defines one line of the {@link android.widget.ListView}. For example, you could create
|
|
the main layout file <code>res/layout/contacts_list_view.xml</code> with
|
|
the following XML:
|
|
</p>
|
|
<pre>
|
|
<?xml version="1.0" encoding="utf-8"?>
|
|
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
|
|
android:id="@android:id/list"
|
|
android:layout_width="match_parent"
|
|
android:layout_height="match_parent"/>
|
|
</pre>
|
|
<p>
|
|
This XML uses the built-in Android {@link android.widget.ListView} widget
|
|
{@link android.R.id#list android:id/list}.
|
|
</p>
|
|
<p>
|
|
Define the item layout file <code>contacts_list_item.xml</code> with the following XML:
|
|
</p>
|
|
<pre>
|
|
<?xml version="1.0" encoding="utf-8"?>
|
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
|
android:id="@android:id/text1"
|
|
android:layout_width="match_parent"
|
|
android:layout_height="wrap_content"
|
|
android:clickable="true"/>
|
|
</pre>
|
|
<p>
|
|
This XML uses the built-in Android {@link android.widget.TextView} widget
|
|
{@link android.R.id#text1 android:text1}.
|
|
</p>
|
|
<p class="note">
|
|
<strong>Note:</strong> This lesson doesn't describe the UI for getting a search string from the
|
|
user, because you may want to get the string indirectly. For example, you can give the user
|
|
an option to search for contacts whose name matches a string in an incoming text message.
|
|
</p>
|
|
<p>
|
|
The two layout files you've written define a user interface that shows a
|
|
{@link android.widget.ListView}. The next step is to write code that uses this UI to display a
|
|
list of contacts.
|
|
</p>
|
|
<h3 id="Fragment">Define a Fragment that displays the list of contacts</h3>
|
|
<p>
|
|
To display the list of contacts, start by defining a {@link android.support.v4.app.Fragment}
|
|
that's loaded by an {@link android.app.Activity}. Using a
|
|
{@link android.support.v4.app.Fragment} is a more flexible technique, because you can use
|
|
one {@link android.support.v4.app.Fragment} to display the list and a second
|
|
{@link android.support.v4.app.Fragment} to display the details for a contact that the user
|
|
chooses from the list. Using this approach, you can combine one of the techniques presented in
|
|
this lesson with one from the lesson <a href="retrieve-details.html">
|
|
Retrieving Details for a Contact</a>.
|
|
</p>
|
|
<p>
|
|
To learn how to use one or more {@link android.support.v4.app.Fragment} objects from an
|
|
an {@link android.app.Activity}, read the training class
|
|
<a href="{@docRoot}training/basics/fragments/index.html">
|
|
Building a Dynamic UI with Fragments</a>.
|
|
</p>
|
|
<p>
|
|
To help you write queries against the Contacts Provider, the Android framework provides a
|
|
contracts class called {@link android.provider.ContactsContract}, which defines useful
|
|
constants and methods for accessing the provider. When you use this class, you don't have to
|
|
define your own constants for content URIs, table names, or columns. To use this class,
|
|
include the following statement:
|
|
</p>
|
|
<pre>
|
|
import android.provider.ContactsContract;
|
|
</pre>
|
|
<p>
|
|
Since the code uses a {@link android.support.v4.content.CursorLoader} to retrieve data
|
|
from the provider, you must specify that it implements the loader interface
|
|
{@link android.support.v4.app.LoaderManager.LoaderCallbacks}. Also, to help detect which contact
|
|
the user selects from the list of search results, implement the adapter interface
|
|
{@link android.widget.AdapterView.OnItemClickListener}. For example:
|
|
</p>
|
|
<pre>
|
|
...
|
|
import android.support.v4.app.Fragment;
|
|
import android.support.v4.app.LoaderManager.LoaderCallbacks;
|
|
import android.widget.AdapterView;
|
|
...
|
|
public class ContactsFragment extends Fragment implements
|
|
LoaderManager.LoaderCallbacks<Cursor>,
|
|
AdapterView.OnItemClickListener {
|
|
</pre>
|
|
<h3 id="DefineVariables">Define global variables</h3>
|
|
<p>
|
|
Define global variables that are used in other parts of the code:
|
|
</p>
|
|
<pre>
|
|
...
|
|
/*
|
|
* Defines an array that contains column names to move from
|
|
* the Cursor to the ListView.
|
|
*/
|
|
@SuppressLint("InlinedApi")
|
|
private final static String[] FROM_COLUMNS = {
|
|
Build.VERSION.SDK_INT
|
|
>= Build.VERSION_CODES.HONEYCOMB ?
|
|
Contacts.DISPLAY_NAME_PRIMARY :
|
|
Contacts.DISPLAY_NAME
|
|
};
|
|
/*
|
|
* Defines an array that contains resource ids for the layout views
|
|
* that get the Cursor column contents. The id is pre-defined in
|
|
* the Android framework, so it is prefaced with "android.R.id"
|
|
*/
|
|
private final static int[] TO_IDS = {
|
|
android.R.id.text1
|
|
};
|
|
// Define global mutable variables
|
|
// Define a ListView object
|
|
ListView mContactsList;
|
|
// Define variables for the contact the user selects
|
|
// The contact's _ID value
|
|
long mContactId;
|
|
// The contact's LOOKUP_KEY
|
|
String mContactKey;
|
|
// A content URI for the selected contact
|
|
Uri mContactUri;
|
|
// An adapter that binds the result Cursor to the ListView
|
|
private SimpleCursorAdapter mCursorAdapter;
|
|
...
|
|
</pre>
|
|
<p class="note">
|
|
<strong>Note:</strong> Since
|
|
{@link android.provider.ContactsContract.Contacts#DISPLAY_NAME_PRIMARY
|
|
Contacts.DISPLAY_NAME_PRIMARY} requires Android 3.0 (API version 11) or later, setting your
|
|
app's <code>minSdkVersion</code> to 10 or below generates an Android Lint warning in
|
|
Eclipse with ADK. To turn off this warning, add the annotation
|
|
<code>@SuppressLint("InlinedApi")</code> before the definition of <code>FROM_COLUMNS</code>.
|
|
</p>
|
|
<h3 id="InitializeFragment">Initialize the Fragment</h3>
|
|
<p>
|
|
|
|
Initialize the {@link android.support.v4.app.Fragment}. Add the empty, public constructor
|
|
required by the Android system, and inflate the {@link android.support.v4.app.Fragment} object's
|
|
UI in the callback method {@link android.support.v4.app.Fragment#onCreateView onCreateView()}.
|
|
For example:
|
|
</p>
|
|
<pre>
|
|
// Empty public constructor, required by the system
|
|
public ContactsFragment() {}
|
|
|
|
// A UI Fragment must inflate its View
|
|
@Override
|
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
|
Bundle savedInstanceState) {
|
|
// Inflate the fragment layout
|
|
return inflater.inflate(R.layout.contact_list_fragment,
|
|
container, false);
|
|
}
|
|
</pre>
|
|
<h3 id="DefineAdapter">Set up the CursorAdapter for the ListView</h3>
|
|
<p>
|
|
Set up the {@link android.support.v4.widget.SimpleCursorAdapter} that binds the results of the
|
|
search to the {@link android.widget.ListView}. To get the {@link android.widget.ListView} object
|
|
that displays the contacts, you need to call {@link android.app.Activity#findViewById
|
|
Activity.findViewById()} using the parent activity of the
|
|
{@link android.support.v4.app.Fragment}. Use the {@link android.content.Context} of the
|
|
parent activity when you call {@link android.widget.ListView#setAdapter setAdapter()}.
|
|
For example:
|
|
</p>
|
|
<pre>
|
|
public void onActivityCreated(Bundle savedInstanceState) {
|
|
super.onActivityCreated(savedInstanceState);
|
|
...
|
|
// Gets the ListView from the View list of the parent activity
|
|
mContactsList =
|
|
(ListView) getActivity().findViewById(R.layout.contact_list_view);
|
|
// Gets a CursorAdapter
|
|
mCursorAdapter = new SimpleCursorAdapter(
|
|
getActivity(),
|
|
R.layout.contact_list_item,
|
|
null,
|
|
FROM_COLUMNS, TO_IDS,
|
|
0);
|
|
// Sets the adapter for the ListView
|
|
mContactsList.setAdapter(mCursorAdapter);
|
|
}
|
|
</pre>
|
|
<h3 id="SetListener">Set the selected contact listener</h3>
|
|
<p>
|
|
When you display the results of a search, you usually want to allow the user to select a
|
|
single contact for further processing. For example, when the user clicks a contact you can
|
|
display the contact's address on a map. To provide this feature, you first defined the current
|
|
{@link android.support.v4.app.Fragment} as the click listener by specifying that the class
|
|
implements {@link android.widget.AdapterView.OnItemClickListener}, as shown in the section
|
|
<a href="#Fragment">Define a Fragment that displays the list of contacts</a>.
|
|
</p>
|
|
<p>
|
|
To continue setting up the listener, bind it to the {@link android.widget.ListView} by
|
|
calling the method {@link android.widget.ListView#setOnItemClickListener
|
|
setOnItemClickListener()} in {@link android.support.v4.app.Fragment#onActivityCreated
|
|
onActivityCreated()}. For example:
|
|
</p>
|
|
<pre>
|
|
public void onActivityCreated(Bundle savedInstanceState) {
|
|
...
|
|
// Set the item click listener to be the current fragment.
|
|
mContactsList.setOnItemClickListener(this);
|
|
...
|
|
}
|
|
</pre>
|
|
<p>
|
|
Since you specified that the current {@link android.support.v4.app.Fragment} is the
|
|
{@link android.widget.AdapterView.OnItemClickListener OnItemClickListener} for the
|
|
{@link android.widget.ListView}, you now need to implement its required method
|
|
{@link android.widget.AdapterView.OnItemClickListener#onItemClick onItemClick()}, which
|
|
handles the click event. This is described in a succeeding section.
|
|
</p>
|
|
<h3 id="DefineProjection">Define a projection</h3>
|
|
<p>
|
|
Define a constant that contains the columns you want to return from your query. Each item in
|
|
the {@link android.widget.ListView} displays the contact's display name,
|
|
which contains the main form of the contact's name. In Android 3.0 (API version 11) and later,
|
|
the name of this column is
|
|
{@link android.provider.ContactsContract.Contacts#DISPLAY_NAME_PRIMARY
|
|
Contacts.DISPLAY_NAME_PRIMARY}; in versions previous to that, its name is
|
|
{@link android.provider.ContactsContract.Contacts#DISPLAY_NAME Contacts.DISPLAY_NAME}.
|
|
</p>
|
|
<p>
|
|
The column {@link android.provider.ContactsContract.Contacts#_ID Contacts._ID} is used by the
|
|
{@link android.support.v4.widget.SimpleCursorAdapter} binding process.
|
|
{@link android.provider.ContactsContract.Contacts#_ID Contacts._ID} and
|
|
{@link android.provider.ContactsContract.Contacts#LOOKUP_KEY} are used together to
|
|
construct a content URI for the contact the user selects.
|
|
</p>
|
|
<pre>
|
|
...
|
|
@SuppressLint("InlinedApi")
|
|
private static final String[] PROJECTION =
|
|
{
|
|
Contacts._ID,
|
|
Contacts.LOOKUP_KEY,
|
|
Build.VERSION.SDK_INT
|
|
>= Build.VERSION_CODES.HONEYCOMB ?
|
|
Contacts.DISPLAY_NAME_PRIMARY :
|
|
Contacts.DISPLAY_NAME
|
|
|
|
};
|
|
</pre>
|
|
<h3 id="DefineConstants">Define constants for the Cursor column indexes</h3>
|
|
<p>
|
|
To get data from an individual column in a {@link android.database.Cursor}, you need
|
|
the column's index within the {@link android.database.Cursor}. You can define constants
|
|
for the indexes of the {@link android.database.Cursor} columns, because the indexes are
|
|
the same as the order of the column names in your projection. For example:
|
|
</p>
|
|
<pre>
|
|
// The column index for the _ID column
|
|
private static final int CONTACT_ID_INDEX = 0;
|
|
// The column index for the LOOKUP_KEY column
|
|
private static final int LOOKUP_KEY_INDEX = 1;
|
|
</pre>
|
|
<h3 id="SelectionCriteria">Specify the selection criteria</h3>
|
|
<p>
|
|
To specify the data you want, create a combination of text expressions and variables
|
|
that tell the provider the data columns to search and the values to find.
|
|
</p>
|
|
<p>
|
|
For the text expression, define a constant that lists the search columns. Although this
|
|
expression can contain values as well, the preferred practice is to represent the values with
|
|
a "?" placeholder. During retrieval, the placeholder is replaced with values from an
|
|
array. Using "?" as a placeholder ensures that the search specification is generated by binding
|
|
rather than by SQL compilation. This practice eliminates the possibility of malicious SQL
|
|
injection. For example:
|
|
</p>
|
|
<pre>
|
|
// Defines the text expression
|
|
@SuppressLint("InlinedApi")
|
|
private static final String SELECTION =
|
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ?
|
|
Contacts.DISPLAY_NAME_PRIMARY + " LIKE ?" :
|
|
Contacts.DISPLAY_NAME + " LIKE ?";
|
|
// Defines a variable for the search string
|
|
private String mSearchString;
|
|
// Defines the array to hold values that replace the ?
|
|
private String[] mSelectionArgs = { mSearchString };
|
|
</pre>
|
|
<h3 id="OnItemClick">Define the onItemClick() method</h3>
|
|
<p>
|
|
In a previous section, you set the item click listener for the {@link android.widget.ListView}.
|
|
Now implement the action for the listener by defining the method
|
|
{@link android.widget.AdapterView.OnItemClickListener#onItemClick
|
|
AdapterView.OnItemClickListener.onItemClick()}:
|
|
</p>
|
|
<pre>
|
|
@Override
|
|
public void onItemClick(
|
|
AdapterView<?> parent, View item, int position, long rowID) {
|
|
// Get the Cursor
|
|
Cursor cursor = parent.getAdapter().getCursor();
|
|
// Move to the selected contact
|
|
cursor.moveToPosition(position);
|
|
// Get the _ID value
|
|
mContactId = getLong(CONTACT_ID_INDEX);
|
|
// Get the selected LOOKUP KEY
|
|
mContactKey = getString(CONTACT_KEY_INDEX);
|
|
// Create the contact's content Uri
|
|
mContactUri = Contacts.getLookupUri(mContactId, mContactKey);
|
|
/*
|
|
* You can use mContactUri as the content URI for retrieving
|
|
* the details for a contact.
|
|
*/
|
|
}
|
|
</pre>
|
|
<h3 id="InitializeLoader">Initialize the loader</h3>
|
|
<p>
|
|
Since you're using a {@link android.support.v4.content.CursorLoader} to retrieve data,
|
|
you must initialize the background thread and other variables that control asynchronous
|
|
retrieval. Do the initialization in
|
|
{@link android.support.v4.app.Fragment#onActivityCreated onActivityCreated()}, which
|
|
is invoked immediately before the {@link android.support.v4.app.Fragment} UI appears, as
|
|
shown in the following example:
|
|
</p>
|
|
<pre>
|
|
public class ContactsFragment extends Fragment implements
|
|
LoaderManager.LoaderCallbacks<Cursor> {
|
|
...
|
|
// Called just before the Fragment displays its UI
|
|
@Override
|
|
public void onActivityCreated(Bundle savedInstanceState) {
|
|
// Always call the super method first
|
|
super.onActivityCreated(savedInstanceState);
|
|
...
|
|
// Initializes the loader
|
|
getLoaderManager().initLoader(0, null, this);
|
|
</pre>
|
|
<h3 id="OnCreateLoader">Implement onCreateLoader()</h3>
|
|
<p>
|
|
Implement the method
|
|
{@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()},
|
|
which is called by the loader framework immediately after you call
|
|
{@link android.support.v4.app.LoaderManager#initLoader initLoader()}.
|
|
<p>
|
|
In {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()},
|
|
set up the search string pattern. To make a string into a pattern, insert "%"
|
|
(percent) characters to represent a sequence of zero or more characters, or "_" (underscore)
|
|
characters to represent a single character, or both. For example, the pattern "%Jefferson%"
|
|
would match both "Thomas Jefferson" and "Jefferson Davis".
|
|
</p>
|
|
<p>
|
|
Return a new {@link android.support.v4.content.CursorLoader} from the method. For the content
|
|
URI, use {@link android.provider.ContactsContract.Contacts#CONTENT_URI Contacts.CONTENT_URI}.
|
|
This URI refers to the entire table, as shown in the following example:
|
|
</p>
|
|
<pre>
|
|
...
|
|
@Override
|
|
public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) {
|
|
/*
|
|
* Makes search string into pattern and
|
|
* stores it in the selection array
|
|
*/
|
|
mSelectionArgs[0] = "%" + mSearchString + "%";
|
|
// Starts the query
|
|
return new CursorLoader(
|
|
getActivity(),
|
|
Contacts.CONTENT_URI,
|
|
PROJECTION,
|
|
SELECTION,
|
|
mSelectionArgs,
|
|
null
|
|
);
|
|
}
|
|
</pre>
|
|
<h3 id="FinishedReset">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. In this method, put the
|
|
result {@link android.database.Cursor} in the
|
|
{@link android.support.v4.widget.SimpleCursorAdapter}. This automatically updates the
|
|
{@link android.widget.ListView} with the search results:
|
|
</p>
|
|
<pre>
|
|
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
|
|
// Put the result Cursor in the adapter for the ListView
|
|
mCursorAdapter.swapCursor(cursor);
|
|
}
|
|
</pre>
|
|
<p>
|
|
The method {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoaderReset
|
|
onLoaderReset()} is invoked when the loader framework detects that the
|
|
result {@link android.database.Cursor} contains stale data. Delete the
|
|
{@link android.support.v4.widget.SimpleCursorAdapter} reference to the existing
|
|
{@link android.database.Cursor}. If you don't, the loader framework will not
|
|
recycle the {@link android.database.Cursor}, which causes a memory leak. For example:
|
|
</p>
|
|
<pre>
|
|
@Override
|
|
public void onLoaderReset(Loader<Cursor> loader) {
|
|
// Delete the reference to the existing Cursor
|
|
mCursorAdapter.swapCursor(null);
|
|
|
|
}
|
|
</pre>
|
|
|
|
<p>
|
|
You now have the key pieces of an app that matches a search string to contact names and returns
|
|
the result in a {@link android.widget.ListView}. The user can click a contact name to select it.
|
|
This triggers a listener, in which you can work further with the contact's data. For example,
|
|
you can retrieve the contact's details. To learn how to do this, continue with the next
|
|
lesson, <a href="#retrieve-details.html">Retrieving Details for a Contact</a>.
|
|
</p>
|
|
<p>
|
|
To learn more about search user interfaces, read the API guide
|
|
<a href="{@docRoot}guide/topics/search/search-dialog.html">Creating a Search Interface</a>.
|
|
</p>
|
|
<p>
|
|
The remaining sections in this lesson demonstrate other ways of finding contacts in the
|
|
Contacts Provider.
|
|
</p>
|
|
<h2 id="TypeMatch">Match a Contact By a Specific Type of Data</h2>
|
|
<p>
|
|
This technique allows you to specify the type of data you want to match. Retrieving
|
|
by name is a specific example of this type of query, but you can also do it for any of the types
|
|
of detail data associated with a contact. For example, you can retrieve contacts that have a
|
|
specific postal code; in this case, the search string has to match data stored in a postal code
|
|
row.
|
|
</p>
|
|
<p>
|
|
To implement this type of retrieval, first implement the following code, as listed in
|
|
previous sections:
|
|
</p>
|
|
<ul>
|
|
<li>
|
|
Request Permission to Read the Provider.
|
|
</li>
|
|
<li>
|
|
Define ListView and item layouts.
|
|
</li>
|
|
<li>
|
|
Define a Fragment that displays the list of contacts.
|
|
</li>
|
|
<li>
|
|
Define global variables.
|
|
</li>
|
|
<li>
|
|
Initialize the Fragment.
|
|
</li>
|
|
<li>
|
|
Set up the CursorAdapter for the ListView.
|
|
</li>
|
|
<li>
|
|
Set the selected contact listener.
|
|
</li>
|
|
<li>
|
|
Define constants for the Cursor column indexes.
|
|
<p>
|
|
Although you're retrieving data from a different table, the order of the columns in
|
|
the projection is the same, so you can use the same indexes for the Cursor.
|
|
</p>
|
|
</li>
|
|
<li>
|
|
Define the onItemClick() method.
|
|
</li>
|
|
<li>
|
|
Initialize the loader.
|
|
</li>
|
|
<li>
|
|
|
|
Implement onLoadFinished() and onLoaderReset().
|
|
</li>
|
|
</ul>
|
|
<p>
|
|
The following steps show you the additional code you need to match a search string to
|
|
a particular type of detail data and display the results.
|
|
</p>
|
|
<h3>Choose the data type and table</h3>
|
|
<p>
|
|
To search for a particular type of detail data, you have to know the custom MIME type value
|
|
for the data type. Each data type has a unique MIME type
|
|
value defined by a constant <code>CONTENT_ITEM_TYPE</code> in the subclass of
|
|
{@link android.provider.ContactsContract.CommonDataKinds} associated with the data type.
|
|
The subclasses have names that indicate their data type; for example, the subclass for email
|
|
data is {@link android.provider.ContactsContract.CommonDataKinds.Email}, and the custom MIME
|
|
type for email data is defined by the constant
|
|
{@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_ITEM_TYPE
|
|
Email.CONTENT_ITEM_TYPE}.
|
|
</p>
|
|
<p>
|
|
Use the {@link android.provider.ContactsContract.Data} table for your search. All of the
|
|
constants you need for your projection, selection clause, and sort order are defined in or
|
|
inherited by this table.
|
|
</p>
|
|
<h3 id="SpecificProjection">Define a projection</h3>
|
|
<p>
|
|
To define a projection, choose one or more of the columns defined in
|
|
{@link android.provider.ContactsContract.Data} or the classes from which it inherits. The
|
|
Contacts Provider does an implicit join between {@link android.provider.ContactsContract.Data}
|
|
and other tables before it returns rows. For example:
|
|
</p>
|
|
<pre>
|
|
@SuppressLint("InlinedApi")
|
|
private static final String[] PROJECTION =
|
|
{
|
|
/*
|
|
* The detail data row ID. To make a ListView work,
|
|
* this column is required.
|
|
*/
|
|
Data._ID,
|
|
// The primary display name
|
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ?
|
|
Data.DISPLAY_NAME_PRIMARY :
|
|
Data.DISPLAY_NAME,
|
|
// The contact's _ID, to construct a content URI
|
|
Data.CONTACT_ID
|
|
// The contact's LOOKUP_KEY, to construct a content URI
|
|
Data.LOOKUP_KEY (a permanent link to the contact
|
|
};
|
|
</pre>
|
|
<h3 id="SpecificCriteria">Define search criteria</h3>
|
|
<p>
|
|
To search for a string within a particular type of data, construct a selection clause from
|
|
the following:
|
|
</p>
|
|
<ul>
|
|
<li>
|
|
The name of the column that contains your search string. This name varies by data type,
|
|
so you need to find the subclass of
|
|
{@link android.provider.ContactsContract.CommonDataKinds} that corresponds to the data type
|
|
and then choose the column name from that subclass. For example, to search for
|
|
email addresses, use the column
|
|
{@link android.provider.ContactsContract.CommonDataKinds.Email#ADDRESS Email.ADDRESS}.
|
|
</li>
|
|
<li>
|
|
The search string itself, represented as the "?" character in the selection clause.
|
|
</li>
|
|
<li>
|
|
The name of the column that contains the custom MIME type value. This name is always
|
|
{@link android.provider.ContactsContract.Data#MIMETYPE Data.MIMETYPE}.
|
|
</li>
|
|
<li>
|
|
The custom MIME type value for the data type. As described previously, this is the constant
|
|
<code>CONTENT_ITEM_TYPE</code> in the
|
|
{@link android.provider.ContactsContract.CommonDataKinds} subclass. For example, the MIME
|
|
type value for email data is
|
|
{@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_ITEM_TYPE
|
|
Email.CONTENT_ITEM_TYPE}. Enclose the 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 value 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.
|
|
</li>
|
|
</ul>
|
|
<p>
|
|
For example:
|
|
</p>
|
|
<pre>
|
|
/*
|
|
* Constructs search criteria from the search string
|
|
* and email MIME type
|
|
*/
|
|
private static final String SELECTION =
|
|
/*
|
|
* Searches for an email address
|
|
* that matches the search string
|
|
*/
|
|
Email.ADDRESS + " LIKE ? " + "AND " +
|
|
/*
|
|
* Searches for a MIME type that matches
|
|
* the value of the constant
|
|
* Email.CONTENT_ITEM_TYPE. Note the
|
|
* single quotes surrounding Email.CONTENT_ITEM_TYPE.
|
|
*/
|
|
Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'";
|
|
</pre>
|
|
<p>
|
|
Next, define variables to contain the selection argument:
|
|
</p>
|
|
<pre>
|
|
String mSearchString;
|
|
String[] mSelectionArgs = { "" };
|
|
</pre>
|
|
<h3 id="SpecificLoader">Implement onCreateLoader()</h3>
|
|
<p>
|
|
Now that you've specified the data you want and how to find it, define a query in your
|
|
implementation of {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader
|
|
onCreateLoader()}. Return a new {@link android.support.v4.content.CursorLoader} from this
|
|
method, using your projection, selection text expression, and selection array as
|
|
arguments. For a content URI, use
|
|
{@link android.provider.ContactsContract.Data#CONTENT_URI Data.CONTENT_URI}. For example:
|
|
</p>
|
|
<pre>
|
|
@Override
|
|
public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) {
|
|
// OPTIONAL: Makes search string into pattern
|
|
mSearchString = "%" + mSearchString + "%";
|
|
// Puts the search string into the selection criteria
|
|
mSelectionArgs[0] = mSearchString;
|
|
// Starts the query
|
|
return new CursorLoader(
|
|
getActivity(),
|
|
Data.CONTENT_URI,
|
|
PROJECTION,
|
|
SELECTION,
|
|
mSelectionArgs,
|
|
null
|
|
);
|
|
}
|
|
</pre>
|
|
<p>
|
|
These code snippets are the basis of a simple reverse lookup based on a specific type of detail
|
|
data. This is the best technique to use if your app focuses on a particular type of data, such
|
|
as emails, and you want allow users to get the names associated with a piece of data.
|
|
</p>
|
|
<h2 id="GeneralMatch">Match a Contact By Any Type of Data</h2>
|
|
<p>
|
|
Retrieving a contact based on any type of data returns contacts if any of their data matches a
|
|
the search string, including name, email address, postal address, phone number, and so forth.
|
|
This results in a broad set of search results. For example, if the search string
|
|
is "Doe", then searching for any data type returns the contact "John Doe"; it also returns
|
|
contacts who live on "Doe Street".
|
|
</p>
|
|
<p>
|
|
To implement this type of retrieval, first implement the following code, as listed in
|
|
previous sections:
|
|
</p>
|
|
<ul>
|
|
<li>
|
|
Request Permission to Read the Provider.
|
|
</li>
|
|
<li>
|
|
Define ListView and item layouts.
|
|
</li>
|
|
<li>
|
|
<li>
|
|
Define a Fragment that displays the list of contacts.
|
|
</li>
|
|
<li>
|
|
Define global variables.
|
|
</li>
|
|
<li>
|
|
Initialize the Fragment.
|
|
</li>
|
|
<li>
|
|
Set up the CursorAdapter for the ListView.
|
|
</li>
|
|
<li>
|
|
Set the selected contact listener.
|
|
</li>
|
|
<li>
|
|
Define a projection.
|
|
</li>
|
|
<li>
|
|
Define constants for the Cursor column indexes.
|
|
<p>
|
|
For this type of retrieval, you're using the same table you used in the section
|
|
<a href="#NameMatch">Match a Contact by Name and List the Results</a>. Use the
|
|
same column indexes as well.
|
|
</p>
|
|
</li>
|
|
<li>
|
|
Define the onItemClick() method.
|
|
</li>
|
|
<li>
|
|
Initialize the loader.
|
|
</li>
|
|
<li>
|
|
|
|
Implement onLoadFinished() and onLoaderReset().
|
|
</li>
|
|
</ul>
|
|
<p>
|
|
The following steps show you the additional code you need to match a search string to
|
|
any type of data and display the results.
|
|
</p>
|
|
<h3 id="NoSelection">Remove selection criteria</h3>
|
|
<p>
|
|
Don't define the <code>SELECTION</code> constants or the <code>mSelectionArgs</code> variable.
|
|
These aren't used in this type of retrieval.
|
|
</p>
|
|
<h3 id="CreateLoaderAny">Implement onCreateLoader()</h3>
|
|
<p>
|
|
Implement the {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader
|
|
onCreateLoader()} method, returning a new {@link android.support.v4.content.CursorLoader}.
|
|
You don't need to convert the search string into a pattern, because the Contacts Provider does
|
|
that automatically. Use
|
|
{@link android.provider.ContactsContract.Contacts#CONTENT_FILTER_URI
|
|
Contacts.CONTENT_FILTER_URI} as the base URI, and append your search string to it by calling
|
|
{@link android.net.Uri#withAppendedPath Uri.withAppendedPath()}. Using this URI
|
|
automatically triggers searching for any data type, as shown in the following example:
|
|
</p>
|
|
<pre>
|
|
@Override
|
|
public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) {
|
|
/*
|
|
* Appends the search string to the base URI. Always
|
|
* encode search strings to ensure they're in proper
|
|
* format.
|
|
*/
|
|
Uri contentUri = Uri.withAppendedPath(
|
|
Contacts.CONTENT_FILTER_URI,
|
|
Uri.encode(mSearchString));
|
|
// Starts the query
|
|
return new CursorLoader(
|
|
getActivity(),
|
|
contentUri,
|
|
PROJECTION,
|
|
null,
|
|
null,
|
|
null
|
|
);
|
|
}
|
|
</pre>
|
|
<p>
|
|
These code snippets are the basis of an app that does a broad search of the Contacts Provider.
|
|
The technique is useful for apps that want to implement functionality similar to the
|
|
People app's contact list screen.
|
|
</p>
|