Better way to get contact details via content provider in Android
Generally, to get the mobile numbers, name and email address, you need to make a different query to a content resolver in Android. You get data from different queries; merge all those together to put the data in a single collection and show it on UI.
Multiple queries are performance hit as DB query involves connect, query and disconnect so fewer the queries, better the performance.
Old Approach :
- Get the list of contacts to contactCursor from Contacts.CONTENT_URI
- Get all phone numbers to phoneCursor from Phone.CONTENT_ITEM_TYPE
- Get all emails to emailCursor from Email.CONTENT_ITEM_TYPE
- Link: https://gist.github.com/srayhunter/47ab2816b01f0b00b79150150feb2eb2
New, Better and Optimized Approach :
I used the Rx Java approach to fetch the contact details on the separate observable.
Declare variables and constants
private const val DISPLAY_NAME = ContactsContract.Contacts.DISPLAY_NAME_PRIMARY
private val PROJECTION = arrayOf(
ContactsContract.Data.CONTACT_ID,
ContactsContract.Contacts.HAS_PHONE_NUMBER,
ContactsContract.Data.DISPLAY_NAME,
ContactsContract.Data.DATA1,
ContactsContract.Data.MIMETYPE
)
private const val ORDER = DISPLAY_NAME
private const val selection = ContactsContract.Data.MIMETYPE + " = ?" +
" OR " + ContactsContract.Data.MIMETYPE + " = ?" + " OR " + ContactsContract.Data.MIMETYPE + " = ?"
private val selectionArgs = arrayOf(
"%" + "@" + "%",
ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE,
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
)
and call the below code on separate observable or on a separate thread
val userContactDetailLinkedHashMap: LinkedHashMap<Int, UserContactDetail?> = LinkedHashMap()
try {
val cr: ContentResolver = context.getContentResolver()
val cur: Cursor = cr.query(
ContactsContract.Data.CONTENT_URI,
PROJECTION,
selection,
selectionArgs,
ORDER
)
if (cur != null && cur.getCount()>0 && cur.moveToFirst()) {
do {
val hasPhone: Int =
cur.getInt(cur.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))
val contactId: Int =
cur.getInt(cur.getColumnIndex(ContactsContract.Data.CONTACT_ID))
val name: String =
cur.getString(cur.getColumnIndex(ContactsContract.Data.DISPLAY_NAME_PRIMARY))
val emailOrMobile: String? =
cur.getString(cur.getColumnIndex(ContactsContract.Data.DATA1))
if (hasPhone > 0 && emailOrMobile != null) {
var contactDetail: UserContactDetail?
if (!userContactDetailLinkedHashMap.containsKey(contactId)) {
contactDetail = UserContactDetail()
if (!CommonUtil.isEmailValid(emailOrMobile)) {
contactDetail.setPhoneNumber(Collections.singletonList(emailOrMobile))
} else {
contactDetail.setEmailId(Collections.singletonList(emailOrMobile))
}
contactDetail.setName(name)
userContactDetailLinkedHashMap[contactId] = contactDetail
} else {
contactDetail = userContactDetailLinkedHashMap[contactId]
if (contactDetail == null) continue
if (!CommonUtil.isEmailValid(emailOrMobile)) {
contactDetail.setPhoneNumber(Collections.singletonList(emailOrMobile))
} else {
contactDetail.setEmailId(Collections.singletonList(emailOrMobile))
}
contactDetail.setName(name)
userContactDetailLinkedHashMap[contactId] = contactDetail
}
}
} while (cur.moveToNext())
}
if (cur != null) {
cur.close()
}
} catch (e: Exception) {
Crashlytics.getInstance().core.logException(e)
}
Here the UserContactDetail have
class UserContactDetail {
var name: String? = null
var emailId: List<String> = ArrayList()
var phoneNumber: List<String> = ArrayList()
}
That's it. LinkedHashMap userContactDetailLinkedHashMap will have the final user details along with name, emails and mobile numbers.