Search code examples
androidkotlinandroid-contactsandroid-contentresolverstartactivityforresult

Getting a Contact Without Permissions Using registerForActivityResult


New to Kotlin and Android development and, after updating some libraries, I had some issue with my previously working method of grabbing Contact details, so I decided to try out the new registerForActivityResult defined at https://developer.android.com/training/basics/intents/result instead of debugging my “legacy” code. In doing so, I’m no longer able to get this to work without requiring READ_CONTACTS permissions, so I’m hoping someone can hit me with a clue stick and help me bend the new methods to do what the previous method did.

Thank you.

Previous method

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    fab.setOnClickListener {
        val intent = Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI).also {
            it.type = ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE
            startActivityForResult(it, 1)
        }
    }
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    if (requestCode == 1 && resultCode == RESULT_OK) {
        val projection = arrayOf(
                ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY,
                ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
                ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER
        )

        GlobalScope.launch {
            data?.data?.also { contactUri ->
                context?.contentResolver?.query(contactUri, projection, null, null, null)?.apply {
                    moveToFirst()
                    //val id = viewModel.addPerson(DefaultPerson(getString(0), getString(1), getString(2)))
                    close()
                }
            }
        }
    }
}

New method

private val getPerson = registerForActivityResult(PickContact()) {
    val projection = arrayOf(
            ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY,
            ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
            ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER
    )

    context?.contentResolver?.query(it!!.toUri(), projection, null, null, null)?.apply {
        moveToFirst()
        //val id = viewModel.addPerson(DefaultPerson(getString(0), getString(1), getString(2)))
        close()
    }
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    fab.setOnClickListener {
        getPerson.launch(0)
    }
}

class PickContact : ActivityResultContract<Int, String?>() {
    override fun createIntent(context: Context, input: Int?) =
            Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI).also {
                it.type = ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE
            }

    override fun parseResult(resultCode: Int, intent: Intent?): String? {
       return if (resultCode == RESULT_OK) intent?.toUri(0) else null
    }
}

Solution

  • Looks like I messed things up in relation to the Uri. The following changes in relation to this resolves the issue:

    val getPerson = registerForActivityResult(PickContact()) {
        it?.also { contactUri ->
            val projection = arrayOf(
                ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY,
                ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
                ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER
            )
    
            context?.contentResolver?.query(contactUri, projection, null, null, null)?.apply {
                moveToFirst()
                //val id = viewModel.addPerson(DefaultPerson(getString(0), getString(1), getString(2)))
                close()
            }
        }
    }
    
    class PickContact : ActivityResultContract<Int, Uri?>() {
        override fun createIntent(context: Context, input: Int?) =
                Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI).also {
                    it.type = ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE
                }
    
        override fun parseResult(resultCode: Int, intent: Intent?): Uri? {
           return if (resultCode == RESULT_OK) intent?.data else null
        }
    }