Android Essentials: Using the Contact Picker
This tutorial will not only show you how to launch the contact picker and get results, but also how to use those results in Android SDK 2.0 and above.
We’ll then be adding functionality to allow a user to choose one of their existing contacts and send a canned message to them. We’re going to dive right in, so have all of your code and tools ready. Finally, make sure your device or emulator has some contacts configured (with names and emails) within the Contacts application.
The following Layout segment has both of these elements defined appropriately:
This layout XML is part of a larger layout. Here’s what it looks like in the layout designer, complete with the string resources filled out:
Here’s the implementation of doLaunchContactPicker():
Once launched, the contacts picker in your application will look something like this:
The full Uri looks something like:
In this case, the resulting id would simply be 7.
We can retrieve the contact identifier using the getLastPathSegment() method, as follows:
One way you can query the contacts content provider for the appropriate contact details is by using the default ContentResolver with one of the ContactsContract.CommonDataKinds subclasses. For email, you can use the ContactsContract.CommonDataKinds.Email class as follows:
This snippet of code will show you, via LogCat output, every column and value that is returned from the query to the content provider:
First, we didn’t include any error checking; we did this for clarity, but in production code, this is an essential piece of the solution. An easy way to implement some checking would be to to wrap just about everything in a try-catch block.
Second, you need to remember that Cursor objects require management within your Activity lifecycle. Always remember to release Cursor objects when you are done using them.
Here’s the complete implementation of the onActivityResult() method to put these points in perspective:
Getting Started
This tutorial will start out simple, but then we’ll get in to some of the technical details of using Contacts with the ContactsContract class, which was introduced in API Level 5. Make sure you have your Android development environment installed and configured correctly. You’re free to build upon any application you have, start a new one from scratch, or follow along using the InviteActivity code in our open source project.We’ll then be adding functionality to allow a user to choose one of their existing contacts and send a canned message to them. We’re going to dive right in, so have all of your code and tools ready. Finally, make sure your device or emulator has some contacts configured (with names and emails) within the Contacts application.
Step 1: Creating Your Layout
There are two essential form controls necessary for the contact picker to work. First, we need an EditText field where the resulting email will show. Second, we need some way for the user to launch the contact picker. A Button control works well for this.The following Layout segment has both of these elements defined appropriately:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 | < RelativeLayout android:layout_height = "wrap_content" android:layout_width = "match_parent" > < EditText android:layout_height = "wrap_content" android:hint = "@string/invite_email_hint" android:id = "@+id/invite_email" android:inputType = "textEmailAddress" android:layout_width = "wrap_content" android:layout_toLeftOf = "@+id/do_email_picker" android:layout_alignParentLeft = "true" ></ EditText > < Button android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:id = "@+id/do_email_picker" android:text = "@string/pick_email_label" android:layout_alignParentRight = "true" android:onClick = "doLaunchContactPicker" ></ Button > </ RelativeLayout > |
Step 2: Launching the Contact Picker
Now you need to write the code to handle the Button push, which will launch the contact picker. One of the most powerful features of the Android platform is that you can leverage other applications’ functionality by using the Intent mechanism. An Intent can be used along with the startActivityForResult() method to launch another Android application and retrieve the result. In this case, you can use an Intent to pick a contact from the data provided by the Contacts content provider.Here’s the implementation of doLaunchContactPicker():
import android.provider.ContactsContract.Contacts;Note: The import commands are important here. Make sure you’re using the Contacts class from the ContactsContract and not the older android.provider.Contacts one.
import android.provider.ContactsContract.CommonDataKinds.Email;
private static final int CONTACT_PICKER_RESULT = 1001;
public void doLaunchContactPicker(View view) {
Intent contactPickerIntent = new Intent(Intent.ACTION_PICK,
Contacts.CONTENT_URI);
startActivityForResult(contactPickerIntent, CONTACT_PICKER_RESULT);
}
Once launched, the contacts picker in your application will look something like this:
Step 3: Handling the Results
Now you are ready to handle the results of the picker. Once the user taps on one of the contacts in the picker, focus will return to the calling Activity (your application’s Activity). You can grab the result from the contacts picker by implementing the onActivityResult() method of your Activity. Here you can check that the result matches your requestCode and that the result was good. Your onActivityResult() method implementation should be structured like this:protected void onActivityResult(int requestCode, int resultCode, Intent data) {You’ll get a result other than RESULT_OK if the user cancels the operation or if something else goes wrong.
if (resultCode == RESULT_OK) {
switch (requestCode) {
case CONTACT_PICKER_RESULT:
// handle contact results
break;
}
} else {
// gracefully handle failure
Log.w(DEBUG_TAG, "Warning: activity result not ok");
}
}
Step 4: Reading the Result Data
The final parameter to onActivityResult is an Intent called “data.” This parameter contains the results data we are looking for. Different Intents will return different types of results. One option for inspecting the results is to display everything found in the Extras bundle in addition to the data Uri. Here’s a code snippet that will show all of the Extras, should any exist:Bundle extras = data.getExtras();We’re not really interested in the Extras bundle for the contacts picker because it doesn’t contain the information we need. We just want the Uri which will lead us to the important contact details.
Set keys = extras.keySet();
Iterator iterate = keys.iterator();
while (iterate.hasNext()) {
String key = iterate.next();
Log.v(DEBUG_TAG, key + "[" + extras.get(key) + "]");
}
Uri result = data.getData();
Log.v(DEBUG_TAG, "Got a result: "
+ result.toString());
Step 5: Understanding the Result
In the onActivityResult() callback, we are supplied the Uri to the specific contact that the user chose from the contact picker. Using this Uri directly would allow us to get the basic Contact data, but no details. However, we are interested in determining the email address of the contact. So, an easy way to deal with this is to just grab the contact id from the Uri, which is the number at the end of the path:The full Uri looks something like:
content://com.android.contacts/contacts/lookup/0r7-2C46324E483C324A3A484634/7
In this case, the resulting id would simply be 7.
We can retrieve the contact identifier using the getLastPathSegment() method, as follows:
// get the contact id from the Uri
String id = result.getLastPathSegment();
Step 6: Querying the Contacts Database for Email
Now that you have the identifier for the chosen contact, you have all the information you need to query the Contacts content provider directly for that contact’s email address. Android content providers are a powerful way of sharing data amongst applications. The interface to them is similar to that of a database and many are database backed, using SQLite, but they need not be.One way you can query the contacts content provider for the appropriate contact details is by using the default ContentResolver with one of the ContactsContract.CommonDataKinds subclasses. For email, you can use the ContactsContract.CommonDataKinds.Email class as follows:
// query for everything emailSome other useful ContactsContract.CommonDataKinds subclasses include Phone, Photo, Website, Nickname, Organization, and StructuredPostal.
cursor = getContentResolver().query(
Email.CONTENT_URI, null,
Email.CONTACT_ID + "=?",
new String[]{id}, null);
Step 7: Viewing the Query Results
Certainly, you could read the class documentation for the ContactsContract.CommonDataKinds.Email class and determine what kind of results to expect. However, this is not always the case so let’s inspect the results of this call. This is a very handy trick if you are working with a content provider that has less-than-adequate documentation, or is not behaving as expected.This snippet of code will show you, via LogCat output, every column and value that is returned from the query to the content provider:
cursor.moveToFirst();Now you can see that, indeed, they really did mean for the email to come back via a column called DATA1, aliased to Email.DATA. The Android Contacts system is very flexible, and this sort of generic column name shows where some of that flexibility comes from. The email type, such as Home or Work, is found in Email.TYPE.
String columns[] = cursor.getColumnNames();
for (String column : columns) {
int index = cursor.getColumnIndex(column);
Log.v(DEBUG_TAG, "Column: " + column + " == ["
+ cursor.getString(index) + "]");
Step 8: Retrieving the Email
We have all of the data we need to actually get the email address, or addresses, of the contact picked by the user. When using database Cursors, we have to make sure they are internally referencing a data row we’re interested in, so we start with a call to the moveToFirst() method and make sure it was successful. For this tutorial, we won’t worry about multiple email addresses. Instead, we’ll just use the first result:if (cursor.moveToFirst()) {It’s important to remember that a contact may have many addresses. If you wanted to give the user the option of choosing from multiple email addresses, you could display your own email chooser to pick amongst these after the user has chosen a specific contact.
int emailIdx = cursor.getColumnIndex(Email.DATA);
email = cursor.getString(emailIdx);
Log.v(DEBUG_TAG, "Got email: " + email);
}
Step 9: Updating the Form
After all that work to get the email address, don’t forget to update the form. You might also consider informing the user if the contact didn’t have any email address listed.EditText emailEntry = (EditText)findViewById(R.id.invite_email);And there it is:
emailEntry.setText(email);
if (email.length() == 0) {
Toast.makeText(this, "No email found for contact.", Toast.LENGTH_LONG).show();
}
Step 10: Putting it All Together
We skipped over two important items in this tutorial that are worth mentioning now.First, we didn’t include any error checking; we did this for clarity, but in production code, this is an essential piece of the solution. An easy way to implement some checking would be to to wrap just about everything in a try-catch block.
Second, you need to remember that Cursor objects require management within your Activity lifecycle. Always remember to release Cursor objects when you are done using them.
Here’s the complete implementation of the onActivityResult() method to put these points in perspective:
@OverrideYou’ve now got everything you need to complete the application. Remember, though, that if you’re working with real data, take care not to spam your friends too much. ☺
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
switch (requestCode) {
case CONTACT_PICKER_RESULT:
Cursor cursor = null;
String email = "";
try {
Uri result = data.getData();
Log.v(DEBUG_TAG, "Got a contact result: "
+ result.toString());
// get the contact id from the Uri
String id = result.getLastPathSegment();
// query for everything email
cursor = getContentResolver().query(Email.CONTENT_URI,
null, Email.CONTACT_ID + "=?", new String[] { id },
null);
int emailIdx = cursor.getColumnIndex(Email.DATA);
// let's just get the first email
if (cursor.moveToFirst()) {
email = cursor.getString(emailIdx);
Log.v(DEBUG_TAG, "Got email: " + email);
} else {
Log.w(DEBUG_TAG, "No results");
}
} catch (Exception e) {
Log.e(DEBUG_TAG, "Failed to get email data", e);
} finally {
if (cursor != null) {
cursor.close();
}
EditText emailEntry = (EditText) findViewById(R.id.invite_email);
emailEntry.setText(email);
if (email.length() == 0) {
Toast.makeText(this, "No email found for contact.",
Toast.LENGTH_LONG).show();
}
}
break;
}
} else {
Log.w(DEBUG_TAG, "Warning: activity result not ok");
}
}
Comments
Post a Comment