Search code examples
androidandroid-intentandroid-jetpack-composeandroid-contactsactivityresultcontracts

Select and extract contact details using Intent and ActivityResultContracts


I have a button to open and select a contact for some of it's specific data - name and number using ActivityResultContracts.PickContact. Using the guide from this page and some modifications, I was able to retrieve the contact name successfully.

I have this in my manifest file for permission

<uses-permission android:name="android.permission.READ_CONTACTS" />

Issue is my app crashes whenever I select the contact to get it's number.

Exception thrown is:

java.lang.IllegalArgumentException: Invalid column data1

Can I have more knowledge about what is going on and how to make it work as intended?

///THE COMMENTED LINES ARE FOR CONTACT'S NAME AND IT WORKED PERFECTLY (COMMENT THE NUMBER LINES FOR TEST)

@Composable
@Preview
fun openAndSelectContact() {
    val context = LocalContext.current
    val launchContact = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.PickContact()
    ) {

        val projection: Array<String> = arrayOf(
//            ContactsContract.Contacts.DISPLAY_NAME,
                        ContactsContract.CommonDataKinds.Phone.NUMBER
        )


        context.contentResolver.query(
            it!!,
            projection,
            null,
            null,
            null
        )
            .use { cursor ->
                if (cursor!!.moveToFirst()) {
                      val numberIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)
                      val number = cursor.getString(numberIndex)
                      Toast.makeText(context, "Number is $number!", Toast.LENGTH_SHORT).show()

//                    val nameIndex =
//                        cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)
//                    val name = cursor.getString(nameIndex)
//                    Toast.makeText(context, "Name is $name!", Toast.LENGTH_SHORT)
//                        .show()

                }
            }
    }


    val launchContactPermission = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.RequestPermission()
    ) { isGranted ->
        if (isGranted) {
            launchContact.launch()
        } else {
            Toast.makeText(context, "Permission Denied!", Toast.LENGTH_SHORT)
                .show()
        }

    }

    Button(
        content = { Text("IMPORT FROM CONTACT") },
        onClick = {
            when (PackageManager.PERMISSION_GRANTED) {
                //First time asking for permission ... to be granted by user
                ContextCompat.checkSelfPermission(
                    context,
                    Manifest.permission.READ_CONTACTS
                ) -> {
                    launchContact.launch()
                }
                else -> {
                    //If permission has been already granted
                    launchContactPermission.launch(Manifest.permission.READ_CONTACTS)
                }
            }
        }
    )
}

Solution

  • It seems using ActivityResultContracts.PickContact() is the issue.

    I had to modify the code using Intent and ActivityResultContracts.StartActivityForResult() to get my desired result. Here is the new code

    @Composable
    @Preview
    fun openAndSelectContact() {
        val context = LocalContext.current
    
        //create a intent variable
        val contactIntent = Intent(Intent.ACTION_PICK).apply {
            type = ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE
        }
    
        val launchContactForResult = rememberLauncherForActivityResult(
            contract = ActivityResultContracts.StartActivityForResult()
        ) { result ->
    
            if (result.resultCode == Activity.RESULT_OK) {
                val contactUri: Uri? = result?.data?.data
    
                val projection: Array<String> = arrayOf(
                    ContactsContract.CommonDataKinds.Phone.NUMBER,
                    ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME
                )
                
                contactUri?.let {
                    context.contentResolver.query(it, projection, null, null, null).use { cursor ->
                        // If the cursor returned is valid, get the phone number and (or) name
                        if (cursor!!.moveToFirst()) {
                            val numberIndex =
                                cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)
                            val number = cursor.getString(numberIndex)
    
                            val nameIndex =
                                cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)
                            val name = cursor.getString(nameIndex)
                            // Do something with the phone number
                            Toast.makeText(
                                context,
                                "Number is $number & Name is $name",
                                Toast.LENGTH_SHORT
                            ).show()
                        }
                    }
                }
            }
        }
    
        val launchContactPermission = rememberLauncherForActivityResult(
            contract = ActivityResultContracts.RequestPermission()
        ) { isGranted ->
            if (isGranted) {
                launchContactForResult.launch(contactIntent)
            } else {
                Toast.makeText(context, "Permission Denied!", Toast.LENGTH_SHORT)
                    .show()
            }
    
        }
    
        Button(
            content = { Text("IMPORT FROM CONTACT") },
            onClick = {
                when (PackageManager.PERMISSION_GRANTED) {
                    //First time asking for permission ... to be granted by user
                    ContextCompat.checkSelfPermission(
                        context,
                        Manifest.permission.READ_CONTACTS
                    ) -> {
                        launchContactForResult.launch(contactIntent)
                    }
                    else -> {
                        //If permission has been already granted
                        launchContactPermission.launch(Manifest.permission.READ_CONTACTS)
                    }
                }
            }
        )
    }