Friday, April 27, 2012

Android - 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.


Source: Tips4all

5 comments:

  1. 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);
    }

    ReplyDelete
  2. Guys, I spent many hours attempting to figure this out. Here is a method I created that will get you your facebook photo by phone number (no dashes). You can, of course, modify it accordingly:

    public Bitmap getFacebookPhoto(String phoneNumber) {
    Uri phoneUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber));
    Uri photoUri = null;
    ContentResolver cr = this.getContentResolver();
    Cursor contact = cr.query(phoneUri,
    new String[] { ContactsContract.Contacts._ID }, null, null, null);

    if (contact.moveToFirst()) {
    long userId = contact.getLong(contact.getColumnIndex(ContactsContract.Contacts._ID));
    photoUri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, userId);

    }
    else {
    Bitmap defaultPhoto = BitmapFactory.decodeResource(getResources(), android.R.drawable.ic_menu_report_image);
    return defaultPhoto;
    }
    if (photoUri != null) {
    InputStream input = ContactsContract.Contacts.openContactPhotoInputStream(
    cr, photoUri);
    if (input != null) {
    return BitmapFactory.decodeStream(input);
    }
    } else {
    Bitmap defaultPhoto = BitmapFactory.decodeResource(getResources(), android.R.drawable.ic_menu_report_image);
    return defaultPhoto;
    }
    Bitmap defaultPhoto = BitmapFactory.decodeResource(getResources(), android.R.drawable.ic_menu_report_image);
    return defaultPhoto;
    }

    ReplyDelete
  3. 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.

    ReplyDelete
  4. None of these approaches worked for me. What did work was:

    String[] projection = new String[] {
    ContactsContract.Contacts.PHOTO_ID, ///< the correct ID for photo retrieval in loadLocalContactPhotoBytes()
    // ContactsContract.Contacts._ID,
    ContactsContract.Contacts.DISPLAY_NAME,
    ContactsContract.CommonDataKinds.Phone.NUMBER,
    // ContactsContract.CommonDataKinds.Photo.PHOTO
    };

    ContentResolver cr = ctx.getContentResolver();

    Cursor cursor = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
    // Cursor cursor = cr.query(ContactsContract.Contacts.CONTENT_URI,
    // new String[] {RawContacts._ID, RawContacts.ACCOUNT_TYPE, RawContacts.ACCOUNT_NAME},
    // new String[] {Contacts._ID},
    projection, null, null, ContactsContract.Contacts.DISPLAY_NAME + " ASC");

    ....


    //the 'cursor' above got passed as an argument below

    private byte[] loadLocalContactPhotoBytes(ContentResolver cr, Cursor cursor, byte[] defaultPhotoBytes)
    {
    byte[] photoBytes = null;// = cursor.getBlob(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Photo.PHOTO));

    // int id = cursor.getInt(cursor.getColumnIndex(ContactsContract.Contacts._ID));
    int id = cursor.getInt(cursor.getColumnIndex(ContactsContract.Contacts.PHOTO_ID));
    // Uri contactUri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, id);
    // Uri photoUri = Uri.withAppendedPath(contactUri, ContactsContract.Contacts.Photo.CONTENT_DIRECTORY);
    Uri photoUri = ContentUris.withAppendedId(ContactsContract.Data.CONTENT_URI, 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
    Log.w(_TAG, e.toString());

    } finally {

    c.close();
    }

    photoBytes = photoBytes == null ? defaultPhotoBytes : photoBytes;
    return photoBytes;
    }

    ReplyDelete
  5. It seems that my problem was because the contacts in my device were synced from facebook, and the photo therefore is not available.

    http://groups.google.com/group/android-developers/msg/be8d0cf3928e4b7f

    ReplyDelete