Il Selettore di contatti Android è un'interfaccia standardizzata e sfogliabile che consente agli utenti di condividere i contatti con la tua app. Disponibile sui dispositivi con Android 17 (livello API 37) o versioni successive, il selettore offre un'alternativa che rispetta la privacy all'ampia autorizzazione READ_CONTACTS. Anziché richiedere
l'accesso all'intera rubrica dell'utente, la tua app specifica i campi di dati di cui
ha bisogno, ad esempio numeri di telefono o indirizzi email, e l'utente seleziona contatti specifici
da condividere. In questo modo, la tua app ottiene l'accesso in lettura solo ai dati selezionati, garantendo un controllo granulare e fornendo un'esperienza utente coerente con
funzionalità di ricerca, cambio profilo e selezione multipla integrate senza
dover creare o gestire l'interfaccia utente.
Integra il selettore di contatti
Per integrare il Selettore di contatti, utilizza l'intent
ContactsPickerSessionContract.ACTION_PICK_CONTACTS.
Questo intent avvia il selettore e restituisce i contatti selezionati alla tua app.
A differenza dell'intent legacy ACTION_PICK, il Selettore di contatti ti consente di specificare più campi di dati richiesti dalla tua app contemporaneamente. A tale scopo, utilizza
ContactsPickerSessionContract.EXTRA_REQUESTED_DATA_FIELDS, passando un
ArrayList<String> di tipi MIME definiti in ContactsContract.CommonDataKinds.
I tipi MIME comuni includono:
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPEContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPEContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE
Avviare il selettore
Utilizza registerForActivityResult con il contratto StartActivityForResult per
avviare il selettore. Puoi configurare l'intent in modo da consentire selezioni singole o multiple.
// 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 to fetch all selected contacts
coroutine.launch {
contacts = processContactPickerResultUri(resultUri, context)
}
}
}
Modalità di selezione
L'interfaccia utente del Selettore di contatti si adatta in base ai campi di dati richiesti. A seconda di questi requisiti, gli utenti possono scegliere un intero record di contatto quando sono necessari più campi o selezionare elementi di dati specifici all'interno delle informazioni di un contatto.
Selezionare un singolo contatto
In questo esempio, l'app richiede solo i numeri di telefono. Il selettore filtra l'elenco per mostrare solo i contatti con numeri di telefono e consente all'utente di selezionare un numero specifico.
// 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_USE_SYSTEM_CONTACTS_PICKER, true)
putStringArrayListExtra(
EXTRA_PICK_CONTACTS_REQUESTED_DATA_FIELDS,
requestedFields
)
}
// Launch the picker
pickContact.launch(pickContactIntent)
Selezionare più contatti
Per attivare la selezione multipla, aggiungi l'extra Intent.EXTRA_ALLOW_MULTIPLE. Se vuoi, puoi
limitare il numero di elementi che un utente può selezionare.
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_USE_SYSTEM_CONTACTS_PICKER, true)
// Enable multi-select
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
// Set limit of selectable contacts
putExtra(EXTRA_PICK_CONTACTS_SELECTION_LIMIT, 5)
// Define the specific contact data fields you need
putStringArrayListExtra(
EXTRA_PICK_CONTACTS_REQUESTED_DATA_FIELDS,
requestedFields
)
// Enable this option to only filter contacts that have all the requested data fields
putExtra(EXTRA_PICK_CONTACTS_MATCH_ALL_DATA_FIELDS, false)
}
// Launch the picker
pickContact.launch(pickContactIntent)
Gestire i risultati
Quando l'utente completa la selezione, il sistema restituisce un RESULT_OK e un URI di sessione. Questo URI concede l'accesso temporaneo in lettura ai dati selezionati.
Puoi eseguire query su questo URI utilizzando un ContentResolver standard. Il
Cursor risultante contiene i campi di dati richiesti e segue lo schema di
ContactsContract.Data.
// Data class representing a parsed Contact with selected details.
data class Contact(
val lookupKey: String,
val name: String,
val emails: List<String>,
val phones: List<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.LOOKUP_KEY,
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)
)
// We use `LOOKUP_KEY` as a unique ID to aggregate all contact info related to a same person
val contactsMap = mutableMapOf<String, Contact>()
// Note: The Contact Picker Session Uri doesn't support custom selection & selectionArgs.
// We query the URI directly to get the results chosen by the user.
context.contentResolver.query(sessionUri, projection, null, null, null)?.use { cursor ->
// Get the column indices for our requested projection
val lookupKeyIdx = cursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY)
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 lookupKey = cursor.getString(lookupKeyIdx)
val mimeType = cursor.getString(mimeTypeIdx)
val name = cursor.getString(nameIdx) ?: ""
val data1 = cursor.getString(data1Idx) ?: ""
val email = if (mimeType == Email.CONTENT_ITEM_TYPE) data1 else null
val phone = if (mimeType == Phone.CONTENT_ITEM_TYPE) data1 else null
val existingContact = contactsMap[lookupKey]
if (existingContact != null) {
contactsMap[lookupKey] = existingContact.copy(
emails = if (email != null) existingContact.emails + email else existingContact.emails,
phones = if (phone != null) existingContact.phones + phone else existingContact.phones
)
} else {
contactsMap[lookupKey] = Contact(
lookupKey = lookupKey,
name = name,
emails = if (email != null) listOf(email) else emptyList(),
phones = if (phone != null) listOf(phone) else emptyList()
)
}
}
}
return@withContext contactsMap.values.toList()
}
Compatibilità con le versioni precedenti
Per le app destinate ad Android 17 (livello API 37) e versioni successive, il sistema
esegue automaticamente l'upgrade dell'intent Intent.ACTION_PICK esistente per utilizzare la nuova
interfaccia del Selettore di contatti.
Se la tua app utilizza già ACTION_PICK, non devi modificare il codice per ricevere la nuova UI. Tuttavia, per usufruire di nuove funzionalità, come
ricevere un singolo Uri per eseguire query sui dati di contatto, passare da un profilo personale a uno
di lavoro o richiedere più campi di dati, devi aggiornare l'implementazione
per utilizzare ContactsPickerSessionContract.ACTION_PICK_CONTACTS o
i nuovi extra intent.
Test su SDK target precedenti
Puoi testare il nuovo comportamento del selettore sui dispositivi con Android 17 e
versioni successive anche se la tua app ha come target una versione dell'SDK precedente aggiungendo l'extra
EXTRA_USE_SYSTEM_CONTACTS_PICKER booleano all'intent ACTION_PICK.
Best practice
- Richiedi solo ciò di cui hai bisogno: se la tua app deve solo inviare un SMS,
richiedi
Phone.CONTENT_ITEM_TYPE. Il selettore filtra automaticamente i contatti che non hanno numeri di telefono, il che si traduce in un'interfaccia utente più pulita per l'utente. - Gestione di più voci di dati per contatto: i singoli contatti spesso
contengono vari indirizzi email o numeri di telefono. Per garantire che questi
vengano presentati in modo chiaro e intuitivo per l'utente, è consigliabile raggrupparli
utilizzando
ContactsContract.Contacts.LOOKUP_KEY. Inoltre, puoi recuperare etichette specifiche per ogni voce (ad esempio lavoro o personale) per offrire opzioni di selezione più granulari all'interno dell'interfaccia dell'app. - Rendi persistenti i dati immediatamente: l'URI di sessione concede l'autorizzazione di lettura temporanea. Se devi accedere a queste informazioni di contatto in un secondo momento (dopo l'interruzione del processo dell'app), l'app deve conservare i dati di contatto.
- Non fare affidamento sui dati dell'account: per proteggere la privacy degli utenti ed evitare l'identificazione univoca del dispositivo, i metadati specifici dell'account vengono rimossi dai risultati.