Thursday, 25 December 2014

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.
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.
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:
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:
Using 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;
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);
}
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.
Once launched, the contacts picker in your application will look something like this:
Using Contact Picker
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) {
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");
}
}
You’ll get a result other than RESULT_OK if the user cancels the operation or if something else goes wrong.
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();
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());
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.
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();
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 email
cursor = getContentResolver().query(
Email.CONTENT_URI, null,
Email.CONTACT_ID + "=?",
new String[]{id}, null);
Some other useful ContactsContract.CommonDataKinds subclasses include Phone, Photo, Website, Nickname, Organization, and StructuredPostal.
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();
String columns[] = cursor.getColumnNames();
for (String column : columns) {
int index = cursor.getColumnIndex(column);
Log.v(DEBUG_TAG, "Column: " + column + " == ["
+ cursor.getString(index) + "]");
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.
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()) {
int emailIdx = cursor.getColumnIndex(Email.DATA);
email = cursor.getString(emailIdx);
Log.v(DEBUG_TAG, "Got email: " + email);
}
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.
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);
emailEntry.setText(email);
if (email.length() == 0) {
Toast.makeText(this, "No email found for contact.", Toast.LENGTH_LONG).show();
}
And there it is:
Content Picker
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:
@Override
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");
}
}
You’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. ☺
In this tutorial, you’ve learned how to launch the Contacts picker and retrieve the chosen result. You also learned how to inspect the results and retrieve the email address for the picked contact using the contacts content provider. You can use this method to retrieve all sorts of information about a given contact.

No comments:

Post a Comment