Search code examples
androiduriandroid-filecontactscontract

Function to update a photo in ContactsContract


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;
    }

Solution

  • 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;
    }