0
votes

I would like to build a custom ListView of information from contacts in Android.

The Android docs layout a really simple example here: https://developer.android.com/training/contacts-provider/retrieve-names#Permissions

It grabs the display name from your contacts and shows it in the list view. The projection given to the CursorLoader is:

private val PROJECTION: Array<out String> = arrayOf(
    ContactsContract.Contacts._ID,
    ContactsContract.Contacts.LOOKUP_KEY,
    ContactsContract.Contacts.DISPLAY_NAME
)

Then map to the simple cursor:

private val FROM_COLUMNS: Array<String> = arrayOf(
    ContactsContract.Contacts.DISPLAY_NAME
)

I would like to also add an email address to the listview. Is this possible with a the SimpleCursorAdapter that the documentation uses?

I tried to change the projection to project the email address as well:

private val PROJECTION: Array<out String> = arrayOf(
    ContactsContract.Contacts._ID,
    ContactsContract.Contacts.LOOKUP_KEY,
    ContactsContract.Contacts.DISPLAY_NAME,
    ContactsContract.CommonDataKinds.Email.DISPLAY_NAME
)

However when I do that I get an exception:

java.lang.IllegalArgumentException: Invalid column data4

I realize that the mapping from contacts to email(s) is one to many. Is that my problem? Can I project the "Main" email address?

1

1 Answers

0
votes

I don't know if SimpleCursorAdapter can do this as I didn't use it, so let me just tell you how the e-mail addresses can be loaded with a simple query.

I used the following method to load both e-mails and phone numbers at the same time so the code might not be the optimal one.

First of all, the e-mail addresses are stored in a generic ContactsContract.Data.DATA1 data column, but the same column may also be used for other data types (e.g. for the phone numbers). That's why I used the following projection:

private val PROJECTION =
    arrayOf(
        ContactsContract.Data.CONTACT_ID,
        ContactsContract.Data.DISPLAY_NAME_PRIMARY,
        ContactsContract.Data.DATA1,
        ContactsContract.Data.MIMETYPE
    )

And then I queried it like below and processed the data one by one, checking the data1 MIME type each time:

contentResolver.query(
    ContactsContract.Data.CONTENT_URI,
    PROJECTION,
    null,
    null,
    null
)?.use { cursor ->
    val idxId = cursor.getColumnIndex(ContactsContract.Data.CONTACT_ID)
    val idxDisplayNamePrimary = cursor.getColumnIndex(ContactsContract.Data.DISPLAY_NAME_PRIMARY)
    val idxData1 = cursor.getColumnIndex(ContactsContract.Data.DATA1)
    val idxMimeType = cursor.getColumnIndex(ContactsContract.Data.MIMETYPE)

    while (cursor.moveToNext()) {
        val id = cursor.getLong(idxId)
        val name: String? = cursor.getString(idxDisplayNamePrimary)
        val data1: String? = cursor.getString(idxData1)
        val mimeType: String? = cursor.getString(idxMimeType)

        if (mimeType == ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) {
            // e-mail was found and loaded into data1 - do something with it here
        }
    }
}

I'm pretty sure there might be a better way though.

Update #1

Some docs here:

Update #2

As you can see in the docs or in the code, ContactsContract.CommonDataKinds.Email.ADDRESS points to the ContactsContract.Data.DATA1 column.