1page.title=Retrieving a List of Contacts 2 3trainingnavtop=true 4@jd:body 5 6<div id="tb-wrapper"> 7<div id="tb"> 8 9<!-- table of contents --> 10<h2>This lesson teaches you to</h2> 11<ol> 12 <li><a href="#Permissions">Request Permission to Read the Provider</a> 13 <li><a href="#NameMatch">Match a Contact by Name and List the Results</a></li> 14 <li><a href="#TypeMatch">Match a Contact By a Specific Type of Data</a></li> 15 <li><a href="#GeneralMatch">Match a Contact By Any Type of Data</a></li> 16</ol> 17 18<!-- other docs (NOT javadocs) --> 19<h2>You should also read</h2> 20<ul> 21 <li> 22 <a href="{@docRoot}guide/topics/providers/content-provider-basics.html"> 23 Content Provider Basics</a> 24 </li> 25 <li> 26 <a href="{@docRoot}guide/topics/providers/contacts-provider.html"> 27 Contacts Provider</a> 28 </li> 29 <li> 30 <a href="{@docRoot}guide/components/loaders.html">Loaders</a> 31 </li> 32 <li> 33 <a href="{@docRoot}guide/topics/search/search-dialog.html">Creating a Search Interface</a> 34 </li> 35</ul> 36 37<h2>Try it out</h2> 38 39<div class="download-box"> 40 <a href="http://developer.android.com/shareables/training/ContactsList.zip" class="button"> 41 Download the sample 42 </a> 43 <p class="filename">ContactsList.zip</p> 44</div> 45 46</div> 47</div> 48<p> 49 This lesson shows you how to retrieve a list of contacts whose data matches all or part of a 50 search string, using the following techniques: 51</p> 52<dl> 53 <dt>Match contact names</dt> 54 <dd> 55 Retrieve a list of contacts by matching the search string to all or part of the contact 56 name data. The Contacts Provider allows multiple instances of the same name, so this 57 technique can return a list of matches. 58 </dd> 59 <dt>Match a specific type of data, such as a phone number</dt> 60 <dd> 61 Retrieve a list of contacts by matching the search string to a particular type of detail 62 data such as an email address. For example, this technique allows you to list all of the 63 contacts whose email address matches the search string. 64 </dd> 65 <dt>Match any type of data</dt> 66 <dd> 67 Retrieve a list of contacts by matching the search string to any type of detail data, 68 including name, phone number, street address, email address, and so forth. For example, 69 this technique allows you to accept any type of data for a search string and then list the 70 contacts for which the data matches the string. 71 </dd> 72</dl> 73<p class="note"> 74 <strong>Note:</strong> All the examples in this lesson use a 75 {@link android.support.v4.content.CursorLoader} to retrieve data from the Contacts 76 Provider. A {@link android.support.v4.content.CursorLoader} runs its query on a 77 thread that's separate from the UI thread. This ensures that the query doesn't slow down UI 78 response times and cause a poor user experience. For more information, see the Android 79 training class <a href="{@docRoot}training/load-data-background/index.html"> 80 Loading Data in the Background</a>. 81</p> 82<h2 id="Permissions">Request Permission to Read the Provider</h2> 83<p> 84 To do any type of search of the Contacts Provider, your app must have 85 {@link android.Manifest.permission#READ_CONTACTS READ_CONTACTS} permission. 86 To request this, add this 87<code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html"><uses-permission></a></code> 88 element to your manifest file as a child element of 89<code><a href="{@docRoot}guide/topics/manifest/manifest-element.html"><manifest></a></code>: 90</p> 91<pre> 92 <uses-permission android:name="android.permission.READ_CONTACTS" /> 93</pre> 94<h2 id="NameMatch">Match a Contact by Name and List the Results</h2> 95<p> 96 This technique tries to match a search string to the name of a contact or contacts in the 97 Contact Provider's {@link android.provider.ContactsContract.Contacts} table. You usually want 98 to display the results in a {@link android.widget.ListView}, to allow the user to choose among 99 the matched contacts. 100</p> 101<h3 id="DefineListView">Define ListView and item layouts</h3> 102<p> 103 To display the search results in a {@link android.widget.ListView}, you need a main layout file 104 that defines the entire UI including the {@link android.widget.ListView}, and an item layout 105 file that defines one line of the {@link android.widget.ListView}. For example, you can define 106 the main layout file <code>res/layout/contacts_list_view.xml</code> that contains the 107 following XML: 108</p> 109<pre> 110<?xml version="1.0" encoding="utf-8"?> 111<ListView xmlns:android="http://schemas.android.com/apk/res/android" 112 android:id="@android:id/list" 113 android:layout_width="match_parent" 114 android:layout_height="match_parent"/> 115</pre> 116<p> 117 This XML uses the built-in Android {@link android.widget.ListView} widget 118 {@link android.R.id#list android:id/list}. 119</p> 120<p> 121 Define the item layout file <code>contacts_list_item.xml</code> with the following XML: 122</p> 123<pre> 124<?xml version="1.0" encoding="utf-8"?> 125<TextView xmlns:android="http://schemas.android.com/apk/res/android" 126 android:id="@android:id/text1" 127 android:layout_width="match_parent" 128 android:layout_height="wrap_content" 129 android:clickable="true"/> 130</pre> 131<p> 132 This XML uses the built-in Android {@link android.widget.TextView} widget 133 {@link android.R.id#text1 android:text1}. 134</p> 135<p class="note"> 136 <strong>Note:</strong> This lesson doesn't describe the UI for getting a search string from the 137 user, because you may want to get the string indirectly. For example, you can give the user 138 an option to search for contacts whose name matches a string in an incoming text message. 139</p> 140<p> 141 The two layout files you've written define a user interface that shows a 142 {@link android.widget.ListView}. The next step is to write code that uses this UI to display a 143 list of contacts. 144</p> 145<h3 id="Fragment">Define a Fragment that displays the list of contacts</h3> 146<p> 147 To display the list of contacts, start by defining a {@link android.support.v4.app.Fragment} 148 that's loaded by an {@link android.app.Activity}. Using a 149 {@link android.support.v4.app.Fragment} is a more flexible technique, because you can use 150 one {@link android.support.v4.app.Fragment} to display the list and a second 151 {@link android.support.v4.app.Fragment} to display the details for a contact that the user 152 chooses from the list. Using this approach, you can combine one of the techniques presented in 153 this lesson with one from the lesson <a href="retrieve-details.html"> 154 Retrieving Details for a Contact</a>. 155</p> 156<p> 157 To learn how to use one or more {@link android.support.v4.app.Fragment} objects from an 158 an {@link android.app.Activity}, read the training class 159 <a href="{@docRoot}training/basics/fragments/index.html"> 160 Building a Dynamic UI with Fragments</a>. 161</p> 162<p> 163 To help you write queries against the Contacts Provider, the Android framework provides a 164 contracts class called {@link android.provider.ContactsContract}, which defines useful 165 constants and methods for accessing the provider. When you use this class, you don't have to 166 define your own constants for content URIs, table names, or columns. To use this class, 167 include the following statement: 168</p> 169<pre> 170import android.provider.ContactsContract; 171</pre> 172<p> 173 Since the code uses a {@link android.support.v4.content.CursorLoader} to retrieve data 174 from the provider, you must specify that it implements the loader interface 175 {@link android.support.v4.app.LoaderManager.LoaderCallbacks}. Also, to help detect which contact 176 the user selects from the list of search results, implement the adapter interface 177 {@link android.widget.AdapterView.OnItemClickListener}. For example: 178</p> 179<pre> 180... 181import android.support.v4.app.Fragment; 182import android.support.v4.app.LoaderManager.LoaderCallbacks; 183import android.widget.AdapterView; 184... 185public class ContactsFragment extends Fragment implements 186 LoaderManager.LoaderCallbacks<Cursor>, 187 AdapterView.OnItemClickListener { 188</pre> 189<h3 id="DefineVariables">Define global variables</h3> 190<p> 191 Define global variables that are used in other parts of the code: 192</p> 193<pre> 194 ... 195 /* 196 * Defines an array that contains column names to move from 197 * the Cursor to the ListView. 198 */ 199 @SuppressLint("InlinedApi") 200 private final static String[] FROM_COLUMNS = { 201 Build.VERSION.SDK_INT 202 >= Build.VERSION_CODES.HONEYCOMB ? 203 Contacts.DISPLAY_NAME_PRIMARY : 204 Contacts.DISPLAY_NAME 205 }; 206 /* 207 * Defines an array that contains resource ids for the layout views 208 * that get the Cursor column contents. The id is pre-defined in 209 * the Android framework, so it is prefaced with "android.R.id" 210 */ 211 private final static int[] TO_IDS = { 212 android.R.id.text1 213 }; 214 // Define global mutable variables 215 // Define a ListView object 216 ListView mContactsList; 217 // Define variables for the contact the user selects 218 // The contact's _ID value 219 long mContactId; 220 // The contact's LOOKUP_KEY 221 String mContactKey; 222 // A content URI for the selected contact 223 Uri mContactUri; 224 // An adapter that binds the result Cursor to the ListView 225 private SimpleCursorAdapter mCursorAdapter; 226 ... 227</pre> 228<p class="note"> 229 <strong>Note:</strong> Since 230 {@link android.provider.ContactsContract.Contacts#DISPLAY_NAME_PRIMARY 231 Contacts.DISPLAY_NAME_PRIMARY} requires Android 3.0 (API version 11) or later, setting your 232 app's <code>minSdkVersion</code> to 10 or below generates an Android Lint warning in 233 Eclipse with ADK. To turn off this warning, add the annotation 234 <code>@SuppressLint("InlinedApi")</code> before the definition of <code>FROM_COLUMNS</code>. 235</p> 236<h3 id="InitializeFragment">Initialize the Fragment</h3> 237<p> 238 239 Initialize the {@link android.support.v4.app.Fragment}. Add the empty, public constructor 240 required by the Android system, and inflate the {@link android.support.v4.app.Fragment} object's 241 UI in the callback method {@link android.support.v4.app.Fragment#onCreateView onCreateView()}. 242 For example: 243</p> 244<pre> 245 // Empty public constructor, required by the system 246 public ContactsFragment() {} 247 248 // A UI Fragment must inflate its View 249 @Override 250 public View onCreateView(LayoutInflater inflater, ViewGroup container, 251 Bundle savedInstanceState) { 252 // Inflate the fragment layout 253 return inflater.inflate(R.layout.contacts_list_layout, container, false); 254 } 255</pre> 256<h3 id="DefineAdapter">Set up the CursorAdapter for the ListView</h3> 257<p> 258 Set up the {@link android.support.v4.widget.SimpleCursorAdapter} that binds the results of the 259 search to the {@link android.widget.ListView}. To get the {@link android.widget.ListView} object 260 that displays the contacts, you need to call {@link android.app.Activity#findViewById 261 Activity.findViewById()} using the parent activity of the 262 {@link android.support.v4.app.Fragment}. Use the {@link android.content.Context} of the 263 parent activity when you call {@link android.widget.ListView#setAdapter setAdapter()}. 264 For example: 265</p> 266<pre> 267 public void onActivityCreated(Bundle savedInstanceState) { 268 super.onActivityCreated(savedInstanceState); 269 ... 270 // Gets the ListView from the View list of the parent activity 271 mContactsList = (ListView) getActivity().findViewById(R.layout.contact_list_view); 272 // Gets a CursorAdapter 273 mCursorAdapter = new SimpleCursorAdapter( 274 getActivity(), 275 R.layout.contact_list_item, 276 null, 277 FROM_COLUMNS, TO_IDS, 278 0); 279 // Sets the adapter for the ListView 280 mContactsList.setAdapter(mCursorAdapter); 281 } 282</pre> 283<h3 id="SetListener">Set the selected contact listener</h3> 284<p> 285 When you display the results of a search, you usually want to allow the user to select a 286 single contact for further processing. For example, when the user clicks a contact you can 287 display the contact's address on a map. To provide this feature, you first defined the current 288 {@link android.support.v4.app.Fragment} as the click listener by specifying that the class 289 implements {@link android.widget.AdapterView.OnItemClickListener}, as shown in the section 290 <a href="#Fragment">Define a Fragment that displays the list of contacts</a>. 291</p> 292<p> 293 To continue setting up the listener, bind it to the {@link android.widget.ListView} by 294 calling the method {@link android.widget.ListView#setOnItemClickListener 295 setOnItemClickListener()} in {@link android.support.v4.app.Fragment#onActivityCreated 296 onActivityCreated()}. For example: 297</p> 298<pre> 299 public void onActivityCreated(Bundle savedInstanceState) { 300 ... 301 // Set the item click listener to be the current fragment. 302 mContactsList.setOnItemClickListener(this); 303 ... 304 } 305</pre> 306<p> 307 Since you specified that the current {@link android.support.v4.app.Fragment} is the 308 {@link android.widget.AdapterView.OnItemClickListener OnItemClickListener} for the 309 {@link android.widget.ListView}, you now need to implement its required method 310 {@link android.widget.AdapterView.OnItemClickListener#onItemClick onItemClick()}, which 311 handles the click event. This is described in a succeeding section. 312</p> 313<h3 id="DefineProjection">Define a projection</h3> 314<p> 315 Define a constant that contains the columns you want to return from your query. Each item in 316 the {@link android.widget.ListView} displays the contact's display name, 317 which contains the main form of the contact's name. In Android 3.0 (API version 11) and later, 318 the name of this column is 319 {@link android.provider.ContactsContract.Contacts#DISPLAY_NAME_PRIMARY 320 Contacts.DISPLAY_NAME_PRIMARY}; in versions previous to that, its name is 321 {@link android.provider.ContactsContract.Contacts#DISPLAY_NAME Contacts.DISPLAY_NAME}. 322</p> 323<p> 324 The column {@link android.provider.ContactsContract.Contacts#_ID Contacts._ID} is used by the 325 {@link android.support.v4.widget.SimpleCursorAdapter} binding process. 326 {@link android.provider.ContactsContract.Contacts#_ID Contacts._ID} and 327 {@link android.provider.ContactsContract.Contacts#LOOKUP_KEY} are used together to 328 construct a content URI for the contact the user selects. 329</p> 330<pre> 331... 332@SuppressLint("InlinedApi") 333private static final String[] PROJECTION = 334 { 335 Contacts._ID, 336 Contacts.LOOKUP_KEY, 337 Build.VERSION.SDK_INT 338 >= Build.VERSION_CODES.HONEYCOMB ? 339 Contacts.DISPLAY_NAME_PRIMARY : 340 Contacts.DISPLAY_NAME 341 342 }; 343</pre> 344<h3 id="DefineConstants">Define constants for the Cursor column indexes</h3> 345<p> 346 To get data from an individual column in a {@link android.database.Cursor}, you need 347 the column's index within the {@link android.database.Cursor}. You can define constants 348 for the indexes of the {@link android.database.Cursor} columns, because the indexes are 349 the same as the order of the column names in your projection. For example: 350</p> 351<pre> 352// The column index for the _ID column 353private static final int CONTACT_ID_INDEX = 0; 354// The column index for the LOOKUP_KEY column 355private static final int LOOKUP_KEY_INDEX = 1; 356</pre> 357<h3 id="SelectionCriteria">Specify the selection criteria</h3> 358<p> 359 To specify the data you want, create a combination of text expressions and variables 360 that tell the provider the data columns to search and the values to find. 361</p> 362<p> 363 For the text expression, define a constant that lists the search columns. Although this 364 expression can contain values as well, the preferred practice is to represent the values with 365 a "?" placeholder. During retrieval, the placeholder is replaced with values from an 366 array. Using "?" as a placeholder ensures that the search specification is generated by binding 367 rather than by SQL compilation. This practice eliminates the possibility of malicious SQL 368 injection. For example: 369</p> 370<pre> 371 // Defines the text expression 372 @SuppressLint("InlinedApi") 373 private static final String SELECTION = 374 Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ? 375 Contacts.DISPLAY_NAME_PRIMARY + " LIKE ?" : 376 Contacts.DISPLAY_NAME + " LIKE ?"; 377 // Defines a variable for the search string 378 private String mSearchString; 379 // Defines the array to hold values that replace the ? 380 private String[] mSelectionArgs = { mSearchString }; 381</pre> 382<h3 id="OnItemClick">Define the onItemClick() method</h3> 383<p> 384 In a previous section, you set the item click listener for the {@link android.widget.ListView}. 385 Now implement the action for the listener by defining the method 386 {@link android.widget.AdapterView.OnItemClickListener#onItemClick 387 AdapterView.OnItemClickListener.onItemClick()}: 388</p> 389<pre> 390 @Override 391 public void onItemClick( 392 AdapterView<?> parent, View item, int position, long rowID) { 393 // Get the Cursor 394 Cursor cursor = parent.getAdapter().getCursor(); 395 // Move to the selected contact 396 cursor.moveToPosition(position); 397 // Get the _ID value 398 mContactId = getLong(CONTACT_ID_INDEX); 399 // Get the selected LOOKUP KEY 400 mContactKey = getString(CONTACT_KEY_INDEX); 401 // Create the contact's content Uri 402 mContactUri = Contacts.getLookupUri(mContactId, mContactKey); 403 /* 404 * You can use mContactUri as the content URI for retrieving 405 * the details for a contact. 406 */ 407 } 408</pre> 409<h3 id="InitializeLoader">Initialize the loader</h3> 410<p> 411 Since you're using a {@link android.support.v4.content.CursorLoader} to retrieve data, 412 you must initialize the background thread and other variables that control asynchronous 413 retrieval. Do the initialization in 414 {@link android.support.v4.app.Fragment#onActivityCreated onActivityCreated()}, which 415 is invoked immediately before the {@link android.support.v4.app.Fragment} UI appears, as 416 shown in the following example: 417</p> 418<pre> 419public class ContactsFragment extends Fragment implements 420 LoaderManager.LoaderCallbacks<Cursor> { 421 ... 422 // Called just before the Fragment displays its UI 423 @Override 424 public void onActivityCreated(Bundle savedInstanceState) { 425 // Always call the super method first 426 super.onActivityCreated(savedInstanceState); 427 ... 428 // Initializes the loader 429 getLoaderManager().initLoader(0, null, this); 430</pre> 431<h3 id="OnCreateLoader">Implement onCreateLoader()</h3> 432<p> 433 Implement the method 434 {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}, 435 which is called by the loader framework immediately after you call 436 {@link android.support.v4.app.LoaderManager#initLoader initLoader()}. 437<p> 438 In {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}, 439 set up the search string pattern. To make a string into a pattern, insert "%" 440 (percent) characters to represent a sequence of zero or more characters, or "_" (underscore) 441 characters to represent a single character, or both. For example, the pattern "%Jefferson%" 442 would match both "Thomas Jefferson" and "Jefferson Davis". 443</p> 444<p> 445 Return a new {@link android.support.v4.content.CursorLoader} from the method. For the content 446 URI, use {@link android.provider.ContactsContract.Contacts#CONTENT_URI Contacts.CONTENT_URI}. 447 This URI refers to the entire table, as shown in the following example: 448</p> 449<pre> 450 ... 451 @Override 452 public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) { 453 /* 454 * Makes search string into pattern and 455 * stores it in the selection array 456 */ 457 mSelectionArgs[0] = "%" + mSearchString + "%"; 458 // Starts the query 459 return new CursorLoader( 460 getActivity(), 461 Contacts.CONTENT_URI, 462 PROJECTION, 463 SELECTION, 464 mSelectionArgs, 465 null 466 ); 467 } 468</pre> 469<h3 id="FinishedReset">Implement onLoadFinished() and onLoaderReset()</h3> 470<p> 471 Implement the 472 {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()} 473 method. The loader framework calls 474 {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()} 475 when the Contacts Provider returns the results of the query. In this method, put the 476 result {@link android.database.Cursor} in the 477 {@link android.support.v4.widget.SimpleCursorAdapter}. This automatically updates the 478 {@link android.widget.ListView} with the search results: 479</p> 480<pre> 481 public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { 482 // Put the result Cursor in the adapter for the ListView 483 mCursorAdapter.swapCursor(cursor); 484 } 485</pre> 486<p> 487 The method {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoaderReset 488 onLoaderReset()} is invoked when the loader framework detects that the 489 result {@link android.database.Cursor} contains stale data. Delete the 490 {@link android.support.v4.widget.SimpleCursorAdapter} reference to the existing 491 {@link android.database.Cursor}. If you don't, the loader framework will not 492 recycle the {@link android.database.Cursor}, which causes a memory leak. For example: 493</p> 494<pre> 495 @Override 496 public void onLoaderReset(Loader<Cursor> loader) { 497 // Delete the reference to the existing Cursor 498 mCursorAdapter.swapCursor(null); 499 500 } 501</pre> 502 503<p> 504 You now have the key pieces of an app that matches a search string to contact names and returns 505 the result in a {@link android.widget.ListView}. The user can click a contact name to select it. 506 This triggers a listener, in which you can work further with the contact's data. For example, 507 you can retrieve the contact's details. To learn how to do this, continue with the next 508 lesson, <a href="#retrieve-details.html">Retrieving Details for a Contact</a>. 509</p> 510<p> 511 To learn more about search user interfaces, read the API guide 512 <a href="{@docRoot}guide/topics/search/search-dialog.html">Creating a Search Interface</a>. 513</p> 514<p> 515 The remaining sections in this lesson demonstrate other ways of finding contacts in the 516 Contacts Provider. 517</p> 518<h2 id="TypeMatch">Match a Contact By a Specific Type of Data</h2> 519<p> 520 This technique allows you to specify the type of data you want to match. Retrieving 521 by name is a specific example of this type of query, but you can also do it for any of the types 522 of detail data associated with a contact. For example, you can retrieve contacts that have a 523 specific postal code; in this case, the search string has to match data stored in a postal code 524 row. 525</p> 526<p> 527 To implement this type of retrieval, first implement the following code, as listed in 528 previous sections: 529</p> 530<ul> 531 <li> 532 Request Permission to Read the Provider. 533 </li> 534 <li> 535 Define ListView and item layouts. 536 </li> 537 <li> 538 Define a Fragment that displays the list of contacts. 539 </li> 540 <li> 541 Define global variables. 542 </li> 543 <li> 544 Initialize the Fragment. 545 </li> 546 <li> 547 Set up the CursorAdapter for the ListView. 548 </li> 549 <li> 550 Set the selected contact listener. 551 </li> 552 <li> 553 Define constants for the Cursor column indexes. 554 <p> 555 Although you're retrieving data from a different table, the order of the columns in 556 the projection is the same, so you can use the same indexes for the Cursor. 557 </p> 558 </li> 559 <li> 560 Define the onItemClick() method. 561 </li> 562 <li> 563 Initialize the loader. 564 </li> 565 <li> 566 567 Implement onLoadFinished() and onLoaderReset(). 568 </li> 569</ul> 570<p> 571 The following steps show you the additional code you need to match a search string to 572 a particular type of detail data and display the results. 573</p> 574<h3>Choose the data type and table</h3> 575<p> 576 To search for a particular type of detail data, you have to know the custom MIME type value 577 for the data type. Each data type has a unique MIME type 578 value defined by a constant <code>CONTENT_ITEM_TYPE</code> in the subclass of 579 {@link android.provider.ContactsContract.CommonDataKinds} associated with the data type. 580 The subclasses have names that indicate their data type; for example, the subclass for email 581 data is {@link android.provider.ContactsContract.CommonDataKinds.Email}, and the custom MIME 582 type for email data is defined by the constant 583 {@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_ITEM_TYPE 584 Email.CONTENT_ITEM_TYPE}. 585</p> 586<p> 587 Use the {@link android.provider.ContactsContract.Data} table for your search. All of the 588 constants you need for your projection, selection clause, and sort order are defined in or 589 inherited by this table. 590</p> 591<h3 id="SpecificProjection">Define a projection</h3> 592<p> 593 To define a projection, choose one or more of the columns defined in 594 {@link android.provider.ContactsContract.Data} or the classes from which it inherits. The 595 Contacts Provider does an implicit join between {@link android.provider.ContactsContract.Data} 596 and other tables before it returns rows. For example: 597</p> 598<pre> 599 @SuppressLint("InlinedApi") 600 private static final String[] PROJECTION = 601 { 602 /* 603 * The detail data row ID. To make a ListView work, 604 * this column is required. 605 */ 606 Data._ID, 607 // The primary display name 608 Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ? 609 Data.DISPLAY_NAME_PRIMARY : 610 Data.DISPLAY_NAME, 611 // The contact's _ID, to construct a content URI 612 Data.CONTACT_ID 613 // The contact's LOOKUP_KEY, to construct a content URI 614 Data.LOOKUP_KEY (a permanent link to the contact 615 }; 616</pre> 617<h3 id="SpecificCriteria">Define search criteria</h3> 618<p> 619 To search for a string within a particular type of data, construct a selection clause from 620 the following: 621</p> 622<ul> 623 <li> 624 The name of the column that contains your search string. This name varies by data type, 625 so you need to find the subclass of 626 {@link android.provider.ContactsContract.CommonDataKinds} that corresponds to the data type 627 and then choose the column name from that subclass. For example, to search for 628 email addresses, use the column 629 {@link android.provider.ContactsContract.CommonDataKinds.Email#ADDRESS Email.ADDRESS}. 630 </li> 631 <li> 632 The search string itself, represented as the "?" character in the selection clause. 633 </li> 634 <li> 635 The name of the column that contains the custom MIME type value. This name is always 636 {@link android.provider.ContactsContract.Data#MIMETYPE Data.MIMETYPE}. 637 </li> 638 <li> 639 The custom MIME type value for the data type. As described previously, this is the constant 640 <code>CONTENT_ITEM_TYPE</code> in the 641 {@link android.provider.ContactsContract.CommonDataKinds} subclass. For example, the MIME 642 type value for email data is 643 {@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_ITEM_TYPE 644 Email.CONTENT_ITEM_TYPE}. Enclose the value in single quotes by concatenating a 645 "<code>'</code>" (single quote) character to the start and end of the constant; otherwise, 646 the provider interprets the value as a variable name rather than as a string value. 647 You don't need to use a placeholder for this value, because you're using a constant 648 rather than a user-supplied value. 649 </li> 650</ul> 651<p> 652 For example: 653</p> 654<pre> 655 /* 656 * Constructs search criteria from the search string 657 * and email MIME type 658 */ 659 private static final String SELECTION = 660 /* 661 * Searches for an email address 662 * that matches the search string 663 */ 664 Email.ADDRESS + " LIKE ? " + "AND " + 665 /* 666 * Searches for a MIME type that matches 667 * the value of the constant 668 * Email.CONTENT_ITEM_TYPE. Note the 669 * single quotes surrounding Email.CONTENT_ITEM_TYPE. 670 */ 671 Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'"; 672</pre> 673<p> 674 Next, define variables to contain the selection argument: 675</p> 676<pre> 677 String mSearchString; 678 String[] mSelectionArgs = { "" }; 679</pre> 680<h3 id="SpecificLoader">Implement onCreateLoader()</h3> 681<p> 682 Now that you've specified the data you want and how to find it, define a query in your 683 implementation of {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader 684 onCreateLoader()}. Return a new {@link android.support.v4.content.CursorLoader} from this 685 method, using your projection, selection text expression, and selection array as 686 arguments. For a content URI, use 687 {@link android.provider.ContactsContract.Data#CONTENT_URI Data.CONTENT_URI}. For example: 688</p> 689<pre> 690 @Override 691 public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) { 692 // OPTIONAL: Makes search string into pattern 693 mSearchString = "%" + mSearchString + "%"; 694 // Puts the search string into the selection criteria 695 mSelectionArgs[0] = mSearchString; 696 // Starts the query 697 return new CursorLoader( 698 getActivity(), 699 Data.CONTENT_URI, 700 PROJECTION, 701 SELECTION, 702 mSelectionArgs, 703 null 704 ); 705 } 706</pre> 707<p> 708 These code snippets are the basis of a simple reverse lookup based on a specific type of detail 709 data. This is the best technique to use if your app focuses on a particular type of data, such 710 as emails, and you want allow users to get the names associated with a piece of data. 711</p> 712<h2 id="GeneralMatch">Match a Contact By Any Type of Data</h2> 713<p> 714 Retrieving a contact based on any type of data returns contacts if any of their data matches a 715 the search string, including name, email address, postal address, phone number, and so forth. 716 This results in a broad set of search results. For example, if the search string 717 is "Doe", then searching for any data type returns the contact "John Doe"; it also returns 718 contacts who live on "Doe Street". 719</p> 720<p> 721 To implement this type of retrieval, first implement the following code, as listed in 722 previous sections: 723</p> 724<ul> 725 <li> 726 Request Permission to Read the Provider. 727 </li> 728 <li> 729 Define ListView and item layouts. 730 </li> 731 <li> 732 <li> 733 Define a Fragment that displays the list of contacts. 734 </li> 735 <li> 736 Define global variables. 737 </li> 738 <li> 739 Initialize the Fragment. 740 </li> 741 <li> 742 Set up the CursorAdapter for the ListView. 743 </li> 744 <li> 745 Set the selected contact listener. 746 </li> 747 <li> 748 Define a projection. 749 </li> 750 <li> 751 Define constants for the Cursor column indexes. 752 <p> 753 For this type of retrieval, you're using the same table you used in the section 754 <a href="#NameMatch">Match a Contact by Name and List the Results</a>. Use the 755 same column indexes as well. 756 </p> 757 </li> 758 <li> 759 Define the onItemClick() method. 760 </li> 761 <li> 762 Initialize the loader. 763 </li> 764 <li> 765 766 Implement onLoadFinished() and onLoaderReset(). 767 </li> 768</ul> 769<p> 770 The following steps show you the additional code you need to match a search string to 771 any type of data and display the results. 772</p> 773<h3 id="NoSelection">Remove selection criteria</h3> 774<p> 775 Don't define the <code>SELECTION</code> constants or the <code>mSelectionArgs</code> variable. 776 These aren't used in this type of retrieval. 777</p> 778<h3 id="CreateLoaderAny">Implement onCreateLoader()</h3> 779<p> 780 Implement the {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader 781 onCreateLoader()} method, returning a new {@link android.support.v4.content.CursorLoader}. 782 You don't need to convert the search string into a pattern, because the Contacts Provider does 783 that automatically. Use 784 {@link android.provider.ContactsContract.Contacts#CONTENT_FILTER_URI 785 Contacts.CONTENT_FILTER_URI} as the base URI, and append your search string to it by calling 786 {@link android.net.Uri#withAppendedPath Uri.withAppendedPath()}. Using this URI 787 automatically triggers searching for any data type, as shown in the following example: 788</p> 789<pre> 790 @Override 791 public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) { 792 /* 793 * Appends the search string to the base URI. Always 794 * encode search strings to ensure they're in proper 795 * format. 796 */ 797 Uri contentUri = Uri.withAppendedPath( 798 Contacts.CONTENT_FILTER_URI, 799 Uri.encode(mSearchString)); 800 // Starts the query 801 return new CursorLoader( 802 getActivity(), 803 contentUri, 804 PROJECTION, 805 null, 806 null, 807 null 808 ); 809 } 810</pre> 811<p> 812 These code snippets are the basis of an app that does a broad search of the Contacts Provider. 813 The technique is useful for apps that want to implement functionality similar to the 814 People app's contact list screen. 815</p> 816