Persistant contact id

421 Views Asked by At

I know on some phones contact ids change even on reboot, but I can't reproduce this on my own phone. But I can reproduce following case and would like to know if this case is solveable at least:

  • install whatsapp - it will add it's contacts to the ContentProvider
  • read contacts from ContactProvider => I get data set 1
  • delete whatsapp, reinstall it - it will add it's contacts to the ContentProvider again
  • read contacts from ContentProvider => I get data set 2

Result:

Between data set 1 and data set 2 I don't see any consistent data.

E.g.:

ContactsContract.Data.CONTACT_ID => changes, e.g. from 598 to 679 ContactsContract.Data.LOOKUP_KEY => changes as well, e.g. 3514i2b4948808eec75c9.3789r593-2D374B39.2797r594-2D374B39 to 3514i2b4948808eec75c9.2797r603-2D374B39.3789r670-2D374B39 * of course, some raw ids stay the same, but no common id for a full contact stays the same as far as I can see

What I want

I need an identifier for a contact that I can persistent in my app's database and that will also work after a change like the one described above. I need an identifier for a contact (not a raw contact), raw contacts may change of course during the lifetime of a contact (like in the example I describe above)...

Any ideas how I can solve this problem?

Code

For the sake of completeness, here's how I query the data from the ContentProvider, at least the cursor:

 private fun getCursor(offset: Int?, count: Int?): Cursor? {
    val selection = arrayOf(
            ContactsContract.Data.RAW_CONTACT_ID,
            ContactsContract.Data.CONTACT_ID,
            ContactsContract.Data.LOOKUP_KEY,
            ContactsContract.Data.PHOTO_ID,
            ContactsContract.Data.DISPLAY_NAME,
            ContactsContract.Data.ACCOUNT_TYPE_AND_DATA_SET,
            ContactsContract.CommonDataKinds.Email.DATA,
            ContactsContract.CommonDataKinds.Phone.NUMBER,
            ContactsContract.CommonDataKinds.Phone.TYPE,
            ContactsContract.CommonDataKinds.Phone.LABEL,
            ContactsContract.Contacts.Data.MIMETYPE,
            ContactsContract.RawContacts.ACCOUNT_NAME,
            ContactsContract.CommonDataKinds.Phone.IS_PRIMARY,
            ContactsContract.Data.CONTACT_LAST_UPDATED_TIMESTAMP,
            ContactsContract.RawContacts.SOURCE_ID
    )

    return AppProvider.get().context.contentResolver.query(
            ContactsContract.Data.CONTENT_URI,
            selection, null, null,
            ContactsContract.Data.CONTACT_ID + " ASC" + if (offset != null && count != null) " limit $count offset $offset" else "")
}
1

There are 1 best solutions below

7
marmor On

LOOKUP_KEY is your friend.

LOOKUP_KEY

An opaque value that contains hints on how to find the contact if its row id changed as a result of a sync or aggregation.

So it's not a stable ID on its own, but it helps the ContactsContract DB to figure out the new contactID in case it changed.

You should use a pair of <CONTACT_ID, LOOKUP_KEY> to keep track of contacts. In normal use, use the CONTACT_ID value, but if your code gets a hint that the CONTACT_ID has changed (either missing, or unexpected contact name), you can use the LOOKUP_KEY to find the new contact-id.

You can use Contacts.getLookupUri(long, String) to get a URI you can always use to quickly find a contact no matter what its CONTACT_ID or LOOKUP_KEY actual values are.