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 tutela la privacy rispetto all'autorizzazione READ_CONTACTS generale. Anziché richiedere l'accesso all'intera rubrica dell'utente, la tua app specifica i campi dati di cui ha bisogno, ad esempio numeri di telefono o indirizzi email, e l'utente seleziona i 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 al contempo un'esperienza utente coerente con funzionalità di ricerca, cambio di profilo e selezione multipla integrate, senza dover creare o gestire l'interfaccia utente.
Integrare il Selettore di contatti
Per integrare il Selettore di contatti, utilizza l'intent Intent.ACTION_PICK_CONTACTS.
Questo intent avvia il selettore e restituisce i contatti selezionati alla tua app.
A differenza dell'intent ACTION_PICK precedente, il Selettore di contatti ti consente di specificare più campi dati richiesti dalla tua app contemporaneamente. Per farlo, utilizza
Intent.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 ai campi dati richiesti. A seconda di questi requisiti, gli utenti possono scegliere un intero record di contatto quando sono necessari più campi oppure 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 filtrerà l'elenco in modo da mostrare solo i contatti con numeri di telefono e consentirà 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 {
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. Facoltativamente, 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 {
// 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 in lettura temporaneo ai dati selezionati.
Puoi eseguire una query su questo URI utilizzando un ContentResolver standard. Il Cursor risultante contiene i campi 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 che hanno come target 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 interfaccia utente. Tuttavia, per usufruire delle nuove funzionalità, ad esempio ricevere un singolo Uri per eseguire una query sui dati di contatto, passare da un profilo personale a un profilo di lavoro o richiedere più campi dati, devi aggiornare l'implementazione per utilizzare Intent.ACTION_PICK_CONTACTS o i nuovi extra dell'intent.
Testare gli 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 booleano EXTRA_USE_SYSTEM_CONTACTS_PICKER all'intent ACTION_PICK.
Best practice
- Richiedere solo ciò di cui hai bisogno: se la tua app deve solo inviare un SMS,
richiedi
Phone.CONTENT_ITEM_TYPE. Il selettore filtrerà automaticamente i contatti che non hanno numeri di telefono, con il risultato di un'interfaccia utente più pulita per l'utente. - Gestire 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, ti consigliamo di 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 della tua app. - Persistere immediatamente i dati: l'URI di sessione concede l'autorizzazione di lettura temporanea autorizzazione. Se devi accedere a queste informazioni di contatto in un secondo momento (dopo l'interruzione del processo dell'app), la tua app deve persistere i dati di contatto.
- Non fare affidamento sui dati dell'account: per proteggere la privacy degli utenti e impedire il fingerprinting, i metadati specifici dell'account vengono rimossi dai risultati.