Merge "Android Training: Accessing Contacts" into jb-mr1-dev
This commit is contained in:
BIN
docs/html/training/contacts-provider/ContactsList.zip
Normal file
BIN
docs/html/training/contacts-provider/ContactsList.zip
Normal file
Binary file not shown.
635
docs/html/training/contacts-provider/display-contact-badge.jd
Normal file
635
docs/html/training/contacts-provider/display-contact-badge.jd
Normal file
@@ -0,0 +1,635 @@
|
||||
page.title=Displaying the Quick Contact Badge
|
||||
|
||||
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="#AddView">Add a QuickContactBadge View</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#SetURIThumbnail">Set the Contact URI and Thumbnail</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#ListView">
|
||||
Add a QuickContactBadge to a ListView
|
||||
</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>
|
||||
</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 add a {@link android.widget.QuickContactBadge} to your UI
|
||||
and how to bind data to it. A {@link android.widget.QuickContactBadge} is a widget that
|
||||
initially appears as a thumbnail image. Although you can use any {@link android.graphics.Bitmap}
|
||||
for the thumbnail image, you usually use a {@link android.graphics.Bitmap} decoded from the
|
||||
contact's photo thumbnail image.
|
||||
</p>
|
||||
<p>
|
||||
The small image acts as a control; when users click on the image, the
|
||||
{@link android.widget.QuickContactBadge} expands into a dialog containing the following:
|
||||
</p>
|
||||
<dl>
|
||||
<dt>A large image</dt>
|
||||
<dd>
|
||||
The large image associated with the contact, or no image is available, a placeholder
|
||||
graphic.
|
||||
</dd>
|
||||
<dt>
|
||||
App icons
|
||||
</dt>
|
||||
<dd>
|
||||
An app icon for each piece of detail data that can be handled by a built-in app. For
|
||||
example, if the contact's details include one or more email addresses, an email icon
|
||||
appears. When users click the icon, all of the contact's email addresses appear. When users
|
||||
click one of the addresses, the email app displays a screen for composing a message to the
|
||||
selected email address.
|
||||
</dd>
|
||||
</dl>
|
||||
<p>
|
||||
The {@link android.widget.QuickContactBadge} view provides instant access to a contact's
|
||||
details, as well as a fast way of communicating with the contact. Users don't have to look up
|
||||
a contact, find and copy information, and then paste it into the appropriate app. Instead, they
|
||||
can click on the {@link android.widget.QuickContactBadge}, choose the communication method they
|
||||
want to use, and send the information for that method directly to the appropriate app.
|
||||
</p>
|
||||
<h2 id="AddView">Add a QuickContactBadge View</h2>
|
||||
<p>
|
||||
To add a {@link android.widget.QuickContactBadge}, insert a
|
||||
<code><QuickContactBadge></code> element in your layout. For example:
|
||||
</p>
|
||||
<pre>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
...
|
||||
<QuickContactBadge
|
||||
android:id=@+id/quickbadge
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:scaleType="centerCrop"/>
|
||||
...
|
||||
</RelativeLayout>
|
||||
</pre>
|
||||
<h2 id="">Retrieve provider data</h2>
|
||||
<p>
|
||||
To display a contact in the {@link android.widget.QuickContactBadge}, you need a content URI
|
||||
for the contact and a {@link android.graphics.Bitmap} for the small image. You generate
|
||||
both the content URI and the {@link android.graphics.Bitmap} from columns retrieved from the
|
||||
Contacts Provider. Specify these columns as part of the projection you use to load data into
|
||||
your {@link android.database.Cursor}.
|
||||
</p>
|
||||
<p>
|
||||
For Android 3.0 (API level 11) and later, include the following columns in your projection:</p>
|
||||
<ul>
|
||||
<li>{@link android.provider.ContactsContract.Contacts#_ID Contacts._ID}</li>
|
||||
<li>{@link android.provider.ContactsContract.Contacts#LOOKUP_KEY Contacts.LOOKUP_KEY}</li>
|
||||
<li>
|
||||
{@link android.provider.ContactsContract.Contacts#PHOTO_THUMBNAIL_URI
|
||||
Contacts.PHOTO_THUMBNAIL_URI}
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
For Android 2.3.3 (API level 10) and earlier, use the following columns:
|
||||
</p>
|
||||
<ul>
|
||||
<li>{@link android.provider.ContactsContract.Contacts#_ID Contacts._ID}</li>
|
||||
<li>{@link android.provider.ContactsContract.Contacts#LOOKUP_KEY Contacts.LOOKUP_KEY}</li>
|
||||
</ul>
|
||||
<p>
|
||||
The remainder of this lesson assumes that you've already loaded a
|
||||
{@link android.database.Cursor} that contains these columns as well as others you may have
|
||||
chosen. To learn how to retrieve this columns in a {@link android.database.Cursor}, read the
|
||||
lesson <a href="retrieve-names.html">Retrieving a List of Contacts</a>.
|
||||
</p>
|
||||
<h2 id="SetURIThumbnail">Set the Contact URI and Thumbnail</h2>
|
||||
<p>
|
||||
Once you have the necessary columns, you can bind data to the
|
||||
{@link android.widget.QuickContactBadge}.
|
||||
</p>
|
||||
<h3>Set the Contact URI</h3>
|
||||
<p>
|
||||
To set the content URI for the contact, call
|
||||
{@link android.provider.ContactsContract.Contacts#getLookupUri getLookupUri(id,lookupKey)} to
|
||||
get a {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}, then
|
||||
call {@link android.widget.QuickContactBadge#assignContactUri assignContactUri()} to set the
|
||||
contact. For example:
|
||||
</p>
|
||||
<pre>
|
||||
// The Cursor that contains contact rows
|
||||
Cursor mCursor;
|
||||
// The index of the _ID column in the Cursor
|
||||
int mIdColumn;
|
||||
// The index of the LOOKUP_KEY column in the Cursor
|
||||
int mLookupKeyColumn;
|
||||
// A content URI for the desired contact
|
||||
Uri mContactUri;
|
||||
// A handle to the QuickContactBadge view
|
||||
QuickContactBadge mBadge;
|
||||
...
|
||||
mBadge = (QuickContactBadge) findViewById(R.id.quickbadge);
|
||||
/*
|
||||
* Insert code here to move to the desired cursor row
|
||||
*/
|
||||
// Gets the _ID column index
|
||||
mIdColumn = mCursor.getColumnIndex(Contacts._ID);
|
||||
// Gets the LOOKUP_KEY index
|
||||
mLookupKeyColumn = mCursor.getColumnIndex(Contacts.LOOKUP_KEY);
|
||||
// Gets a content URI for the contact
|
||||
mContactUri =
|
||||
Contacts.getLookupUri(
|
||||
Cursor.getLong(mIdColumn),
|
||||
Cursor.getString(mLookupKeyColumn)
|
||||
);
|
||||
mBadge.assignContactUri(mContactUri);
|
||||
</pre>
|
||||
<p>
|
||||
When users click the {@link android.widget.QuickContactBadge} icon, the contact's
|
||||
details automatically appear in the dialog.
|
||||
</p>
|
||||
<h3>Set the photo thumbnail</h3>
|
||||
<p>
|
||||
Setting the contact URI for the {@link android.widget.QuickContactBadge} does not automatically
|
||||
load the contact's thumbnail photo. To load the photo, get a URI for the photo from the
|
||||
contact's {@link android.database.Cursor} row, use it to open the file containing the compressed
|
||||
thumbnail photo, and read the file into a {@link android.graphics.Bitmap}.
|
||||
</p>
|
||||
<p class="note">
|
||||
<strong>Note:</strong> The
|
||||
{@link android.provider.ContactsContract.Contacts#PHOTO_THUMBNAIL_URI} column isn't available
|
||||
in platform versions prior to 3.0. For those versions, you must retrieve the URI
|
||||
from the {@link android.provider.ContactsContract.Contacts.Photo Contacts.Photo} subtable.
|
||||
</p>
|
||||
<p>
|
||||
First, set up variables for accessing the {@link android.database.Cursor} containing the
|
||||
{@link android.provider.ContactsContract.Contacts#_ID Contacts._ID} and
|
||||
{@link android.provider.ContactsContract.Contacts#LOOKUP_KEY Contacts.LOOKUP_KEY} columns, as
|
||||
described previously:
|
||||
</p>
|
||||
<pre>
|
||||
// The column in which to find the thumbnail ID
|
||||
int mThumbnailColumn;
|
||||
/*
|
||||
* The thumbnail URI, expressed as a String.
|
||||
* Contacts Provider stores URIs as String values.
|
||||
*/
|
||||
String mThumbnailUri;
|
||||
...
|
||||
/*
|
||||
* Gets the photo thumbnail column index if
|
||||
* platform version >= Honeycomb
|
||||
*/
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
mThumbnailColumn =
|
||||
mCursor.getColumnIndex(Contacts.PHOTO_THUMBNAIL_URI);
|
||||
// Otherwise, sets the thumbnail column to the _ID column
|
||||
} else {
|
||||
mThumbnailColumn = mIdColumn;
|
||||
}
|
||||
/*
|
||||
* Assuming the current Cursor position is the contact you want,
|
||||
* gets the thumbnail ID
|
||||
*/
|
||||
mThumbnailUri = Cursor.getString(mThumbnailColumn);
|
||||
...
|
||||
</pre>
|
||||
<p>
|
||||
Define a method that takes photo-related data for the contact and dimensions for the
|
||||
destination view, and returns the properly-sized thumbnail in a
|
||||
{@link android.graphics.Bitmap}. Start by constructing a URI that points to the
|
||||
thumbnail:
|
||||
<p>
|
||||
<pre>
|
||||
/**
|
||||
* Load a contact photo thumbnail and return it as a Bitmap,
|
||||
* resizing the image to the provided image dimensions as needed.
|
||||
* @param photoData photo ID Prior to Honeycomb, the contact's _ID value.
|
||||
* For Honeycomb and later, the value of PHOTO_THUMBNAIL_URI.
|
||||
* @return A thumbnail Bitmap, sized to the provided width and height.
|
||||
* Returns null if the thumbnail is not found.
|
||||
*/
|
||||
private Bitmap loadContactPhotoThumbnail(String photoData) {
|
||||
// Creates an asset file descriptor for the thumbnail file.
|
||||
AssetFileDescriptor afd = null;
|
||||
// try-catch block for file not found
|
||||
try {
|
||||
// Creates a holder for the URI.
|
||||
Uri thumbUri;
|
||||
// If Android 3.0 or later
|
||||
if (Build.VERSION.SDK_INT
|
||||
>=
|
||||
Build.VERSION_CODES.HONEYCOMB) {
|
||||
// Sets the URI from the incoming PHOTO_THUMBNAIL_URI
|
||||
thumbUri = Uri.parse(photoData);
|
||||
} else {
|
||||
// Prior to Android 3.0, constructs a photo Uri using _ID
|
||||
/*
|
||||
* Creates a contact URI from the Contacts content URI
|
||||
* incoming photoData (_ID)
|
||||
*/
|
||||
final Uri contactUri = Uri.withAppendedPath(
|
||||
Contacts.CONTENT_URI, photoData);
|
||||
/*
|
||||
* Creates a photo URI by appending the content URI of
|
||||
* Contacts.Photo.
|
||||
*/
|
||||
thumbUri =
|
||||
Uri.withAppendedPath(
|
||||
contactUri, Photo.CONTENT_DIRECTORY);
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieves an AssetFileDescriptor object for the thumbnail
|
||||
* URI
|
||||
* using ContentResolver.openAssetFileDescriptor
|
||||
*/
|
||||
afd = getActivity().getContentResolver().
|
||||
openAssetFileDescriptor(thumbUri, "r");
|
||||
/*
|
||||
* Gets a file descriptor from the asset file descriptor.
|
||||
* This object can be used across processes.
|
||||
*/
|
||||
FileDescriptor fileDescriptor = afd.getFileDescriptor();
|
||||
// Decode the photo file and return the result as a Bitmap
|
||||
// If the file descriptor is valid
|
||||
if (fileDescriptor != null) {
|
||||
// Decodes the bitmap
|
||||
return BitmapFactory.decodeFileDescriptor(
|
||||
fileDescriptor, null, null);
|
||||
}
|
||||
// If the file isn't found
|
||||
} catch (FileNotFoundException e) {
|
||||
/*
|
||||
* Handle file not found errors
|
||||
*/
|
||||
}
|
||||
// In all cases, close the asset file descriptor
|
||||
} finally {
|
||||
if (afd != null) {
|
||||
try {
|
||||
afd.close();
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
</pre>
|
||||
<p>
|
||||
Call the <code>loadContactPhotoThumbnail()</code> method in your code to get the
|
||||
thumbnail {@link android.graphics.Bitmap}, and use the result to set the photo thumbnail in
|
||||
your {@link android.widget.QuickContactBadge}:
|
||||
</p>
|
||||
<pre>
|
||||
...
|
||||
/*
|
||||
* Decodes the thumbnail file to a Bitmap.
|
||||
*/
|
||||
Bitmap mThumbnail =
|
||||
loadContactPhotoThumbnail(mThumbnailUri);
|
||||
/*
|
||||
* Sets the image in the QuickContactBadge
|
||||
* QuickContactBadge inherits from ImageView, so
|
||||
*/
|
||||
mBadge.setImageBitmap(mThumbnail);
|
||||
</pre>
|
||||
<h2 id="ListView">Add a QuickContactBadge to a ListView</h2>
|
||||
<p>
|
||||
A {@link android.widget.QuickContactBadge} is a useful addition to a
|
||||
{@link android.widget.ListView} that displays a list of contacts. Use the
|
||||
{@link android.widget.QuickContactBadge} to display a thumbnail photo for each contact; when
|
||||
users click the thumbnail, the {@link android.widget.QuickContactBadge} dialog appears.
|
||||
</p>
|
||||
<h3>Add the QuickContactBadge element</h3>
|
||||
<p>
|
||||
To start, add a {@link android.widget.QuickContactBadge} view element to your item layout
|
||||
For example, if you want to display a {@link android.widget.QuickContactBadge} and a name for
|
||||
each contact you retrieve, put the following XML into a layout file:
|
||||
</p>
|
||||
<pre>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<QuickContactBadge
|
||||
android:id="@+id/quickcontact"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:scaleType="centerCrop"/>
|
||||
<TextView android:id="@+id/displayname"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toRightOf="@+id/quickcontact"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentTop="true"/>
|
||||
</RelativeLayout>
|
||||
</pre>
|
||||
<p>
|
||||
In the following sections, this file is referred to as <code>contact_item_layout.xml</code>.
|
||||
</p>
|
||||
<h3>Set up a custom CursorAdapter</h3>
|
||||
<p>
|
||||
To bind a {@link android.support.v4.widget.CursorAdapter} to a {@link android.widget.ListView}
|
||||
containing a {@link android.widget.QuickContactBadge}, define a custom adapter that
|
||||
extends {@link android.support.v4.widget.CursorAdapter}. This approach allows you to process the
|
||||
data in the {@link android.database.Cursor} before you bind it to the
|
||||
{@link android.widget.QuickContactBadge}. This approach also allows you to bind multiple
|
||||
{@link android.database.Cursor} columns to the {@link android.widget.QuickContactBadge}. Neither
|
||||
of these operations is possible in a regular {@link android.support.v4.widget.CursorAdapter}.
|
||||
</p>
|
||||
<p>
|
||||
The subclass of {@link android.support.v4.widget.CursorAdapter} that you define must
|
||||
override the following methods:
|
||||
</p>
|
||||
<dl>
|
||||
<dt>{@link android.support.v4.widget.CursorAdapter#newView CursorAdapter.newView()}</dt>
|
||||
<dd>
|
||||
Inflates a new {@link android.view.View} object to hold the item layout. In the override
|
||||
of this method, store handles to the child {@link android.view.View} objects of the layout,
|
||||
including the child {@link android.widget.QuickContactBadge}. By taking this approach, you
|
||||
avoid having to get handles to the child {@link android.view.View} objects each time you
|
||||
inflate a new layout.
|
||||
<p>
|
||||
You must override this method so you can get handles to the individual child
|
||||
{@link android.view.View} objects. This technique allows you to control their binding in
|
||||
{@link android.support.v4.widget.CursorAdapter#bindView CursorAdapter.bindView()}.
|
||||
</p>
|
||||
</dd>
|
||||
<dt>{@link android.support.v4.widget.CursorAdapter#bindView CursorAdapter.bindView()}</dt>
|
||||
<dd>
|
||||
Moves data from the current {@link android.database.Cursor} row to the child
|
||||
{@link android.view.View} objects of the item layout. You must override this method so
|
||||
you can bind both the contact's URI and thumbnail to the
|
||||
{@link android.widget.QuickContactBadge}. The default implementation only allows a 1-to-1
|
||||
mapping between a column and a {@link android.view.View}
|
||||
</dd>
|
||||
</dl>
|
||||
<p>
|
||||
The following code snippet contains an example of a custom subclass of
|
||||
{@link android.support.v4.widget.CursorAdapter}:
|
||||
</p>
|
||||
<h3>Define the custom list adapter</h3>
|
||||
<p>
|
||||
Define the subclass of {@link android.support.v4.widget.CursorAdapter} including its
|
||||
constructor, and override
|
||||
{@link android.support.v4.widget.CursorAdapter#newView newView()} and
|
||||
{@link android.support.v4.widget.CursorAdapter#bindView bindView()}:
|
||||
</p>
|
||||
<pre>
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
private class ContactsAdapter extends CursorAdapter {
|
||||
private LayoutInflater mInflater;
|
||||
...
|
||||
public ContactsAdapter(Context context) {
|
||||
super(context, null, 0);
|
||||
|
||||
/*
|
||||
* Gets an inflater that can instantiate
|
||||
* the ListView layout from the file.
|
||||
*/
|
||||
mInflater = LayoutInflater.from(context);
|
||||
...
|
||||
}
|
||||
...
|
||||
/**
|
||||
* Defines a class that hold resource IDs of each item layout
|
||||
* row to prevent having to look them up each time data is
|
||||
* bound to a row.
|
||||
*/
|
||||
private class ViewHolder {
|
||||
TextView displayname;
|
||||
QuickContactBadge quickcontact;
|
||||
}
|
||||
..
|
||||
@Override
|
||||
public View newView(
|
||||
Context context,
|
||||
Cursor cursor,
|
||||
ViewGroup viewGroup) {
|
||||
/* Inflates the item layout. Stores resource IDs in a
|
||||
* in a ViewHolder class to prevent having to look
|
||||
* them up each time bindView() is called.
|
||||
*/
|
||||
final View itemView =
|
||||
mInflater.inflate(
|
||||
R.layout.contact_list_layout,
|
||||
viewGroup,
|
||||
false
|
||||
);
|
||||
final ViewHolder holder = new ViewHolder();
|
||||
holder.displayname =
|
||||
(TextView) view.findViewById(R.id.displayname);
|
||||
holder.quickcontact =
|
||||
(QuickContactBadge)
|
||||
view.findViewById(R.id.quickcontact);
|
||||
view.setTag(holder);
|
||||
return view;
|
||||
}
|
||||
...
|
||||
@Override
|
||||
public void bindView(
|
||||
View view,
|
||||
Context context,
|
||||
Cursor cursor) {
|
||||
final ViewHolder holder = (ViewHolder) view.getTag();
|
||||
final String photoData =
|
||||
cursor.getString(mPhotoDataIndex);
|
||||
final String displayName =
|
||||
cursor.getString(mDisplayNameIndex);
|
||||
...
|
||||
// Sets the display name in the layout
|
||||
holder.displayname = cursor.getString(mDisplayNameIndex);
|
||||
...
|
||||
/*
|
||||
* Generates a contact URI for the QuickContactBadge.
|
||||
*/
|
||||
final Uri contactUri = Contacts.getLookupUri(
|
||||
cursor.getLong(mIdIndex),
|
||||
cursor.getString(mLookupKeyIndex));
|
||||
holder.quickcontact.assignContactUri(contactUri);
|
||||
String photoData = cursor.getString(mPhotoDataIndex);
|
||||
/*
|
||||
* Decodes the thumbnail file to a Bitmap.
|
||||
* The method loadContactPhotoThumbnail() is defined
|
||||
* in the section "Set the Contact URI and Thumbnail"
|
||||
*/
|
||||
Bitmap thumbnailBitmap =
|
||||
loadContactPhotoThumbnail(photoData);
|
||||
/*
|
||||
* Sets the image in the QuickContactBadge
|
||||
* QuickContactBadge inherits from ImageView
|
||||
*/
|
||||
holder.quickcontact.setImageBitmap(thumbnailBitmap);
|
||||
}
|
||||
</pre>
|
||||
|
||||
<h3>Set up variables</h3>
|
||||
<p>
|
||||
In your code, set up variables, including a {@link android.database.Cursor} projection that
|
||||
includes the necessary columns.
|
||||
</p>
|
||||
<p class="note">
|
||||
<strong>Note:</strong> The following code snippets use the method
|
||||
<code>loadContactPhotoThumbnail()</code>, which is defined in the section
|
||||
<a href="#SetURIThumbnail">Set the Contact URI and Thumbnail</a>
|
||||
</p>
|
||||
<p>
|
||||
For example:
|
||||
</p>
|
||||
<pre>
|
||||
public class ContactsFragment extends Fragment implements
|
||||
LoaderManager.LoaderCallbacks<Cursor> {
|
||||
...
|
||||
// Defines a ListView
|
||||
private ListView mListView;
|
||||
// Defines a ContactsAdapter
|
||||
private ContactsAdapter mAdapter;
|
||||
...
|
||||
// Defines a Cursor to contain the retrieved data
|
||||
private Cursor mCursor;
|
||||
/*
|
||||
* Defines a projection based on platform version. This ensures
|
||||
* that you retrieve the correct columns.
|
||||
*/
|
||||
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
|
||||
(Build.VERSION.SDK_INT >=
|
||||
Build.VERSION_CODES.HONEYCOMB) ?
|
||||
Contacts.PHOTO_THUMBNAIL_ID :
|
||||
/*
|
||||
* Although it's not necessary to include the
|
||||
* column twice, this keeps the number of
|
||||
* columns the same regardless of version
|
||||
*/
|
||||
Contacts_ID
|
||||
...
|
||||
};
|
||||
/*
|
||||
* As a shortcut, defines constants for the
|
||||
* column indexes in the Cursor. The index is
|
||||
* 0-based and always matches the column order
|
||||
* in the projection.
|
||||
*/
|
||||
// Column index of the _ID column
|
||||
private int mIdIndex = 0;
|
||||
// Column index of the LOOKUP_KEY column
|
||||
private int mLookupKeyIndex = 1;
|
||||
// Column index of the display name column
|
||||
private int mDisplayNameIndex = 3;
|
||||
/*
|
||||
* Column index of the photo data column.
|
||||
* It's PHOTO_THUMBNAIL_URI for Honeycomb and later,
|
||||
* and _ID for previous versions.
|
||||
*/
|
||||
private int mPhotoDataIndex =
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ?
|
||||
3 :
|
||||
0;
|
||||
...
|
||||
</pre>
|
||||
<h3>Set up the ListView</h3>
|
||||
<p>
|
||||
In {@link android.support.v4.app.Fragment#onCreate Fragment.onCreate()}, instantiate the custom
|
||||
cursor adapter and get a handle to the {@link android.widget.ListView}:
|
||||
</p>
|
||||
<pre>
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
...
|
||||
/*
|
||||
* Instantiates the subclass of
|
||||
* CursorAdapter
|
||||
*/
|
||||
ContactsAdapter mContactsAdapter =
|
||||
new ContactsAdapter(getActivity());
|
||||
/*
|
||||
* Gets a handle to the ListView in the file
|
||||
* contact_list_layout.xml
|
||||
*/
|
||||
mListView = (ListView) findViewById(R.layout.contact_list_layout);
|
||||
...
|
||||
}
|
||||
...
|
||||
</pre>
|
||||
<p>
|
||||
In {@link android.support.v4.app.Fragment#onActivityCreated onActivityCreated()}, bind the
|
||||
<code>ContactsAdapter</code> to the {@link android.widget.ListView}:
|
||||
</p>
|
||||
<pre>
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
...
|
||||
// Sets up the adapter for the ListView
|
||||
mListView.setAdapter(mAdapter);
|
||||
...
|
||||
}
|
||||
...
|
||||
</pre>
|
||||
<p>
|
||||
When you get back a {@link android.database.Cursor} containing the contacts data, usually in
|
||||
{@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()},
|
||||
call {@link android.support.v4.widget.CursorAdapter#swapCursor swapCursor()} to move the
|
||||
{@link android.database.Cursor} data to the {@link android.widget.ListView}. This displays the
|
||||
{@link android.widget.QuickContactBadge} for each entry in the list of contacts:
|
||||
</p>
|
||||
<pre>
|
||||
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
|
||||
// When the loader has completed, swap the cursor into the adapter.
|
||||
mContactsAdapter.swapCursor(cursor);
|
||||
}
|
||||
</pre>
|
||||
<p>
|
||||
When you bind a {@link android.database.Cursor} to a
|
||||
{@link android.widget.ListView} with a {@link android.support.v4.widget.CursorAdapter}
|
||||
(or subclass), and you use a {@link android.support.v4.content.CursorLoader} to load the
|
||||
{@link android.database.Cursor}, always clear references to the {@link android.database.Cursor}
|
||||
in your implementation of
|
||||
{@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoaderReset onLoaderReset()}.
|
||||
For example:
|
||||
</p>
|
||||
<pre>
|
||||
@Override
|
||||
public void onLoaderReset(Loader<Cursor> loader) {
|
||||
// Removes remaining reference to the previous Cursor
|
||||
mContactsAdapter.swapCursor(null);
|
||||
}
|
||||
</pre>
|
||||
97
docs/html/training/contacts-provider/index.jd
Normal file
97
docs/html/training/contacts-provider/index.jd
Normal file
@@ -0,0 +1,97 @@
|
||||
page.title=Accessing Contacts Data
|
||||
|
||||
trainingnavtop=true
|
||||
startpage=true
|
||||
|
||||
@jd:body
|
||||
|
||||
<div id="tb-wrapper">
|
||||
<div id="tb">
|
||||
|
||||
<!-- Required platform, tools, add-ons, devices, knowledge, etc. -->
|
||||
<h2>Dependencies and prerequisites</h2>
|
||||
<ul>
|
||||
<li>Android 2.0 (API Level 5) or higher</li>
|
||||
<li>Experience in using {@link android.content.Intent} objects</li>
|
||||
<li>Experience in using content providers</li>
|
||||
</ul>
|
||||
|
||||
<!-- related 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>
|
||||
</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>
|
||||
The <a href="{@docRoot}guide/topics/providers/contacts-provider.html">Contacts Provider</a> is
|
||||
the central repository of the user's contacts information, including data from contacts apps and
|
||||
social networking apps. In your apps, you can access Contacts Provider information directly by
|
||||
calling {@link android.content.ContentResolver} methods or by sending intents to a contacts app.
|
||||
</p>
|
||||
<p>
|
||||
This class focuses on retrieving lists of contacts, displaying the details for a particular
|
||||
contact, and modifying contacts using intents. The basic techniques described
|
||||
here can be extended to perform more complex tasks. In addition, this class helps you
|
||||
understand the overall structure and operation of the
|
||||
<a href="{@docRoot}guide/topics/providers/contacts-provider.html">Contacts Provider</a>.
|
||||
</p>
|
||||
<h2>Lessons</h2>
|
||||
|
||||
<dl>
|
||||
<dt>
|
||||
<b><a href="retrieve-names.html">Retrieving a List of Contacts</a></b>
|
||||
</dt>
|
||||
<dd>
|
||||
Learn how to retrieve a list of contacts for which the data matches all or part of a search
|
||||
string, using the following techniques:
|
||||
<ul>
|
||||
<li>Match by contact name</li>
|
||||
<li>Match any type of contact data</li>
|
||||
<li>Match a specific type of contact data, such as a phone number</li>
|
||||
</ul>
|
||||
</dd>
|
||||
<dt>
|
||||
<b><a href="retrieve-details.html">Retrieving Details for a Contact</a></b>
|
||||
</dt>
|
||||
<dd>
|
||||
Learn how to retrieve the details for a single contact. A contact's details are data
|
||||
such as phone numbers and email addresses. You can retrieve all details, or you can
|
||||
retrieve details of a specific type, such as all email addresses.
|
||||
</dd>
|
||||
<dt>
|
||||
<b><a href="modify-data.html">Modifying Contacts Using Intents</a></b>
|
||||
</dt>
|
||||
<dd>
|
||||
Learn how to modify a contact by sending an intent to the People app.
|
||||
</dd>
|
||||
<dt>
|
||||
<b>
|
||||
<a href="display-contact-badge.html">Displaying the Quick Contact Badge</a>
|
||||
</b>
|
||||
</dt>
|
||||
<dd>
|
||||
Learn how to display the {@link android.widget.QuickContactBadge} widget. When the user
|
||||
clicks the contact badge widget, a dialog opens that displays the contact's details and
|
||||
action buttons for apps that can handle the details. For example, if the contact has an
|
||||
email address, the dialog displays an action button for the default email app.
|
||||
</dd>
|
||||
</dl>
|
||||
305
docs/html/training/contacts-provider/modify-data.jd
Normal file
305
docs/html/training/contacts-provider/modify-data.jd
Normal file
@@ -0,0 +1,305 @@
|
||||
page.title=Modifying Contacts Using Intents
|
||||
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="#InsertContact">Insert a New Contact Using an Intent</a></li>
|
||||
<li><a href="#EditContact">Edit an Existing Contact Using an Intent</a></li>
|
||||
<li><a href="#InsertEdit">Let Users Choose to Insert or Edit Using an Intent</a>
|
||||
</ol>
|
||||
<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/intents-filters.html">Intents and Intent Filters</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 use an {@link android.content.Intent} to insert a new contact or
|
||||
modify a contact's data. Instead of accessing the Contacts Provider directly, an
|
||||
{@link android.content.Intent} starts the contacts app, which runs the appropriate
|
||||
{@link android.app.Activity}. For the modification actions described in this lesson,
|
||||
if you send extended data in the {@link android.content.Intent} it's entered into the UI of the
|
||||
{@link android.app.Activity} that is started.
|
||||
</p>
|
||||
<p>
|
||||
Using an {@link android.content.Intent} to insert or update a single contact is the preferred
|
||||
way of modifying the Contacts Provider, for the following reasons:
|
||||
</p>
|
||||
<ul>
|
||||
<li>It saves you the time and and effort of developing your own UI and code.</li>
|
||||
<li>
|
||||
It avoids introducing errors caused by modifications that don't follow the
|
||||
Contacts Provider's rules.
|
||||
</li>
|
||||
<li>
|
||||
It reduces the number of permissions you need to request. Your app doesn't need permission
|
||||
to write to the Contacts Provider, because it delegates modifications to the contacts app,
|
||||
which already has that permission.
|
||||
</li>
|
||||
</ul>
|
||||
<h2 id="InsertContact">Insert a New Contact Using an Intent</h2>
|
||||
<p>
|
||||
You often want to allow the user to insert a new contact when your app receives new data. For
|
||||
example, a restaurant review app can allow users to add the restaurant as a contact as they're
|
||||
reviewing it. To do this using an intent, create the intent using as much data as you have
|
||||
available, and then send the intent to the contacts app.
|
||||
</p>
|
||||
<p>
|
||||
Inserting a contact using the contacts app inserts a new <em>raw</em> contact into the Contacts
|
||||
Provider's {@link android.provider.ContactsContract.RawContacts} table. If necessary,
|
||||
the contacts app prompts users for the account type and account to use when creating the raw
|
||||
contact. The contacts app also notifies users if the raw contact already exists. Users then have
|
||||
option of canceling the insertion, in which case no contact is created. To learn
|
||||
more about raw contacts, see the
|
||||
<a href="{@docRoot}guide/topics/providers/contacts-provider.html">Contacts Provider</a>
|
||||
API guide.
|
||||
</p>
|
||||
|
||||
<h3>Create an Intent</h3>
|
||||
<p>
|
||||
To start, create a new {@link android.content.Intent} object with the action
|
||||
{@link android.provider.ContactsContract.Intents.Insert#ACTION Intents.Insert.ACTION}.
|
||||
Set the MIME type to {@link android.provider.ContactsContract.RawContacts#CONTENT_TYPE
|
||||
RawContacts.CONTENT_TYPE}. For example:
|
||||
</p>
|
||||
<pre>
|
||||
...
|
||||
// Creates a new Intent to insert a contact
|
||||
Intent intent = new Intent(Intents.Insert.ACTION);
|
||||
// Sets the MIME type to match the Contacts Provider
|
||||
intent.setType(ContactsContract.RawContacts.CONTENT_TYPE);
|
||||
</pre>
|
||||
<p>
|
||||
If you already have details for the contact, such as a phone number or email address, you can
|
||||
insert them into the intent as extended data. For a key value, use the appropriate constant from
|
||||
{@link android.provider.ContactsContract.Intents.Insert Intents.Insert}. The contacts app
|
||||
displays the data in its insert screen, allowing users to make further edits and additions.
|
||||
</p>
|
||||
<pre>
|
||||
/* Assumes EditText fields in your UI contain an email address
|
||||
* and a phone number.
|
||||
*
|
||||
*/
|
||||
private EditText mEmailAddress = (EditText) findViewById(R.id.email);
|
||||
private EditText mPhoneNumber = (EditText) findViewById(R.id.phone);
|
||||
...
|
||||
/*
|
||||
* Inserts new data into the Intent. This data is passed to the
|
||||
* contacts app's Insert screen
|
||||
*/
|
||||
// Inserts an email address
|
||||
intent.putExtra(Intents.Insert.EMAIL, mEmailAddress.getText())
|
||||
/*
|
||||
* In this example, sets the email type to be a work email.
|
||||
* You can set other email types as necessary.
|
||||
*/
|
||||
.putExtra(Intents.Insert.EMAIL_TYPE, CommonDataKinds.Email.TYPE_WORK)
|
||||
// Inserts a phone number
|
||||
.putExtra(Intents.Insert.PHONE, mPhoneNumber.getText())
|
||||
/*
|
||||
* In this example, sets the phone type to be a work phone.
|
||||
* You can set other phone types as necessary.
|
||||
*/
|
||||
.putExtra(Intents.Insert.PHONE_TYPE, Phone.TYPE_WORK);
|
||||
|
||||
</pre>
|
||||
<p>
|
||||
Once you've created the {@link android.content.Intent}, send it by calling
|
||||
{@link android.support.v4.app.Fragment#startActivity startActivity()}.
|
||||
</p>
|
||||
<pre>
|
||||
/* Sends the Intent
|
||||
*/
|
||||
startActivity(intent);
|
||||
</pre>
|
||||
<p>
|
||||
This call opens a screen in the contacts app that allows users to enter a new contact. The
|
||||
account type and account name for the contact is listed at the top of the screen. Once users
|
||||
enter the data and click <i>Done</i>, the contacts app's contact list appears. Users return to
|
||||
your app by clicking <i>Back</i>.
|
||||
</p>
|
||||
<h2 id="EditContact">Edit an Existing Contact Using an Intent</h2>
|
||||
<p>
|
||||
Editing an existing contact using an {@link android.content.Intent} is useful if the user
|
||||
has already chosen a contact of interest. For example, an app that finds contacts that have
|
||||
postal addresses but lack a postal code could give users the option of looking up the code and
|
||||
then adding it to the contact.
|
||||
</p>
|
||||
<p>
|
||||
To edit an existing contact using an intent, use a procedure similar to
|
||||
inserting a contact. Create an intent as described in the section
|
||||
<a href="#InsertContact">Insert a New Contact Using an Intent</a>, but add the contact's
|
||||
{@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI
|
||||
Contacts.CONTENT_LOOKUP_URI} and the MIME type
|
||||
{@link android.provider.ContactsContract.Contacts#CONTENT_ITEM_TYPE
|
||||
Contacts.CONTENT_ITEM_TYPE} to the intent. If you want to edit the contact with details you
|
||||
already have, you can put them in the intent's extended data. Notice that some
|
||||
name columns can't be edited using an intent; these columns are listed in the summary
|
||||
section of the API reference for the class {@link android.provider.ContactsContract.Contacts}
|
||||
under the heading "Update".
|
||||
</p>
|
||||
<p>
|
||||
Finally, send the intent. In response, the contacts app displays an edit screen. When the user
|
||||
finishes editing and saves the edits, the contacts app displays a contact list. When the user
|
||||
clicks <i>Back</i>, your app is displayed.
|
||||
</p>
|
||||
<div class="sidebox-wrapper">
|
||||
<div class="sidebox">
|
||||
<h2>Contacts Lookup Key</h2>
|
||||
<p>
|
||||
A contact's {@link android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} value is
|
||||
the identifier that you should use to retrieve a contact. It remains constant,
|
||||
even if the provider changes the contact's row ID to handle internal operations.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<h3>Create the Intent</h3>
|
||||
<p>
|
||||
To edit a contact, call {@link android.content.Intent#Intent Intent(action)} to
|
||||
create an intent with the action {@link android.content.Intent#ACTION_EDIT}. Call
|
||||
{@link android.content.Intent#setDataAndType setDataAndType()} to set the data value for the
|
||||
intent to the contact's {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI
|
||||
Contacts.CONTENT_LOOKUP_URI} and the MIME type to
|
||||
{@link android.provider.ContactsContract.Contacts#CONTENT_ITEM_TYPE
|
||||
Contacts.CONTENT_ITEM_TYPE} MIME type; because a call to
|
||||
{@link android.content.Intent#setType setType()} overwrites the current data value for the
|
||||
{@link android.content.Intent}, you must set the data and the MIME type at the same time.
|
||||
</p>
|
||||
<p>
|
||||
To get a contact's {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI
|
||||
Contacts.CONTENT_LOOKUP_URI}, call
|
||||
{@link android.provider.ContactsContract.Contacts#getLookupUri
|
||||
Contacts.getLookupUri(id, lookupkey)} with the contact's
|
||||
{@link android.provider.ContactsContract.Contacts#_ID Contacts._ID} and
|
||||
{@link android.provider.ContactsContract.Contacts#LOOKUP_KEY Contacts.LOOKUP_KEY} values as
|
||||
arguments.
|
||||
</p>
|
||||
<p>
|
||||
The following snippet shows you how to create an intent:
|
||||
</p>
|
||||
<pre>
|
||||
// The Cursor that contains the Contact row
|
||||
public Cursor mCursor;
|
||||
// The index of the lookup key column in the cursor
|
||||
public int mLookupKeyIndex;
|
||||
// The index of the contact's _ID value
|
||||
public int mIdIndex;
|
||||
// The lookup key from the Cursor
|
||||
public String mCurrentLookupKey;
|
||||
// The _ID value from the Cursor
|
||||
public long mCurrentId;
|
||||
// A content URI pointing to the contact
|
||||
Uri mSelectedContactUri;
|
||||
...
|
||||
/*
|
||||
* Once the user has selected a contact to edit,
|
||||
* this gets the contact's lookup key and _ID values from the
|
||||
* cursor and creates the necessary URI.
|
||||
*/
|
||||
// Gets the lookup key column index
|
||||
mLookupKeyIndex = mCursor.getColumnIndex(Contacts.LOOKUP_KEY);
|
||||
// Gets the lookup key value
|
||||
mCurrentLookupKey = mCursor.getString(mLookupKeyIndex);
|
||||
// Gets the _ID column index
|
||||
mIdIndex = mCursor.getColumnIndex(Contacts._ID);
|
||||
mCurrentId = mCursor.getLong(mIdIndex);
|
||||
mSelectedContactUri =
|
||||
Contacts.getLookupUri(mCurrentId, mCurrentLookupKey);
|
||||
...
|
||||
// Creates a new Intent to edit a contact
|
||||
Intent editIntent = new Intent(Intent.ACTION_EDIT);
|
||||
/*
|
||||
* Sets the contact URI to edit, and the data type that the
|
||||
* Intent must match
|
||||
*/
|
||||
editIntent.setDataAndType(mSelectedContactUri,Contacts.CONTENT_ITEM_TYPE);
|
||||
</pre>
|
||||
<h3>Add the navigation flag</h3>
|
||||
<p>
|
||||
In Android 4.0 (API version 14) and later, a problem in the contacts app causes incorrect
|
||||
navigation. When your app sends an edit intent to the contacts app, and users edit and save a
|
||||
contact, when they click <i>Back</i> they see the contacts list screen. To navigate back to
|
||||
your app, they have to click <i>Recents</i> and choose your app.
|
||||
</p>
|
||||
<p>
|
||||
To work around this problem in Android 4.0.3 (API version 15) and later, add the extended
|
||||
data key {@code finishActivityOnSaveCompleted} to the intent, with a value of {@code true}.
|
||||
Android versions prior to Android 4.0 accept this key, but it has no effect. To set the
|
||||
extended data, do the following:
|
||||
</p>
|
||||
<pre>
|
||||
// Sets the special extended data for navigation
|
||||
editIntent.putExtra("finishActivityOnSaveCompleted", true);
|
||||
</pre>
|
||||
<h3>Add other extended data</h3>
|
||||
<p>
|
||||
To add additional extended data to the {@link android.content.Intent}, call
|
||||
{@link android.content.Intent#putExtra putExtra()} as desired.
|
||||
You can add extended data for common contact fields by using the key values specified in
|
||||
{@link android.provider.ContactsContract.Intents.Insert Intents.Insert}. Remember that some
|
||||
columns in the {@link android.provider.ContactsContract.Contacts} table can't be modified.
|
||||
These columns are listed in the summary section of the API reference for the class
|
||||
{@link android.provider.ContactsContract.Contacts} under the heading "Update".
|
||||
</p>
|
||||
|
||||
<h3>Send the Intent</h3>
|
||||
<p>
|
||||
Finally, send the intent you've constructed. For example:
|
||||
</p>
|
||||
<pre>
|
||||
// Sends the Intent
|
||||
startActivity(editIntent);
|
||||
</pre>
|
||||
<h2 id="InsertEdit">Let Users Choose to Insert or Edit Using an Intent</h2>
|
||||
<p>
|
||||
You can allow users to choose whether to insert a contact or edit an existing one by sending
|
||||
an {@link android.content.Intent} with the action
|
||||
{@link android.content.Intent#ACTION_INSERT_OR_EDIT}. For example, an email client app could
|
||||
allow users to add an incoming email address to a new contact, or add it as an additional
|
||||
address for an existing contact. Set the MIME type for this intent to
|
||||
{@link android.provider.ContactsContract.Contacts#CONTENT_ITEM_TYPE Contacts.CONTENT_ITEM_TYPE},
|
||||
but don't set the data URI.
|
||||
</p>
|
||||
<p>
|
||||
When you send this intent, the contacts app displays a list of contacts.
|
||||
Users can either insert a new contact or pick an existing contact and edit it.
|
||||
Any extended data fields you add to the intent populates the screen that appears. You can use
|
||||
any of the key values specified in {@link android.provider.ContactsContract.Intents.Insert
|
||||
Intents.Insert}. The following code snippet shows how to construct and send the intent:
|
||||
</p>
|
||||
<pre>
|
||||
// Creates a new Intent to insert or edit a contact
|
||||
Intent intentInsertEdit = new Intent(Intent.ACTION_INSERT_OR_EDIT);
|
||||
// Sets the MIME type
|
||||
intentInsertEdit.setType(Contacts.CONTENT_ITEM_TYPE);
|
||||
// Add code here to insert extended data, if desired
|
||||
...
|
||||
// Sends the Intent with an request ID
|
||||
startActivity(intentInsertEdit);
|
||||
</pre>
|
||||
378
docs/html/training/contacts-provider/retrieve-details.jd
Normal file
378
docs/html/training/contacts-provider/retrieve-details.jd
Normal file
@@ -0,0 +1,378 @@
|
||||
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>
|
||||
815
docs/html/training/contacts-provider/retrieve-names.jd
Normal file
815
docs/html/training/contacts-provider/retrieve-names.jd
Normal file
@@ -0,0 +1,815 @@
|
||||
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 can define
|
||||
the main layout file <code>res/layout/contacts_list_view.xml</code> that contains 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.contacts_list_layout, 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>
|
||||
@@ -480,7 +480,37 @@
|
||||
</a>
|
||||
</div>
|
||||
<ul>
|
||||
|
||||
<li class="nav-section">
|
||||
<div class="nav-section-header">
|
||||
<a href="<?cs var:toroot ?>training/contacts-provider/index.html"
|
||||
description=
|
||||
"How to use Android's central address book, the Contacts Provider, to
|
||||
display contacts and their details and modify contact information.">
|
||||
Accessing Contacts Data</a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="<?cs var:toroot ?>training/contacts-provider/retrieve-names.html">
|
||||
Retrieving a List of Contacts
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="<?cs var:toroot ?>training/contacts-provider/retrieve-details.html">
|
||||
Retrieving Details for a Contact
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="<?cs var:toroot ?>training/contacts-provider/modify-data.html">
|
||||
Modifying Contacts Using Intents
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="<?cs var:toroot ?>training/contacts-provider/display-contact-badge.html">
|
||||
Displaying the Quick Contact Badge
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="nav-section">
|
||||
<div class="nav-section-header">
|
||||
<a href="<?cs var:toroot ?>training/id-auth/index.html"
|
||||
@@ -875,7 +905,7 @@
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li> <!-- end of User Input -->
|
||||
</li> <!-- end of User Input -->
|
||||
|
||||
<li class="nav-section">
|
||||
<div class="nav-section-header">
|
||||
|
||||
Reference in New Issue
Block a user