1

I'm trying to get some information about a contact that the user can select in the contact list, using Kotlin. I tried a couple tutorials that only worked partly. This is the code that should call startActivityForResult(), from MainActivity.kt:

add_contact.setOnClickListener {
    val intent = Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI)
    startActivityForResult(intent, CONTACT_PICK_CODE)
}

And the following is the overridden onActivityResult method:

@SuppressLint("Range")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)

    // handle intent results || calls when user from Intent (Contact Pick) picks or cancels pick contact
    if (resultCode == RESULT_OK) {
        if (requestCode == CONTACT_PICK_CODE) {
            val cursor1: Cursor
            val cursor2: Cursor?

            // get data from intent
            val uri = data!!.data
            cursor1 = contentResolver.query(uri!!, null, null, null, null)!!
            if (cursor1.moveToFirst()) {
                // get contact details
                val contactId = cursor1.getString(cursor1.getColumnIndex(ContactsContract.Contacts._ID))
                val contactName = cursor1.getString(cursor1.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME))
                val contactThumbnail = cursor1.getString(cursor1.getColumnIndex(ContactsContract.Contacts.PHOTO_THUMBNAIL_URI))
                val idResults = cursor1.getString(cursor1.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))
                val idResultHold = idResults.toInt()
                // check if contact has a phone number or not
                if (idResultHold == 1) {
                    cursor2 = contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_FILTER_URI,
                    null,
                    ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " + contactId,
                    null,
                    null
                    )
                    // a contact may have multiple phone numbers
                    var contactNumber : String = ""
                    while (cursor2!!.moveToNext()) {
                        // get phone number
                        contactNumber = cursor2.getString(cursor2.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))
                    }
                    Toast.makeText(this, "Contact number: $contactNumber", Toast.LENGTH_SHORT).show()
                    cursor2.close()
                }
                cursor1.close()
            }
        }
    } else {
        Toast.makeText(this, "Cancelled", Toast.LENGTH_SHORT).show()
    }
}

The line that prints "Contact number: ..." never prints the contact number, which means that the while loop never runs.

All the other variables (contactId, contactName...) seem to be assigned the correct value every time, but the number doesn't. Does anyone know what causes that to happen?

Enrico Cortinovis
  • 811
  • 3
  • 8
  • 31

2 Answers2

1

Try this to get multiple phone numbers from a contact

const val REQUEST_SELECT_PHONE_NUMBER = 1

fun selectContact() {
    // Start an activity for the user to pick a phone number from contacts
    val intent = Intent(Intent.ACTION_PICK).apply {
        type = CommonDataKinds.Phone.CONTENT_TYPE
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivityForResult(intent, REQUEST_SELECT_PHONE_NUMBER)
    }
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
    if (requestCode == REQUEST_SELECT_PHONE_NUMBER && resultCode == Activity.RESULT_OK) {
        // Get the URI and query the content provider for the phone number
        val contactUri: Uri = data.data
        val projection: Array<String> = arrayOf(CommonDataKinds.Phone.NUMBER)
        contentResolver.query(contactUri, projection, null, null, null).use { cursor ->
            // If the cursor returned is valid, get the phone number
            if (cursor.moveToFirst()) {
                val numberIndex = cursor.getColumnIndex(CommonDataKinds.Phone.NUMBER)
                val number = cursor.getString(numberIndex)
                // Do something with the phone number
                ...
            }

         val c: Cursor =  managedQuery(contactUri, null, null, null, null)
         val name = c.getString(c.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME))

        }
    }
}
user16930239
  • 6,319
  • 2
  • 9
  • 33
  • Thank you! This works. I am trying to get the contact name as well though (as in the starting code I had), with `val contactName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME))` right after the line of code where you get the contactNumber, but it does not seem to work. In Logcat it says the error is at that line, and says *Make sure the Cursor is initialized correctly before accessing data from it.* Could you explain why that doesn't work? – Enrico Cortinovis Oct 02 '21 at 04:42
  • I think you need new cursor, (I've updated the code above. Also check out this [answer](https://stackoverflow.com/questions/9496350/pick-a-number-and-name-from-contacts-list-in-android-app) – user16930239 Oct 02 '21 at 22:01
  • I tried that but it doesn't seem to work. It gives an error for the line `val name = ...`, saying *Caused by: android.database.CursorIndexOutOfBoundsException: Index -1 requested, with a size 1*. The app crashes whenever it reaches that line of code. Am I missing something or do you know what could be causing that? – Enrico Cortinovis Oct 03 '21 at 18:57
1
cursor2 = > contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_FILTER_URI, ...

To return the phone numbers you need to replace the ContactsContract.CommonDataKinds.Phone.CONTENT_FILTER_URI Uri with ContactsContract.CommonDataKinds.Phone.CONTENT_URI

Instead of using a String, you can use a Set to have no duplicate numbers, because; this URI returns all the links to the phone numbers besides the phone storage; like Google, WhatsApp, .. etc.

@SuppressLint("Range")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)

    // handle intent results || calls when user from Intent (Contact Pick) picks or cancels pick contact
    if (resultCode == RESULT_OK) {
        if (requestCode == CONTACT_PICK_CODE) {
            val cursor1: Cursor
            val cursor2: Cursor?

            // get data from intent
            val uri = data!!.data
            cursor1 = contentResolver.query(uri!!, null, null, null, null)!!

            if (cursor1.moveToFirst()) {
                // get contact details
                val contactId =
                    cursor1.getString(cursor1.getColumnIndex(ContactsContract.Contacts._ID))
                val contactName =
                    cursor1.getString(cursor1.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME))
                val contactThumbnail =
                    cursor1.getString(cursor1.getColumnIndex(ContactsContract.Contacts.PHOTO_THUMBNAIL_URI))
                val idResults =
                    cursor1.getString(cursor1.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))
                val idResultHold = idResults.toInt()
                // check if contact has a phone number or not
                if (idResultHold == 1) {
                    cursor2 = contentResolver.query(
//                            ContactsContract.CommonDataKinds.Phone.CONTENT_FILTER_URI, // WRONG
                        ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                        null,
                        ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " + contactId,
                        null,
                        null
                    )

                    // a contact may have multiple phone numbers
                    val numbers = mutableSetOf<String>()
                    cursor2?.let {
                        while (cursor2.moveToNext()) {
                            val phoneNumber =
                                cursor2.getString(
                                    cursor2.getColumnIndex(
                                        ContactsContract.CommonDataKinds.Phone.NUMBER
                                    )
                                ).replace("-", "").replace(
                                    " ",
                                    ""
                                )  // Remove the dash sign & spaces from the numbers

                            numbers.add(phoneNumber)
                        }
                        Toast.makeText(
                            this@MainActivity,
                            "$contactName $numbers",
                            Toast.LENGTH_LONG
                        ).show()

                        cursor2.close()
                    }
                    cursor1.close()
                }
            }


        }
    } else {
        Toast.makeText(this, "Cancelled", Toast.LENGTH_SHORT).show()
    }
}

Note: you're using a deprecated API onActivityResult that is replaced with registerForActivityResult

Here is a demo of using it:

class MainActivity : AppCompatActivity() {

    private val requestSinglePermission =
        registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
            // Do something if permission granted
            if (isGranted) {
                Log.d("LOG_TAG", "permission granted by the user")

                // Do something as the permission is not granted
            } else {
                Log.d("LOG_TAG", "permission denied by the user")
            }
        }


    private var allNumLauncher =
        registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
            if (result.resultCode == Activity.RESULT_OK) {
                // There are no request codes
                val data = result.data?.data

                data?.let {
                    val cursor = contentResolver.query(
                        data,
                        null,
                        null,
                        null,
                        null
                    )

                    cursor?.let {
                        if (it.moveToFirst()) {
                            val name =
                                it.getString(it.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME))

                            if (Integer.parseInt(
                                    it.getString(
                                        it.getColumnIndex(
                                            ContactsContract.Contacts.HAS_PHONE_NUMBER
                                        )
                                    )
                                ) > 0 // Check if the contact has phone numbers
                            ) {

                                val id =
                                    it.getString(it.getColumnIndex(ContactsContract.Contacts._ID))

                                val phonesCursor = contentResolver.query(
                                    ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                                    null,
                                    ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " + id,
                                    null,
                                    null
                                )

                                val numbers = mutableSetOf<String>()
                                phonesCursor?.let {
                                    while (phonesCursor.moveToNext()) {
                                        val phoneNumber =
                                            phonesCursor.getString(
                                                phonesCursor.getColumnIndex(
                                                    ContactsContract.CommonDataKinds.Phone.NUMBER
                                                )
                                            ).replace("-", "").replace(" ", "")
                                        numbers.add(phoneNumber)
                                    }
                                    Toast.makeText(
                                        this@MainActivity,
                                        "$name $numbers",
                                        Toast.LENGTH_LONG
                                    ).show()
                                    Log.d(TAG, "$name $numbers")
                                }

                                phonesCursor?.close()

                            } else {
                                Toast.makeText(
                                    this@MainActivity,
                                    "$name - No numbers",
                                    Toast.LENGTH_LONG
                                ).show()
                                Log.d(TAG, "$name - No numbers")
                            }
                        }

                        cursor.close()
                    }

                }
            }
        }


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        if (ContextCompat.checkSelfPermission(
                this,
                Manifest.permission.READ_CONTACTS
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            requestSinglePermission.launch(
                Manifest.permission.READ_CONTACTS
            )

        }


        // get a contact:
        val intent = Intent(Intent.ACTION_PICK,
                ContactsContract.Contacts.CONTENT_URI) 
        allNumLauncher.launch(intent)

    }
}
Zain
  • 37,492
  • 7
  • 60
  • 84
  • Thank you for the detailed answer! I tried your code, and for some reason it only works the first time I choose a contact after having opened the app. The times after that, it just shows an empty list. Why could that be? – Enrico Cortinovis Oct 03 '21 at 19:02
  • @Thanks for the comment. It could be that this contact doesn't have an associated phone number.. I just tested it out again now and it works multiple times. – Zain Oct 03 '21 at 20:02
  • That's very strange. Maybe it has to do with my version of Android? I have 8.0. I saw that actually it just works very rarely. Maybe it was just a coincidence that it was working the first time. With some contacts it returns the correct name and number, with others (that all have an associated phone number) it does not. – Enrico Cortinovis Oct 03 '21 at 20:38
  • 1
    However, I still managed to make the code work, by using @Jabbar's answer partly and then adding your code that gets the contact's name (and to "clean" the phone number string). I really appreciate the added demo that uses `registerForActivityResult`, and I will do my best to make the code I have now work with that. I will assign the 50 rep. to this answer since it was the most useful. Thank you. – Enrico Cortinovis Oct 03 '21 at 20:40
  • @EnricoCortinovis I tested it on Android 11, but let me check that on Android 8 on the emulator – Zain Oct 03 '21 at 20:45
  • @EnricoCortinovis I just tested that on Android 8, also works.. I beleive it's a tiny thing that is missed out; so created [this demo app repo](https://github.com/M7mdZain/ktGetContactInfo) for you... Hopefully you can figure it out. – Zain Oct 03 '21 at 21:05