In my application i want to show a list with my contacts and their phone number of each one. So i get the contacts from the ContentProvider and then all the numbers of the phone and then i match them by the contact id. code is the following
fun Context.retrieveAllContacts(): List<ContactData> {
val result: MutableList<ContactData> = mutableListOf()
contentResolver.query(
ContactsContract.Contacts.CONTENT_URI,
CONTACT_PROJECTION,
null,
null,
ContactsContract.Contacts.DISPLAY_NAME_PRIMARY + " ASC"
)?.use {
if (it.moveToFirst()) {
do {
val contactId = it.getString(it.getColumnIndex(CONTACT_PROJECTION[0]))
val firstName = it.getString(it.getColumnIndex(CONTACT_PROJECTION[2])) ?: ""
val lastName = it.getString(it.getColumnIndex(CONTACT_PROJECTION[3])) ?: ""
val initials = getInitials(firstName, lastName)
result.add(ContactData(contactId, firstName, initials, mutableListOf(), null))
} while (it.moveToNext())
}
}
return result
}
fun Context.retrieveAllContactNumbers(): HashMap<String, ArrayList<String>> {
val contactsNumberMap = HashMap<String, ArrayList<String>>()
val phoneCursor: Cursor? = contentResolver.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
null,
null,
null
)
if (phoneCursor != null && phoneCursor.count > 0) {
val contactIdIndex =
phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.CONTACT_ID)
val numberIndex = phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)
while (phoneCursor.moveToNext()) {
val contactId = phoneCursor.getString(contactIdIndex)
val number: String = phoneCursor.getString(numberIndex)
// check if the map contains key or not, if not then create a new array list with number
if (contactsNumberMap.containsKey(contactId)) {
contactsNumberMap[contactId]?.add(number)
} else {
contactsNumberMap[contactId] = arrayListOf(number)
}
}
// contact contains all the number of a particular contact
phoneCursor.close()
}
return contactsNumberMap
}
And after that i run a loop to make the correspondence
val contactsListAsync = async { context?.retrieveAllContacts() ?: emptyList() }
val contacts = contactsListAsync.await()
val contactNumbersAsync = async { requireActivity().retrieveAllContactNumbers() }
val contactNumbers = contactNumbersAsync.await()
contacts.forEach { contactData ->
contactNumbers[contactData.contactId]?.let { numbers ->
numbers.forEach {
if (isValidIrisCellphoneNumber(PhoneNumberUtils.normalizeNumber(it)))
contactData.phoneNumbers.add(it)
}
}
}
As i can see many contacts have the same number 2 or 3 or evan 4 times. I guess the "problem" comes from the retrieveAllContactNumbers() which fetchs numbers from ContactsContract.CommonDataKinds.Phone. Mostly, i want to know why is this happening and then how can i prevent it?
I also noticed this: I added a fake contact and i saw that in my list with 2 numbers. Perhaps it has to do with from where the contentResolver gets the numbers? For example Google account, SIM card, Internal storage...? I dont know...
That is expected by the way the Contacts Database is organized:
Contacts- each entry represents one contact, and groups together one or moreRawContactsRawContacts- each entry represents data about a contact that was synced in by someSyncAdapter(e.g. Whatsapp, Google, Facebook, Viber), this groups multiple Data entriesData- The actual data about a contact, emails, phones, etc. each line is a single piece of data that belongs to a singleRawContactYou are querying phones directly from the
Datatable, as you should, so you may get the same phone number from differentRawContactsrepresenting the sameContact.So for example, if you have Whatsapp installed on your device, and Google is syncing your contacts, the same phone number can and will be stored by 2 RawContacts per contact - one for each app.
The easy solution would be to change your Map to:
The
Setwill enforce uniqueness over the list of phones per contact.