Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I load a contact Photo?

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.

like image 845
PaulH Avatar asked Mar 04 '10 23:03

PaulH


3 Answers

This works for me:

public static Bitmap loadContactPhoto(ContentResolver cr, long  id) {
    Uri uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, id);
    InputStream input = ContactsContract.Contacts.openContactPhotoInputStream(cr, uri);
    if (input == null) {
        return null;
    }
    return BitmapFactory.decodeStream(input);
}
like image 179
wrongmissle Avatar answered Nov 20 '22 00:11

wrongmissle


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.

like image 22
Phil Haigh Avatar answered Nov 20 '22 01:11

Phil Haigh


After a lot of debugging nights I discover that the best approach is to use the contact id and if it fails to use the photo id.

public static Bitmap loadContactPhoto(ContentResolver cr, long  id,long photo_id) 
{

    Uri uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, id);
    InputStream input = ContactsContract.Contacts.openContactPhotoInputStream(cr, uri);
    if (input != null) 
    {
        return BitmapFactory.decodeStream(input);
    }
    else
    {
        Log.d("PHOTO","first try failed to load photo");

    }

    byte[] photoBytes = null;

    Uri photoUri = ContentUris.withAppendedId(ContactsContract.Data.CONTENT_URI, photo_id);

    Cursor c = cr.query(photoUri, new String[] {ContactsContract.CommonDataKinds.Photo.PHOTO}, null, null, null);

    try 
    {
        if (c.moveToFirst()) 
            photoBytes = c.getBlob(0);

    } catch (Exception e) {
        // TODO: handle exception
        e.printStackTrace();

    } finally {

        c.close();
    }           

    if (photoBytes != null)
        return BitmapFactory.decodeByteArray(photoBytes,0,photoBytes.length);
    else
        Log.d("PHOTO","second try also failed");
    return null;
}

the code tested on emulator and Nexus S device and seems to work.

like image 17
assaf passal Avatar answered Nov 20 '22 01:11

assaf passal