Guy Guy - 5 months ago 10
Java Question

Changing contact's image to a large photo via PHOTO_FILE_ID in Android

This seems to work for small images:

ContentValues values = new ContentValues();

values.put(ContactsContract.Data.RAW_CONTACT_ID, id);
values.put(ContactsContract.Data.IS_SUPER_PRIMARY, 1);
values.put(ContactsContract.CommonDataKinds.Photo.PHOTO, photo);
values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE);
if (photoRow >= 0) {
context.getContentResolver().update(ContactsContract.Data.CONTENT_URI, values, ContactsContract.Data._ID + " = " + photoRow, null);
} else {
context.getContentResolver().insert(ContactsContract.Data.CONTENT_URI, values);

From the docs I realise that for large images I need to set the PHOTO_FILE_ID, so I can replace:




However, then I need to supply a PHOTO_FILE_ID rather than raw data. My question:

  1. How do I save the photo (byte []) and get a PHOTO_FILE_ID?

  2. If there is already a photo available (PHOTO not PHOTO_FILE_ID). Do I need to delete it for the big image to be seen or does the big image take precedence, if not, how do I delete it?


Your own answer will work, but it's not very efficient because the photo needs to be encoded into an SQL query and piped through Android IPC. That also makes it a subject to Android's IPC size limit of 1MB (i.e. if your photo is too large the content provider operation will fail).

The most efficient way to set (create or override) a RawContact's (primary) photo is by using openAssetFileDescriptor and a ContactsContract.RawContacts.DisplayPhoto URI like so (example copied from Android docs):

public void writeDisplayPhoto(long rawContactId, byte[] photo) {
    Uri rawContactPhotoUri = Uri.withAppendedPath(
            ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
    try {
        AssetFileDescriptor fd =
            getContentResolver().openAssetFileDescriptor(rawContactPhotoUri, "rw");
        OutputStream os = fd.createOutputStream();
    } catch (IOException e) {
        // Handle error cases.

The only drawback of this approach is that it always creates/replaces the primary photo of the RawContact. If the RawContact doesn't have a photo yet this will add one.

Unfortunately there is no way to use openAssetFileDescriptor with a PHOTO_FILE_ID, so you can't override a specific photo identified by its ID using this method. However, in real life most contacts probably have at most one photo, so that's not a real limitation.

This will automatically update the Photo.PHOTO column with a thumbnail of the large photo and assign a PHOTO_FILE_ID.