Seletor de contatos

O Seletor de Contatos do Android é uma interface padronizada e navegável para os usuários compartilharem contatos com seu app. Disponível em dispositivos com o Android 17 (nível 37 da API) ou mais recente, o seletor oferece uma alternativa que preserva a privacidade à permissão READ_CONTACTS. Em vez de solicitar acesso a toda a agenda do usuário, seu app especifica os campos de dados necessários, como números de telefone ou endereços de e-mail, e o usuário seleciona contatos específicos para compartilhar. Isso concede ao app acesso de leitura apenas aos dados selecionados, garantindo o controle granular e proporcionando uma experiência do usuário consistente com recursos integrados de pesquisa, troca de perfil e seleção múltipla sem precisar criar ou manter a interface.

Integrar o Seletor de Contatos

Para integrar o Seletor de Contatos, use a intent Intent.ACTION_PICK_CONTACTS. Essa intent inicia o seletor e retorna os contatos selecionados para o app.

Ao contrário da ACTION_PICK legada, o Seletor de Contatos permite especificar vários campos de dados necessários para o app ao mesmo tempo. Para fazer isso, use Intent.EXTRA_REQUESTED_DATA_FIELDS, transmitindo um ArrayList<String> de tipos MIME definidos em ContactsContract.CommonDataKinds.

Os tipos MIME comuns incluem:

  • ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
  • ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE
  • ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE

Iniciar o seletor

Use registerForActivityResult com o contrato StartActivityForResult para iniciar o seletor. É possível configurar a intent para permitir seleções únicas ou múltiplas.

Selecionar um único contato

Neste exemplo, o app solicita apenas números de telefone. O seletor vai filtrar a lista para mostrar apenas contatos com números de telefone e permitir que o usuário selecione um número específico.

Kotlin

// Define the specific data fields you need
val requestedFields = arrayListOf(
    ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
)

// Set up the intent
val pickContactIntent = Intent(Intent.ACTION_PICK_CONTACTS).apply {
    type = ContactsContract.Contacts.CONTENT_TYPE
    putStringArrayListExtra(Intent.EXTRA_REQUESTED_DATA_FIELDS, requestedFields)
}

// Launch the picker
pickContactLauncher.launch(pickContactIntent)

Java

// Define the specific data fields you need
ArrayList<String> requestedFields = new ArrayList<>();
requestedFields.add(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);

// Set up the intent
Intent pickContactIntent = new Intent(Intent.ACTION_PICK_CONTACTS);
pickContactIntent.setType(ContactsContract.Contacts.CONTENT_TYPE);
pickContactIntent.putStringArrayListExtra(Intent.EXTRA_REQUESTED_DATA_FIELDS,
        requestedFields);

// Launch the picker
pickContactLauncher.launch(pickContactIntent);

Selecionar vários contatos

Para ativar a seleção múltipla, adicione o extra Intent.EXTRA_ALLOW_MULTIPLE. Opcionalmente, é possível limitar o número de itens que um usuário pode selecionar.

Kotlin

val requestedFields = arrayListOf(
    ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE,
    ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE
)

val pickMultipleIntent = Intent(Intent.ACTION_PICK_CONTACTS).apply {
    type = ContactsContract.Contacts.CONTENT_TYPE
    putStringArrayListExtra(Intent.EXTRA_REQUESTED_DATA_FIELDS, requestedFields)
    // Enable multi-select
    putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
    // Optional: Set a custom limit (max 50 recommended)
    putExtra(Intent.EXTRA_SELECTION_LIMIT, 10)
}

pickMultipleLauncher.launch(pickMultipleIntent)

Processar os resultados

Quando o usuário conclui a seleção, o sistema retorna um RESULT_OK e um URI de sessão. Esse URI concede acesso de leitura temporário aos dados selecionados.

É possível consultar esse URI usando um ContentResolver padrão. O Cursor resultante contém os campos de dados solicitados e segue o esquema de ContactsContract.Data.

Kotlin

private val pickContactLauncher = registerForActivityResult(
    ActivityResultContracts.StartActivityForResult()
) { result ->
    if (result.resultCode == Activity.RESULT_OK) {
        // The result data contains the Session URI
        val sessionUri = result.data?.data
        sessionUri?.let { uri ->
            processSelectedContacts(uri)
        }
    } else {
        // User cancelled the picker
    }
}

private fun processSelectedContacts(sessionUri: Uri) {
    // Define the projection (columns) you want to retrieve
    val projection = arrayOf(
        ContactsContract.Data.CONTACT_ID,
        ContactsContract.Contacts.DISPLAY_NAME_PRIMARY,
        ContactsContract.Data.MIMETYPE,
        ContactsContract.Data.DATA1 // Generic data column (Phone number, Email, etc.)
    )

    contentResolver.query(sessionUri, projection, null, null, null)?.use { cursor ->
        val mimeTypeIdx = cursor.getColumnIndex(ContactsContract.Data.MIMETYPE)
        val dataIdx = cursor.getColumnIndex(ContactsContract.Data.DATA1)
        val nameIdx = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY)

        while (cursor.moveToNext()) {
            val mimeType = cursor.getString(mimeTypeIdx)
            val dataValue = cursor.getString(dataIdx)
            val name = cursor.getString(nameIdx)

            when (mimeType) {
                ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE -> {
                    Log.d("ContactPicker", "Picked Phone: $dataValue for $name")
                }
                ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE -> {
                    Log.d("ContactPicker", "Picked Email: $dataValue for $name")
                }
            }
        }
    }
}

Compatibilidade com versões anteriores

Em apps destinados ao Android 17 (nível 37 da API) e versões mais recentes, o sistema faz upgrade automático da intent Intent.ACTION_PICK para usar a nova interface do seletor de contatos.

Se o app já usa ACTION_PICK, não é necessário mudar o código para receber a nova interface. No entanto, para aproveitar novos recursos, como receber um único Uri para consultar dados de contato, alternar entre perfis pessoais e de trabalho ou várias solicitações de campo de dados, é necessário atualizar a implementação para usar Intent.ACTION_PICK_CONTACTS ou os novos extras de intent.

Testar em SDKs mais antigos

É possível testar o novo comportamento do seletor em dispositivos com o Android 17 e versões mais recentes, mesmo que o app seja destinado a uma versão mais antiga do SDK, adicionando o extra booleano EXTRA_USE_SYSTEM_CONTACTS_PICKER à intent ACTION_PICK.

Práticas recomendadas

  • Solicite apenas o necessário: se o app só precisa enviar um SMS, solicite Phone.CONTENT_ITEM_TYPE. O seletor vai filtrar automaticamente os contatos que não têm números de telefone, resultando em uma interface mais limpa para o usuário.
  • Persista os dados imediatamente: o URI de sessão concede permissão de leitura temporária. Se você precisar acessar essas informações de contato mais tarde (depois que o processo do app for encerrado), o app precisará persistir os dados de contato.
  • Não dependa dos dados da conta: para proteger a privacidade do usuário e evitar a impressão digital, os metadados específicos da conta são removidos dos resultados.