How do I load a contact Photo?
android, contactscontract
Solution
Having scanned the many questions and answers to the problem of displaying a thumbnail I thought I would post my solution to this particular conundrum as I could only find a couple that worked at all and none that provided a good canned solution for the lazy developer.
The class below takes a Context, QuickContactBadge and a telephone number and will attach a locally stored image to the badge if there is one available for the specified phone number.
Here's the class:
public final class QuickContactHelper {
private static final String[] PHOTO_ID_PROJECTION = new String[] {
ContactsContract.Contacts.PHOTO_ID
};
private static final String[] PHOTO_BITMAP_PROJECTION = new String[] {
ContactsContract.CommonDataKinds.Photo.PHOTO
};
private final QuickContactBadge badge;
private final String phoneNumber;
private final ContentResolver contentResolver;
public QuickContactHelper(final Context context, final QuickContactBadge badge, final String phoneNumber) {
this.badge = badge;
this.phoneNumber = phoneNumber;
contentResolver = context.getContentResolver();
}
public void addThumbnail() {
final Integer thumbnailId = fetchThumbnailId();
if (thumbnailId != null) {
final Bitmap thumbnail = fetchThumbnail(thumbnailId);
if (thumbnail != null) {
badge.setImageBitmap(thumbnail);
}
}
}
private Integer fetchThumbnailId() {
final Uri uri = Uri.withAppendedPath(ContactsContract.CommonDataKinds.Phone.CONTENT_FILTER_URI, Uri.encode(phoneNumber));
final Cursor cursor = contentResolver.query(uri, PHOTO_ID_PROJECTION, null, null, ContactsContract.Contacts.DISPLAY_NAME + " ASC");
try {
Integer thumbnailId = null;
if (cursor.moveToFirst()) {
thumbnailId = cursor.getInt(cursor.getColumnIndex(ContactsContract.Contacts.PHOTO_ID));
}
return thumbnailId;
}
finally {
cursor.close();
}
}
final Bitmap fetchThumbnail(final int thumbnailId) {
final Uri uri = ContentUris.withAppendedId(ContactsContract.Data.CONTENT_URI, thumbnailId);
final Cursor cursor = contentResolver.query(uri, PHOTO_BITMAP_PROJECTION, null, null, null);
try {
Bitmap thumbnail = null;
if (cursor.moveToFirst()) {
final byte[] thumbnailBytes = cursor.getBlob(0);
if (thumbnailBytes != null) {
thumbnail = BitmapFactory.decodeByteArray(thumbnailBytes, 0, thumbnailBytes.length);
}
}
return thumbnail;
}
finally {
cursor.close();
}
}
}
And here's a typical use case inside an activity:
String phoneNumber = "...";
QuickContactBadge badge = (QuickContactBadge) view.findViewById(R.id.friend);
new QuickContactHelper(this, badge, phoneNumber).addThumbnail();
In a fragment it will be slightly different:
String phoneNumber = "...";
QuickContactBadge badge = (QuickContactBadge) view.findViewById(R.id.friend);
new QuickContactHelper(getActivity(), badge, phoneNumber).addThumbnail();
Now there are ways to be more efficient - for example if you are rendering a message timeline you'd want to re-use the same bitmap object for every badge instance for a given phone number instead of constantly creating new helper class instances and re-retrieving the bitmap - but my purpose here was to post a solution that is stripped down to the absolute minimum for clarity whilst at the same time providing a complete and usable solution out of the box. This solution has been built and tested on Andriod 4.0, and tested on 4.1 as well.
Problem
I'm having trouble loading a photo for a contact in Android. I've googled for an answer, but so far have come up empty. Does anyone have an example of querying for a Contact, then loading the Photo? So, given a contactUri which comes from an Activity result called using ``` startActivityForResult(new Intent(Intent.ACTION_PICK,ContactsContract.CommonDataKinds.Phone.CONTENT_URI),PICK_CONTACT_REQUEST) ``` is: content://com.android.contacts/data/1557 The loadContact(..) works fine. However when I call the getPhoto(...) method, I get a null value for the photo InputStream. It is also confusing because the URI values are different. The contactPhotoUri evaluates to: content://com.android.contacts/contacts/1557 See the comments inline in the code below. ``` class ContactAccessor { /** * Retrieves the contact information. */ public ContactInfo loadContact(ContentResolver contentResolver, Uri contactUri) { //contactUri --> content://com.android.contacts/data/1557 ContactInfo contactInfo = new ContactInfo(); // Load the display name for the specified person Cursor cursor = contentResolver.query(contactUri, new String[]{Contacts._ID, Contacts.DISPLAY_NAME, Phone.NUMBER, Contacts.PHOTO_ID}, null, null, null); try { if (cursor.moveToFirst()) { contactInfo.setId(cursor.getLong(0)); contactInfo.setDisplayName(cursor.getString(1)); contactInfo.setPhoneNumber(cursor.getString(2)); } } finally { cursor.close(); } return contactInfo; // <-- returns info for contact } public Bitmap getPhoto(ContentResolver contentResolver, Long contactId) { Uri contactPhotoUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); // contactPhotoUri --> content://com.android.contacts/contacts/1557 InputStream photoDataStream = Contacts.openContactPhotoInputStream(contentResolver,contactPhotoUri); // <-- always null Bitmap photo = BitmapFactory.decodeStream(photoDataStream); return photo; } public class ContactInfo { private long id; private String displayName; private String phoneNumber; private Uri photoUri; public void setDisplayName(String displayName) { this.displayName = displayName; } public String getDisplayName() { return displayName; } public void setPhoneNumber(String phoneNumber) { this.phoneNumber = phoneNumber; } public String getPhoneNumber() { return phoneNumber; } public Uri getPhotoUri() { return this.photoUri; } public void setPhotoUri(Uri photoUri) { this.photoUri = photoUri; } public long getId() { return this.id; } public void setId(long id) { this.id = id; } } } ``` Clearly, I'm doing something wrong here, but I can't seem to figure out what the problem is. Thanks.