1

I try to update a photo in ContactsContract with a function who take the id of the contact and the uri of the picture but it seem it's not working (and my function return true).

I really don't understand because the code look good.

It seem it's working when the contact have already a photo...

This is my function :

boolean updatePhoto(String idStr, String uri){
        if (uri != null) {
            ArrayList<ContentProviderOperation> ops = new ArrayList<>();

            File imgFile = new File(uri.replace("file://", ""));
            if (imgFile.exists()) {

                Bitmap myBitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath());
                ByteArrayOutputStream stream = new ByteArrayOutputStream();
                myBitmap.compress(Bitmap.CompressFormat.JPEG, 75, stream);
                ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
                        .withSelection(ContactsContract.Data.CONTACT_ID + " = ?" + " AND " + ContactsContract.Data.MIMETYPE + "=?",
                                new String[]{idStr, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE})
                        .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
                        .withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, stream.toByteArray())
                        .build());

                try {
                    getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
                } catch(Exception e) {
                    e.printStackTrace();
                    return false;
                }
            }
        }
        return true;
    }
Jéwôm'
  • 3,753
  • 5
  • 40
  • 73

1 Answers1

1

Your code only works if the contact already has a photo because your using ContentProviderOperation.newUpdate, if the contact doesn't have a photo you'll need to use ContentProviderOperation.newInsert.

You need to first query the contact to see if it has a photo, and then update/insert the new photo:

private boolean hasPhoto(long contactId) {
    Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
    InputStream input = Contacts.openContactPhotoInputStream(getContentResolver(), uri);
    if (input == null) {
        return false;
    }
    Bitmap photo = BitmapFactory.decodeStream(input);
    return (photo != null);
}

private long getRawId(long contactId) {
    String selection = RawContacts.CONTACT_ID + "='" + contactId + "'";
    Cursor cur = contentResolver.query(RawContacts.CONTENT_URI, new String[]{ RawContacts._ID }, selection, null, null);
    try {
        if (cur.moveToNext()) {
            return cur.getLong(0);
        }
    } finally {
        cur.close();
    }
    return 0;
}

private boolean updatePhoto(long contactId, String uri) {
    if (uri == null) {
        // do nothing?
        return false;
    }

    ContentProviderOperation.Builder builder;
    if (hasPhoto(contactId)) {
        builder = ContentProviderOperation.newUpdate(Data.CONTENT_URI);
        builder.withSelection(Data.CONTACT_ID + " = ?" + " AND " + Data.MIMETYPE + "=?",
                    new String[]{ String.valueOf(contactId), Photo.CONTENT_ITEM_TYPE});
    } else {
        long rawId = getRawId(contactId);
        builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
        builder.withValue(Data.RAW_CONTACT_ID, rawId);
    }

    builder.withValue(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
    builder.withValue(Data.IS_SUPER_PRIMARY, 1);
    builder.withValue(Data.IS_PRIMARY, 1);

    byte[] photo = getPhotoAsByteArray(uri); // to simplify the answer's code
    builder.withValue(Photo.PHOTO, photo);

    try {
        ArrayList<ContentProviderOperation> ops = new ArrayList<>();
        ops.add(builder.build());
        getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
    } catch(Exception e) {
        e.printStackTrace();
        return false;
    }
    return true;
}
marmor
  • 27,641
  • 11
  • 107
  • 150
  • thanks but I have the same problem. it's not working when my contact don't have a photo. And it's working when the contact have a photo. – Jéwôm' Dec 05 '17 at 19:16
  • I haven't tested the hasPhoto method, is that working properly for you, does it goes into the newInsert clause? – marmor Dec 05 '17 at 19:43
  • the hasPhoto() method work fine so it goes into the newInsert clause. – Jéwôm' Dec 06 '17 at 07:46
  • of course... you can't add `Data` rows to a `Contact`, you need to specify a `RawContact` that will contain that data row - a Contact is an aggregation of one or more RawContacts, a RawContact is an aggregation of one or more Data rows – marmor Dec 06 '17 at 09:35
  • see my updated answer, also added `IS_SUPER_PRIMARY` to make this photo "win" other photos this contact may have in other RawContacts – marmor Dec 06 '17 at 09:44
  • thanks but I can't find the method getRowId(long). How can I use it ? – Jéwôm' Dec 06 '17 at 10:36
  • it's `getRawId` not `getRowId`, and the code for it is in my answer as well, above the main method – marmor Dec 06 '17 at 10:52
  • oh sorry I didn't see that – Jéwôm' Dec 06 '17 at 11:08
  • it's the same now. When the contact have a photo, it work but when not, I have this error : android.database.sqlite.SQLiteException: unknown error (code 0): Unable to convert BLOB to string – Jéwôm' Dec 06 '17 at 11:10
  • 1
    I found the solution : I don't have to use builder.withValue(Data.CONTACT_ID, contactId); – Jéwôm' Dec 06 '17 at 18:03