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 could create 106 the main layout file <code>res/layout/contacts_list_view.xml</code> with 107 the 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 Android Studio. 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.contact_list_fragment, 254 container, false); 255 } 256</pre> 257<h3 id="DefineAdapter">Set up the CursorAdapter for the ListView</h3> 258<p> 259 Set up the {@link android.support.v4.widget.SimpleCursorAdapter} that binds the results of the 260 search to the {@link android.widget.ListView}. To get the {@link android.widget.ListView} object 261 that displays the contacts, you need to call {@link android.app.Activity#findViewById 262 Activity.findViewById()} using the parent activity of the 263 {@link android.support.v4.app.Fragment}. Use the {@link android.content.Context} of the 264 parent activity when you call {@link android.widget.ListView#setAdapter setAdapter()}. 265 For example: 266</p> 267<pre> 268 public void onActivityCreated(Bundle savedInstanceState) { 269 super.onActivityCreated(savedInstanceState); 270 ... 271 // Gets the ListView from the View list of the parent activity 272 mContactsList = 273 (ListView) getActivity().findViewById(R.layout.contact_list_view); 274 // Gets a CursorAdapter 275 mCursorAdapter = new SimpleCursorAdapter( 276 getActivity(), 277 R.layout.contact_list_item, 278 null, 279 FROM_COLUMNS, TO_IDS, 280 0); 281 // Sets the adapter for the ListView 282 mContactsList.setAdapter(mCursorAdapter); 283 } 284</pre> 285<h3 id="SetListener">Set the selected contact listener</h3> 286<p> 287 When you display the results of a search, you usually want to allow the user to select a 288 single contact for further processing. For example, when the user clicks a contact you can 289 display the contact's address on a map. To provide this feature, you first defined the current 290 {@link android.support.v4.app.Fragment} as the click listener by specifying that the class 291 implements {@link android.widget.AdapterView.OnItemClickListener}, as shown in the section 292 <a href="#Fragment">Define a Fragment that displays the list of contacts</a>. 293</p> 294<p> 295 To continue setting up the listener, bind it to the {@link android.widget.ListView} by 296 calling the method {@link android.widget.ListView#setOnItemClickListener 297 setOnItemClickListener()} in {@link android.support.v4.app.Fragment#onActivityCreated 298 onActivityCreated()}. For example: 299</p> 300<pre> 301 public void onActivityCreated(Bundle savedInstanceState) { 302 ... 303 // Set the item click listener to be the current fragment. 304 mContactsList.setOnItemClickListener(this); 305 ... 306 } 307</pre> 308<p> 309 Since you specified that the current {@link android.support.v4.app.Fragment} is the 310 {@link android.widget.AdapterView.OnItemClickListener OnItemClickListener} for the 311 {@link android.widget.ListView}, you now need to implement its required method 312 {@link android.widget.AdapterView.OnItemClickListener#onItemClick onItemClick()}, which 313 handles the click event. This is described in a succeeding section. 314</p> 315<h3 id="DefineProjection">Define a projection</h3> 316<p> 317 Define a constant that contains the columns you want to return from your query. Each item in 318 the {@link android.widget.ListView} displays the contact's display name, 319 which contains the main form of the contact's name. In Android 3.0 (API version 11) and later, 320 the name of this column is 321 {@link android.provider.ContactsContract.Contacts#DISPLAY_NAME_PRIMARY 322 Contacts.DISPLAY_NAME_PRIMARY}; in versions previous to that, its name is 323 {@link android.provider.ContactsContract.Contacts#DISPLAY_NAME Contacts.DISPLAY_NAME}. 324</p> 325<p> 326 The column {@link android.provider.ContactsContract.Contacts#_ID Contacts._ID} is used by the 327 {@link android.support.v4.widget.SimpleCursorAdapter} binding process. 328 {@link android.provider.ContactsContract.Contacts#_ID Contacts._ID} and 329 {@link android.provider.ContactsContract.Contacts#LOOKUP_KEY} are used together to 330 construct a content URI for the contact the user selects. 331</p> 332<pre> 333... 334@SuppressLint("InlinedApi") 335private static final String[] PROJECTION = 336 { 337 Contacts._ID, 338 Contacts.LOOKUP_KEY, 339 Build.VERSION.SDK_INT 340 >= Build.VERSION_CODES.HONEYCOMB ? 341 Contacts.DISPLAY_NAME_PRIMARY : 342 Contacts.DISPLAY_NAME 343 344 }; 345</pre> 346<h3 id="DefineConstants">Define constants for the Cursor column indexes</h3> 347<p> 348 To get data from an individual column in a {@link android.database.Cursor}, you need 349 the column's index within the {@link android.database.Cursor}. You can define constants 350 for the indexes of the {@link android.database.Cursor} columns, because the indexes are 351 the same as the order of the column names in your projection. For example: 352</p> 353<pre> 354// The column index for the _ID column 355private static final int CONTACT_ID_INDEX = 0; 356// The column index for the LOOKUP_KEY column 357private static final int LOOKUP_KEY_INDEX = 1; 358</pre> 359<h3 id="SelectionCriteria">Specify the selection criteria</h3> 360<p> 361 To specify the data you want, create a combination of text expressions and variables 362 that tell the provider the data columns to search and the values to find. 363</p> 364<p> 365 For the text expression, define a constant that lists the search columns. Although this 366 expression can contain values as well, the preferred practice is to represent the values with 367 a "?" placeholder. During retrieval, the placeholder is replaced with values from an 368 array. Using "?" as a placeholder ensures that the search specification is generated by binding 369 rather than by SQL compilation. This practice eliminates the possibility of malicious SQL 370 injection. For example: 371</p> 372<pre> 373 // Defines the text expression 374 @SuppressLint("InlinedApi") 375 private static final String SELECTION = 376 Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ? 377 Contacts.DISPLAY_NAME_PRIMARY + " LIKE ?" : 378 Contacts.DISPLAY_NAME + " LIKE ?"; 379 // Defines a variable for the search string 380 private String mSearchString; 381 // Defines the array to hold values that replace the ? 382 private String[] mSelectionArgs = { mSearchString }; 383</pre> 384<h3 id="OnItemClick">Define the onItemClick() method</h3> 385<p> 386 In a previous section, you set the item click listener for the {@link android.widget.ListView}. 387 Now implement the action for the listener by defining the method 388 {@link android.widget.AdapterView.OnItemClickListener#onItemClick 389 AdapterView.OnItemClickListener.onItemClick()}: 390</p> 391<pre> 392 @Override 393 public void onItemClick( 394 AdapterView<?> parent, View item, int position, long rowID) { 395 // Get the Cursor 396 Cursor cursor = parent.getAdapter().getCursor(); 397 // Move to the selected contact 398 cursor.moveToPosition(position); 399 // Get the _ID value 400 mContactId = getLong(CONTACT_ID_INDEX); 401 // Get the selected LOOKUP KEY 402 mContactKey = getString(CONTACT_KEY_INDEX); 403 // Create the contact's content Uri 404 mContactUri = Contacts.getLookupUri(mContactId, mContactKey); 405 /* 406 * You can use mContactUri as the content URI for retrieving 407 * the details for a contact. 408 */ 409 } 410</pre> 411<h3 id="InitializeLoader">Initialize the loader</h3> 412<p> 413 Since you're using a {@link android.support.v4.content.CursorLoader} to retrieve data, 414 you must initialize the background thread and other variables that control asynchronous 415 retrieval. Do the initialization in 416 {@link android.support.v4.app.Fragment#onActivityCreated onActivityCreated()}, which 417 is invoked immediately before the {@link android.support.v4.app.Fragment} UI appears, as 418 shown in the following example: 419</p> 420<pre> 421public class ContactsFragment extends Fragment implements 422 LoaderManager.LoaderCallbacks<Cursor> { 423 ... 424 // Called just before the Fragment displays its UI 425 @Override 426 public void onActivityCreated(Bundle savedInstanceState) { 427 // Always call the super method first 428 super.onActivityCreated(savedInstanceState); 429 ... 430 // Initializes the loader 431 getLoaderManager().initLoader(0, null, this); 432</pre> 433<h3 id="OnCreateLoader">Implement onCreateLoader()</h3> 434<p> 435 Implement the method 436 {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}, 437 which is called by the loader framework immediately after you call 438 {@link android.support.v4.app.LoaderManager#initLoader initLoader()}. 439<p> 440 In {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}, 441 set up the search string pattern. To make a string into a pattern, insert "%" 442 (percent) characters to represent a sequence of zero or more characters, or "_" (underscore) 443 characters to represent a single character, or both. For example, the pattern "%Jefferson%" 444 would match both "Thomas Jefferson" and "Jefferson Davis". 445</p> 446<p> 447 Return a new {@link android.support.v4.content.CursorLoader} from the method. For the content 448 URI, use {@link android.provider.ContactsContract.Contacts#CONTENT_URI Contacts.CONTENT_URI}. 449 This URI refers to the entire table, as shown in the following example: 450</p> 451<pre> 452 ... 453 @Override 454 public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) { 455 /* 456 * Makes search string into pattern and 457 * stores it in the selection array 458 */ 459 mSelectionArgs[0] = "%" + mSearchString + "%"; 460 // Starts the query 461 return new CursorLoader( 462 getActivity(), 463 Contacts.CONTENT_URI, 464 PROJECTION, 465 SELECTION, 466 mSelectionArgs, 467 null 468 ); 469 } 470</pre> 471<h3 id="FinishedReset">Implement onLoadFinished() and onLoaderReset()</h3> 472<p> 473 Implement the 474 {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()} 475 method. The loader framework calls 476 {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()} 477 when the Contacts Provider returns the results of the query. In this method, put the 478 result {@link android.database.Cursor} in the 479 {@link android.support.v4.widget.SimpleCursorAdapter}. This automatically updates the 480 {@link android.widget.ListView} with the search results: 481</p> 482<pre> 483 public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { 484 // Put the result Cursor in the adapter for the ListView 485 mCursorAdapter.swapCursor(cursor); 486 } 487</pre> 488<p> 489 The method {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoaderReset 490 onLoaderReset()} is invoked when the loader framework detects that the 491 result {@link android.database.Cursor} contains stale data. Delete the 492 {@link android.support.v4.widget.SimpleCursorAdapter} reference to the existing 493 {@link android.database.Cursor}. If you don't, the loader framework will not 494 recycle the {@link android.database.Cursor}, which causes a memory leak. For example: 495</p> 496<pre> 497 @Override 498 public void onLoaderReset(Loader<Cursor> loader) { 499 // Delete the reference to the existing Cursor 500 mCursorAdapter.swapCursor(null); 501 502 } 503</pre> 504 505<p> 506 You now have the key pieces of an app that matches a search string to contact names and returns 507 the result in a {@link android.widget.ListView}. The user can click a contact name to select it. 508 This triggers a listener, in which you can work further with the contact's data. For example, 509 you can retrieve the contact's details. To learn how to do this, continue with the next 510 lesson, <a href="#retrieve-details.html">Retrieving Details for a Contact</a>. 511</p> 512<p> 513 To learn more about search user interfaces, read the API guide 514 <a href="{@docRoot}guide/topics/search/search-dialog.html">Creating a Search Interface</a>. 515</p> 516<p> 517 The remaining sections in this lesson demonstrate other ways of finding contacts in the 518 Contacts Provider. 519</p> 520<h2 id="TypeMatch">Match a Contact By a Specific Type of Data</h2> 521<p> 522 This technique allows you to specify the type of data you want to match. Retrieving 523 by name is a specific example of this type of query, but you can also do it for any of the types 524 of detail data associated with a contact. For example, you can retrieve contacts that have a 525 specific postal code; in this case, the search string has to match data stored in a postal code 526 row. 527</p> 528<p> 529 To implement this type of retrieval, first implement the following code, as listed in 530 previous sections: 531</p> 532<ul> 533 <li> 534 Request Permission to Read the Provider. 535 </li> 536 <li> 537 Define ListView and item layouts. 538 </li> 539 <li> 540 Define a Fragment that displays the list of contacts. 541 </li> 542 <li> 543 Define global variables. 544 </li> 545 <li> 546 Initialize the Fragment. 547 </li> 548 <li> 549 Set up the CursorAdapter for the ListView. 550 </li> 551 <li> 552 Set the selected contact listener. 553 </li> 554 <li> 555 Define constants for the Cursor column indexes. 556 <p> 557 Although you're retrieving data from a different table, the order of the columns in 558 the projection is the same, so you can use the same indexes for the Cursor. 559 </p> 560 </li> 561 <li> 562 Define the onItemClick() method. 563 </li> 564 <li> 565 Initialize the loader. 566 </li> 567 <li> 568 569 Implement onLoadFinished() and onLoaderReset(). 570 </li> 571</ul> 572<p> 573 The following steps show you the additional code you need to match a search string to 574 a particular type of detail data and display the results. 575</p> 576<h3>Choose the data type and table</h3> 577<p> 578 To search for a particular type of detail data, you have to know the custom MIME type value 579 for the data type. Each data type has a unique MIME type 580 value defined by a constant <code>CONTENT_ITEM_TYPE</code> in the subclass of 581 {@link android.provider.ContactsContract.CommonDataKinds} associated with the data type. 582 The subclasses have names that indicate their data type; for example, the subclass for email 583 data is {@link android.provider.ContactsContract.CommonDataKinds.Email}, and the custom MIME 584 type for email data is defined by the constant 585 {@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_ITEM_TYPE 586 Email.CONTENT_ITEM_TYPE}. 587</p> 588<p> 589 Use the {@link android.provider.ContactsContract.Data} table for your search. All of the 590 constants you need for your projection, selection clause, and sort order are defined in or 591 inherited by this table. 592</p> 593<h3 id="SpecificProjection">Define a projection</h3> 594<p> 595 To define a projection, choose one or more of the columns defined in 596 {@link android.provider.ContactsContract.Data} or the classes from which it inherits. The 597 Contacts Provider does an implicit join between {@link android.provider.ContactsContract.Data} 598 and other tables before it returns rows. For example: 599</p> 600<pre> 601 @SuppressLint("InlinedApi") 602 private static final String[] PROJECTION = 603 { 604 /* 605 * The detail data row ID. To make a ListView work, 606 * this column is required. 607 */ 608 Data._ID, 609 // The primary display name 610 Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ? 611 Data.DISPLAY_NAME_PRIMARY : 612 Data.DISPLAY_NAME, 613 // The contact's _ID, to construct a content URI 614 Data.CONTACT_ID 615 // The contact's LOOKUP_KEY, to construct a content URI 616 Data.LOOKUP_KEY (a permanent link to the contact 617 }; 618</pre> 619<h3 id="SpecificCriteria">Define search criteria</h3> 620<p> 621 To search for a string within a particular type of data, construct a selection clause from 622 the following: 623</p> 624<ul> 625 <li> 626 The name of the column that contains your search string. This name varies by data type, 627 so you need to find the subclass of 628 {@link android.provider.ContactsContract.CommonDataKinds} that corresponds to the data type 629 and then choose the column name from that subclass. For example, to search for 630 email addresses, use the column 631 {@link android.provider.ContactsContract.CommonDataKinds.Email#ADDRESS Email.ADDRESS}. 632 </li> 633 <li> 634 The search string itself, represented as the "?" character in the selection clause. 635 </li> 636 <li> 637 The name of the column that contains the custom MIME type value. This name is always 638 {@link android.provider.ContactsContract.Data#MIMETYPE Data.MIMETYPE}. 639 </li> 640 <li> 641 The custom MIME type value for the data type. As described previously, this is the constant 642 <code>CONTENT_ITEM_TYPE</code> in the 643 {@link android.provider.ContactsContract.CommonDataKinds} subclass. For example, the MIME 644 type value for email data is 645 {@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_ITEM_TYPE 646 Email.CONTENT_ITEM_TYPE}. Enclose the value in single quotes by concatenating a 647 "<code>'</code>" (single quote) character to the start and end of the constant; otherwise, 648 the provider interprets the value as a variable name rather than as a string value. 649 You don't need to use a placeholder for this value, because you're using a constant 650 rather than a user-supplied value. 651 </li> 652</ul> 653<p> 654 For example: 655</p> 656<pre> 657 /* 658 * Constructs search criteria from the search string 659 * and email MIME type 660 */ 661 private static final String SELECTION = 662 /* 663 * Searches for an email address 664 * that matches the search string 665 */ 666 Email.ADDRESS + " LIKE ? " + "AND " + 667 /* 668 * Searches for a MIME type that matches 669 * the value of the constant 670 * Email.CONTENT_ITEM_TYPE. Note the 671 * single quotes surrounding Email.CONTENT_ITEM_TYPE. 672 */ 673 Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'"; 674</pre> 675<p> 676 Next, define variables to contain the selection argument: 677</p> 678<pre> 679 String mSearchString; 680 String[] mSelectionArgs = { "" }; 681</pre> 682<h3 id="SpecificLoader">Implement onCreateLoader()</h3> 683<p> 684 Now that you've specified the data you want and how to find it, define a query in your 685 implementation of {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader 686 onCreateLoader()}. Return a new {@link android.support.v4.content.CursorLoader} from this 687 method, using your projection, selection text expression, and selection array as 688 arguments. For a content URI, use 689 {@link android.provider.ContactsContract.Data#CONTENT_URI Data.CONTENT_URI}. For example: 690</p> 691<pre> 692 @Override 693 public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) { 694 // OPTIONAL: Makes search string into pattern 695 mSearchString = "%" + mSearchString + "%"; 696 // Puts the search string into the selection criteria 697 mSelectionArgs[0] = mSearchString; 698 // Starts the query 699 return new CursorLoader( 700 getActivity(), 701 Data.CONTENT_URI, 702 PROJECTION, 703 SELECTION, 704 mSelectionArgs, 705 null 706 ); 707 } 708</pre> 709<p> 710 These code snippets are the basis of a simple reverse lookup based on a specific type of detail 711 data. This is the best technique to use if your app focuses on a particular type of data, such 712 as emails, and you want allow users to get the names associated with a piece of data. 713</p> 714<h2 id="GeneralMatch">Match a Contact By Any Type of Data</h2> 715<p> 716 Retrieving a contact based on any type of data returns contacts if any of their data matches a 717 the search string, including name, email address, postal address, phone number, and so forth. 718 This results in a broad set of search results. For example, if the search string 719 is "Doe", then searching for any data type returns the contact "John Doe"; it also returns 720 contacts who live on "Doe Street". 721</p> 722<p> 723 To implement this type of retrieval, first implement the following code, as listed in 724 previous sections: 725</p> 726<ul> 727 <li> 728 Request Permission to Read the Provider. 729 </li> 730 <li> 731 Define ListView and item layouts. 732 </li> 733 <li> 734 Define a Fragment that displays the list of contacts. 735 </li> 736 <li> 737 Define global variables. 738 </li> 739 <li> 740 Initialize the Fragment. 741 </li> 742 <li> 743 Set up the CursorAdapter for the ListView. 744 </li> 745 <li> 746 Set the selected contact listener. 747 </li> 748 <li> 749 Define a projection. 750 </li> 751 <li> 752 Define constants for the Cursor column indexes. 753 <p> 754 For this type of retrieval, you're using the same table you used in the section 755 <a href="#NameMatch">Match a Contact by Name and List the Results</a>. Use the 756 same column indexes as well. 757 </p> 758 </li> 759 <li> 760 Define the onItemClick() method. 761 </li> 762 <li> 763 Initialize the loader. 764 </li> 765 <li> 766 767 Implement onLoadFinished() and onLoaderReset(). 768 </li> 769</ul> 770<p> 771 The following steps show you the additional code you need to match a search string to 772 any type of data and display the results. 773</p> 774<h3 id="NoSelection">Remove selection criteria</h3> 775<p> 776 Don't define the <code>SELECTION</code> constants or the <code>mSelectionArgs</code> variable. 777 These aren't used in this type of retrieval. 778</p> 779<h3 id="CreateLoaderAny">Implement onCreateLoader()</h3> 780<p> 781 Implement the {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader 782 onCreateLoader()} method, returning a new {@link android.support.v4.content.CursorLoader}. 783 You don't need to convert the search string into a pattern, because the Contacts Provider does 784 that automatically. Use 785 {@link android.provider.ContactsContract.Contacts#CONTENT_FILTER_URI 786 Contacts.CONTENT_FILTER_URI} as the base URI, and append your search string to it by calling 787 {@link android.net.Uri#withAppendedPath Uri.withAppendedPath()}. Using this URI 788 automatically triggers searching for any data type, as shown in the following example: 789</p> 790<pre> 791 @Override 792 public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) { 793 /* 794 * Appends the search string to the base URI. Always 795 * encode search strings to ensure they're in proper 796 * format. 797 */ 798 Uri contentUri = Uri.withAppendedPath( 799 Contacts.CONTENT_FILTER_URI, 800 Uri.encode(mSearchString)); 801 // Starts the query 802 return new CursorLoader( 803 getActivity(), 804 contentUri, 805 PROJECTION, 806 null, 807 null, 808 null 809 ); 810 } 811</pre> 812<p> 813 These code snippets are the basis of an app that does a broad search of the Contacts Provider. 814 The technique is useful for apps that want to implement functionality similar to the 815 People app's contact list screen. 816</p> 817