Seletor de contatos: compartilhamento de contatos com foco na privacidade
Leitura de 4 minutos
A privacidade e o controle do usuário continuam sendo o foco da experiência do Android. Assim como o seletor de fotos tornou o compartilhamento de mídia seguro e fácil de implementar, agora estamos trazendo o mesmo nível de privacidade, simplicidade e ótima experiência do usuário para a seleção de contatos.
Um novo padrão para a privacidade de contatos
Historicamente, os aplicativos que exigiam acesso aos contatos de um usuário específico dependiam da permissão geral READ_CONTACTS. Embora funcional, essa abordagem geralmente concedia aos apps mais dados do que o necessário. O novo seletor de contatos do Android, lançado no Android 17, muda essa dinâmica ao oferecer uma interface padronizada, segura e pesquisável para a seleção de contatos.
Com esse recurso, os usuários podem conceder aos apps acesso apenas aos contatos específicos que escolherem, o que está alinhado ao compromisso do Android com a transparência de dados e a minimização das permissões.
Como funciona
Os desenvolvedores podem integrar o seletor de contatos usando a intent Intent.ACTION_PICK_CONTACTS. Essa API atualizada oferece vários recursos avançados:
- Solicitações de dados granulares:os apps podem especificar exatamente quais campos precisam, como números de telefone ou endereços de e-mail, em vez de receber todo o registro de contato.
- Suporte à seleção múltipla:o seletor é compatível com a seleção de um ou vários contatos, oferecendo aos desenvolvedores mais flexibilidade para recursos como convites de grupo.
- Limites de seleção:os desenvolvedores podem definir limites personalizados para o número de contatos que um usuário pode selecionar de uma só vez.
- Acesso temporário:ao selecionar essa opção, o sistema retorna um URI de sessão que fornece acesso temporário de leitura aos dados solicitados, garantindo que o acesso não persista por mais tempo do que o necessário.
- Acesso a outros perfis : ao usar essa nova intent, a interface permite que os usuários selecionem conteúdo de outros perfis, como um perfil de trabalho, um perfil clonado ou um espaço privado.
- Performance otimizada : o seletor de contatos retorna um único URI que permite consultar resultados coletivos, eliminando a necessidade de consultar URIs de contatos individuais separadamente, conforme exigido por
ACTION_PICK. Essa eficiência reduz ainda mais a sobrecarga do sistema usando uma única transaçãoBinder.
Compatibilidade com versões anteriores e implementação
Para dispositivos com o Android 17 ou versões mais recentes, o sistema atualiza automaticamente intents ACTION_PICK legadas que especificam tipos de dados de contato para a nova interface mais segura. No entanto, para aproveitar ao máximo os recursos avançados, como a seleção múltipla, os desenvolvedores são incentivados a atualizar o código de implementação e usar o ContentResolver para consultar o URI da sessão retornada.
Integrar o seletor de contatosPara integrar o seletor de contatos, os desenvolvedores usam a intent ACTION_PICK_CONTACTS. Confira abaixo um exemplo de código que demonstra como iniciar o seletor e solicitar campos de dados específicos, como e-mail e números de telefone.
// State to hold the list of selected contacts var contacts by remember { mutableStateOf<List>(emptyList()) } // Launcher for the Contact Picker intent val pickContact = rememberLauncherForActivityResult(StartActivityForResult()) { if (it.resultCode == Activity.RESULT_OK) { val resultUri = it.data?.data ?: return@rememberLauncherForActivityResult // Process the result URI in a background thread coroutine.launch { contacts = processContactPickerResultUri(resultUri, context) } } } // Define the specific contact data fields you need val requestedFields = arrayListOf( Email.CONTENT_ITEM_TYPE, Phone.CONTENT_ITEM_TYPE, ) // Set up the intent for the Contact Picker val pickContactIntent = Intent(ACTION_PICK_CONTACTS).apply { putExtra(EXTRA_PICK_CONTACTS_SELECTION_LIMIT, 5) putStringArrayListExtra( EXTRA_PICK_CONTACTS_REQUESTED_DATA_FIELDS, requestedFields ) putExtra(EXTRA_PICK_CONTACTS_MATCH_ALL_DATA_FIELDS, false) } // Launch the picker pickContact.launch(pickContactIntent)
Depois que o usuário faz uma seleção, o app processa o resultado consultando o URI da sessão retornada para extrair as informações de contato solicitadas.
// Data class representing a parsed Contact with selected details data class Contact(val id: String, val name: String, val email: String?, val phone: String?) // Helper function to query the content resolver with the URI returned by the Contact Picker. // Parses the cursor to extract contact details such as name, email, and phone number private suspend fun processContactPickerResultUri( sessionUri: Uri, context: Context ): List<Contact> = withContext(Dispatchers.IO) { // Define the columns we want to retrieve from the ContactPicker ContentProvider val projection = arrayOf( ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME_PRIMARY, ContactsContract.Data.MIMETYPE, // Type of data (e.g., email or phone) ContactsContract.Data.DATA1, // The actual data (Phone number / Email string) ) val results = mutableListOf<Contact>() // Note: The Contact Picker Session Uri doesn't support custom selection & selectionArgs. context.contentResolver.query(sessionUri, projection, null, null, null)?.use { cursor -> // Get the column indices for our requested projection val contactIdIdx = cursor.getColumnIndex(ContactsContract.Contacts._ID) val mimeTypeIdx = cursor.getColumnIndex(ContactsContract.Data.MIMETYPE) val nameIdx = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY) val data1Idx = cursor.getColumnIndex(ContactsContract.Data.DATA1) while (cursor.moveToNext()) { val contactId = cursor.getString(contactIdIdx) val mimeType = cursor.getString(mimeTypeIdx) val name = cursor.getString(nameIdx) ?: "" val data1 = cursor.getString(data1Idx) ?: "" // Determine if the current row represents an email or a phone number val email = if (mimeType == Email.CONTENT_ITEM_TYPE) data1 else null val phone = if (mimeType == Phone.CONTENT_ITEM_TYPE) data1 else null // Add the parsed contact to our results list results.add(Contact(contactId, name, email, phone)) } } return@withContext results }
Confira a documentação completa aqui.
Práticas recomendadas para desenvolvedores
Para oferecer a melhor experiência do usuário e manter altos padrões de segurança, recomendamos o seguinte:
- Minimização de dados:solicite apenas os campos de dados específicos (por exemplo, e-mail) de que seu app precisa.
- Persistência imediata:persista os dados selecionados imediatamente, já que o acesso ao URI da sessão é temporário.
-
Novidades sobre produtosO seletor de fotos incorporado: uma maneira mais integrada de solicitar fotos e vídeos de forma particular no seu app.
Roxanna Aliabadi Walker, Yacine Rezgui • Leitura de 8 minutos -
Novidades sobre produtosHoje, temos o prazer de anunciar o lançamento estável das bibliotecas AndroidX Security State versão 1.1.0 e Security State Provider versão 1.0.0.
Maunik Shah, Alec Garcia, Joseph Yong • Leitura de 4 minutos -
Novidades sobre produtosHoje, estamos lançando o primeiro conjunto de tarefas de longo prazo (LHT, na sigla em inglês), que são tarefas de grande complexidade que levam vários dias ou até uma semana para serem concluídas por um engenheiro. Também estamos lançando a avaliação com agentes, começando com agentes dos provedores de modelos correspondentes.
Matthew McCullough • Leitura de 3 minutos
Receba os insights mais recentes sobre desenvolvimento Android na sua caixa de entrada semanalmente.