Search code examples
androidandroid-contacts

Save data for contacts adds a new one


I developing an app in which I need to store data related to a contact.

I list the contacts in a recycleView using a cursor and each itemView has a star button to set the contact as favorite (not the same as the system).

I managed to store the data in ContactsContract.Data, doing this:

private void addContactData(long contactId, String displayName, boolean favorite) {

    // displayName same as the value for Contacts.DISPLAY_NAME_PRIMARY

    ArrayList<ContentProviderOperation> ops =
            new ArrayList<>();

    ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
            .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, CUSTOM_ACCOUNT_TYPE)
            .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, CUSTOM_ACCOUNT_NAME)
            .withValue(ContactsContract.RawContacts.CONTACT_ID, contactId)
            .build());

    ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
            .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
            .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, displayName)
            .build());

    ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
            .withValue(ContactsContract.Data.MIMETYPE, CustomData.CONTENT_ITEM_TYPE)
            .withValue(CustomData.IS_FAVORITE, favorite)
            .build());

    try {
        ContentProviderResult[] contentProviderResults = cr.applyBatch(ContactsContract.AUTHORITY, ops);
        return contactUri;
    } catch (RemoteException | OperationApplicationException e) {
        Log.e(getClass().getSimpleName(), e.getMessage(), e);
        return null;
    }
}

The problem I have is when two contacts have the exact same name ej. "Tom" and "Tom" if I press the fav button, is adding a third contact that is even listed in the device contacts app.

Some search guied me to add more data to distinguish the contacts using:

ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
            .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
            .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
            .build());
// OR

ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
            .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
            .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email)
            .build());

And it worked just fine, except if the fields are equals in both contacts. What do I need to avoid this behavior, and Why the CONTACT_ID isn't enough to do this kind of operations?


Solution

  • You need to tell Android to merge the new RawContact you've just created with some (one or more) existing RawContacts.

    You do that using the AggregationException table, adding a row for each such "link".

    See: https://stackoverflow.com/a/40869351/819355

    Code snippet:

    Builder builder = ContentProviderOperation.newUpdate(AggregationExceptions.CONTENT_URI);
    builder.withValue(AggregationExceptions.TYPE, AggregationExceptions.TYPE_KEEP_TOGETHER);
    builder.withValue(AggregationExceptions.RAW_CONTACT_ID1, yourNewRawContact);
    builder.withValue(AggregationExceptions.RAW_CONTACT_ID2, someExistingRawContact);
    ContentProviderOperation op = builder.build();