1 page.title=Retrieving Details for a Contact 2 3 trainingnavtop=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="#RetrieveAll">Retrieve All Details for a Contact</a></li> 13 <li><a href="#RetrieveSpecific">Retrieve Specific Details for a Contact</a></li> 14 </ol> 15 16 <!-- other docs (NOT javadocs) --> 17 <h2>You should also read</h2> 18 <ul> 19 <li> 20 <a href="{@docRoot}guide/topics/providers/content-provider-basics.html"> 21 Content Provider Basics</a> 22 </li> 23 <li> 24 <a href="{@docRoot}guide/topics/providers/contacts-provider.html"> 25 Contacts Provider</a> 26 </li> 27 <li> 28 <a href="{@docRoot}guide/components/loaders.html">Loaders</a> 29 </ul> 30 31 <h2>Try it out</h2> 32 33 <div class="download-box"> 34 <a href="http://developer.android.com/shareables/training/ContactsList.zip" class="button"> 35 Download the sample 36 </a> 37 <p class="filename">ContactsList.zip</p> 38 </div> 39 40 </div> 41 </div> 42 <p> 43 This lesson shows how to retrieve detail data for a contact, such as email addresses, phone 44 numbers, and so forth. It's the details that users are looking for when they retrieve a contact. 45 You can give them all the details for a contact, or only display details of a particular type, 46 such as email addresses. 47 </p> 48 <p> 49 The steps in this lesson assume that you already have a 50 {@link android.provider.ContactsContract.Contacts} row for a contact the user has picked. 51 The <a href="retrieve-names.html">Retrieving Contact Names</a> lesson shows how to 52 retrieve a list of contacts. 53 </p> 54 <h2 id="RetrieveAll">Retrieve All Details for a Contact</h2> 55 <p> 56 To retrieve all the details for a contact, search the 57 {@link android.provider.ContactsContract.Data} table for any rows that contain the contact's 58 {@link android.provider.ContactsContract.Data#LOOKUP_KEY}. This column is available in 59 the {@link android.provider.ContactsContract.Data} table, because the Contacts 60 Provider makes an implicit join between the {@link android.provider.ContactsContract.Contacts} 61 table and the {@link android.provider.ContactsContract.Data} table. The 62 {@link android.provider.ContactsContract.Contacts#LOOKUP_KEY} column is described 63 in more detail in the <a href="retrieve-names.html">Retrieving Contact Names</a> lesson. 64 </p> 65 <p class="note"> 66 <strong>Note:</strong> Retrieving all the details for a contact reduces the performance of a 67 device, because it needs to retrieve all of the columns in the 68 {@link android.provider.ContactsContract.Data} table. Consider the performance impact before 69 you use this technique. 70 </p> 71 <h3>Request permissions</h3> 72 <p> 73 To read from the Contacts Provider, your app must have 74 {@link android.Manifest.permission#READ_CONTACTS READ_CONTACTS} permission. 75 To request this permission, add the following child element of 76 <code><a href="{@docRoot}guide/topics/manifest/manifest-element.html"> 77 <manifest></a></code> to your manifest file: 78 </p> 79 <pre> 80 <uses-permission android:name="android.permission.READ_CONTACTS" /> 81 </pre> 82 <h3>Set up a projection</h3> 83 <p> 84 Depending on the data type a row contains, it may use only a few columns or many. In addition, 85 the data is in different columns depending on the data type. 86 To ensure you get all the possible columns for all possible data types, you need to add all the 87 column names to your projection. Always retrieve 88 {@link android.provider.ContactsContract.Data#_ID Data._ID} if you're binding the result 89 {@link android.database.Cursor} to a {@link android.widget.ListView}; otherwise, the binding 90 won't work. Also retrieve {@link android.provider.ContactsContract.Data#MIMETYPE Data.MIMETYPE} 91 so you can identify the data type of each row you retrieve. For example: 92 </p> 93 <pre> 94 private static final String PROJECTION = 95 { 96 Data._ID, 97 Data.MIMETYPE, 98 Data.DATA1, 99 Data.DATA2, 100 Data.DATA3, 101 Data.DATA4, 102 Data.DATA5, 103 Data.DATA6, 104 Data.DATA7, 105 Data.DATA8, 106 Data.DATA9, 107 Data.DATA10, 108 Data.DATA11, 109 Data.DATA12, 110 Data.DATA13, 111 Data.DATA14, 112 Data.DATA15 113 }; 114 </pre> 115 <p> 116 This projection retrieves all the columns for a row in the 117 {@link android.provider.ContactsContract.Data} table, using the column names defined in 118 the {@link android.provider.ContactsContract.Data} class. 119 </p> 120 <p> 121 Optionally, you can also use any other column constants defined in or inherited by the 122 {@link android.provider.ContactsContract.Data} class. Notice, however, that the columns 123 {@link android.provider.ContactsContract.DataColumns#SYNC1} through 124 {@link android.provider.ContactsContract.DataColumns#SYNC4} are meant to be used by sync 125 adapters, so their data is not useful. 126 </p> 127 <h3>Define the selection criteria</h3> 128 <p> 129 Define a constant for your selection clause, an array to hold selection arguments, and a 130 variable to hold the selection value. Use 131 the {@link android.provider.ContactsContract.Contacts#LOOKUP_KEY Contacts.LOOKUP_KEY} column to 132 find the contact. For example: 133 </p> 134 <pre> 135 // Defines the selection clause 136 private static final String SELECTION = Data.LOOKUP_KEY + " = ?"; 137 // Defines the array to hold the search criteria 138 private String[] mSelectionArgs = { "" }; 139 /* 140 * Defines a variable to contain the selection value. Once you 141 * have the Cursor from the Contacts table, and you've selected 142 * the desired row, move the row's LOOKUP_KEY value into this 143 * variable. 144 */ 145 private String mLookupKey; 146 </pre> 147 <p> 148 Using "?" as a placeholder in your selection text expression ensures that the resulting search 149 is generated by binding rather than SQL compilation. This approach eliminates the 150 possibility of malicious SQL injection. 151 </p> 152 <h3>Define the sort order</h3> 153 <p> 154 Define the sort order you want in the resulting {@link android.database.Cursor}. To 155 keep all rows for a particular data type together, sort by 156 {@link android.provider.ContactsContract.Data#MIMETYPE Data.MIMETYPE}. This query argument 157 groups all email rows together, all phone rows together, and so forth. For example: 158 </p> 159 <pre> 160 /* 161 * Defines a string that specifies a sort order of MIME type 162 */ 163 private static final String SORT_ORDER = Data.MIMETYPE; 164 </pre> 165 <p class="note"> 166 <strong>Note:</strong> Some data types don't use a subtype, so you can't sort on subtype. 167 Instead, you have to iterate through the returned {@link android.database.Cursor}, 168 determine the data type of the current row, and store data for rows that use a subtype. When 169 you finish reading the cursor, you can then sort each data type by subtype and display the 170 results. 171 </p> 172 <h3>Initialize the Loader</h3> 173 <p> 174 Always do retrievals from the Contacts Provider (and all other content providers) in a 175 background thread. Use the Loader framework defined by the 176 {@link android.support.v4.app.LoaderManager} class and the 177 {@link android.support.v4.app.LoaderManager.LoaderCallbacks} interface to do background 178 retrievals. 179 </p> 180 <p> 181 When you're ready to retrieve the rows, initialize the loader framework by 182 calling {@link android.support.v4.app.LoaderManager#initLoader initLoader()}. Pass an 183 integer identifier to the method; this identifier is passed to 184 {@link android.support.v4.app.LoaderManager.LoaderCallbacks} methods. The identifier helps you 185 use multiple loaders in an app by allowing you to differentiate between them. 186 </p> 187 <p> 188 The following snippet shows how to initialize the loader framework: 189 </p> 190 <pre> 191 public class DetailsFragment extends Fragment implements 192 LoaderManager.LoaderCallbacks<Cursor> { 193 ... 194 // Defines a constant that identifies the loader 195 DETAILS_QUERY_ID = 0; 196 ... 197 /* 198 * Invoked when the parent Activity is instantiated 199 * and the Fragment's UI is ready. Put final initialization 200 * steps here. 201 */ 202 @Override 203 onActivityCreated(Bundle savedInstanceState) { 204 ... 205 // Initializes the loader framework 206 getLoaderManager().initLoader(DETAILS_QUERY_ID, null, this); 207 </pre> 208 <h3>Implement onCreateLoader()</h3> 209 <p> 210 Implement the {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader 211 onCreateLoader()} method, which is called by the loader framework immediately after you call 212 {@link android.support.v4.app.LoaderManager#initLoader initLoader()}. Return a 213 {@link android.support.v4.content.CursorLoader} from this method. Since you're searching 214 the {@link android.provider.ContactsContract.Data} table, use the constant 215 {@link android.provider.ContactsContract.Data#CONTENT_URI Data.CONTENT_URI} as the content URI. 216 For example: 217 </p> 218 <pre> 219 @Override 220 public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) { 221 // Choose the proper action 222 switch (loaderId) { 223 case DETAILS_QUERY_ID: 224 // Assigns the selection parameter 225 mSelectionArgs[0] = mLookupKey; 226 // Starts the query 227 CursorLoader mLoader = 228 new CursorLoader( 229 getActivity(), 230 Data.CONTENT_URI, 231 PROJECTION, 232 SELECTION, 233 mSelectionArgs, 234 SORT_ORDER 235 ); 236 ... 237 } 238 </pre> 239 <h3>Implement onLoadFinished() and onLoaderReset()</h3> 240 <p> 241 Implement the 242 {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()} 243 method. The loader framework calls 244 {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()} 245 when the Contacts Provider returns the results of the query. For example: 246 </p> 247 <pre> 248 public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { 249 switch (loader.getId()) { 250 case DETAILS_QUERY_ID: 251 /* 252 * Process the resulting Cursor here. 253 */ 254 } 255 break; 256 ... 257 } 258 } 259 </pre> 260 <p> 261 <p> 262 The method {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoaderReset 263 onLoaderReset()} is invoked when the loader framework detects that the data backing the result 264 {@link android.database.Cursor} has changed. At this point, remove any existing references 265 to the {@link android.database.Cursor} by setting them to null. If you don't, the loader 266 framework won't destroy the old {@link android.database.Cursor}, and you'll get a memory 267 leak. For example: 268 <pre> 269 @Override 270 public void onLoaderReset(Loader<Cursor> loader) { 271 switch (loader.getId()) { 272 case DETAILS_QUERY_ID: 273 /* 274 * If you have current references to the Cursor, 275 * remove them here. 276 */ 277 } 278 break; 279 } 280 </pre> 281 <h2 id="RetrieveSpecific">Retrieve Specific Details for a Contact</h2> 282 <p> 283 Retrieving a specific data type for a contact, such as all the emails, follows the same pattern 284 as retrieving all details. These are the only changes you need to make to the code 285 listed in <a href="#RetrieveAll">Retrieve All Details for a Contact</a>: 286 </p> 287 <dl> 288 <dt> 289 Projection 290 </dt> 291 <dd> 292 Modify your projection to retrieve the columns that are specific to the 293 data type. Also modify the projection to use the column name constants defined in the 294 {@link android.provider.ContactsContract.CommonDataKinds} subclass corresponding to the 295 data type. 296 </dd> 297 <dt> 298 Selection 299 </dt> 300 <dd> 301 Modify the selection text to search for the 302 {@link android.provider.ContactsContract.Data#MIMETYPE MIMETYPE} value that's specific to 303 your data type. 304 </dd> 305 <dt> 306 Sort order 307 </dt> 308 <dd> 309 Since you're only selecting a single detail type, don't group the returned 310 {@link android.database.Cursor} by {@link android.provider.ContactsContract.Data#MIMETYPE 311 Data.MIMETYPE}. 312 </dd> 313 </dl> 314 <p> 315 These modifications are described in the following sections. 316 </p> 317 <h3>Define a projection</h3> 318 <p> 319 Define the columns you want to retrieve, using the column name constants in the subclass 320 of {@link android.provider.ContactsContract.CommonDataKinds} for the data type. 321 If you plan to bind your {@link android.database.Cursor} to a {@link android.widget.ListView}, 322 be sure to retrieve the <code>_ID</code> column. For example, to retrieve email data, define the 323 following projection: 324 </p> 325 <pre> 326 private static final String[] PROJECTION = 327 { 328 Email._ID, 329 Email.ADDRESS, 330 Email.TYPE, 331 Email.LABEL 332 }; 333 </pre> 334 <p> 335 Notice that this projection uses the column names defined in the class 336 {@link android.provider.ContactsContract.CommonDataKinds.Email}, instead of the column names 337 defined in the class {@link android.provider.ContactsContract.Data}. Using the email-specific 338 column names makes the code more readable. 339 </p> 340 <p> 341 In the projection, you can also use any of the other columns defined in the 342 {@link android.provider.ContactsContract.CommonDataKinds} subclass. 343 </p> 344 <h3>Define selection criteria</h3> 345 <p> 346 Define a search text expression that retrieves rows for a specific contact's 347 {@link android.provider.ContactsContract.Data#LOOKUP_KEY} and the 348 {@link android.provider.ContactsContract.Data#MIMETYPE Data.MIMETYPE} of the details you 349 want. Enclose the {@link android.provider.ContactsContract.Data#MIMETYPE MIMETYPE} value in 350 single quotes by concatenating a "<code>'</code>" (single-quote) character to the start and end 351 of the constant; otherwise, the provider interprets the constant as a variable name rather 352 than as a string value. You don't need to use a placeholder for this value, because you're 353 using a constant rather than a user-supplied value. For example: 354 </p> 355 <pre> 356 /* 357 * Defines the selection clause. Search for a lookup key 358 * and the Email MIME type 359 */ 360 private static final String SELECTION = 361 Data.LOOKUP_KEY + " = ?" + 362 " AND " + 363 Data.MIMETYPE + " = " + 364 "'" + Email.CONTENT_ITEM_TYPE + "'"; 365 // Defines the array to hold the search criteria 366 private String[] mSelectionArgs = { "" }; 367 </pre> 368 <h3>Define a sort order</h3> 369 <p> 370 Define a sort order for the returned {@link android.database.Cursor}. Since you're retrieving a 371 specific data type, omit the sort on {@link android.provider.ContactsContract.Data#MIMETYPE}. 372 Instead, if the type of detail data you're searching includes a subtype, sort on it. 373 For example, for email data you can sort on 374 {@link android.provider.ContactsContract.CommonDataKinds.Email#TYPE Email.TYPE}: 375 </p> 376 <pre> 377 private static final String SORT_ORDER = Email.TYPE + " ASC "; 378 </pre> 379