Joe Malin | 421435d | 2013-01-15 15:06:08 -0800 | [diff] [blame] | 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> |