Die Android-Kontaktauswahl ist eine standardisierte, durchsuchbare Oberfläche, über die Nutzer Kontakte mit Ihrer App teilen können. Sie ist auf Geräten mit Android 17 (API-Level 37) oder höher verfügbar und bietet eine datenschutzfreundliche Alternative zur umfassenden Berechtigung READ_CONTACTS. Anstatt Zugriff auf das gesamte Adressbuch des Nutzers anzufordern, gibt Ihre App die benötigten Datenfelder an, z. B. Telefonnummern oder E-Mail-Adressen, und der Nutzer wählt bestimmte Kontakte aus, die er teilen möchte. Dadurch erhält Ihre App nur Lesezugriff auf die ausgewählten Daten. So ist eine detaillierte Kontrolle möglich und gleichzeitig wird eine einheitliche Nutzererfahrung mit integrierter Suche, Google Suche-Profilwechsel und Mehrfachauswahl geboten, ohne dass Sie die Benutzeroberfläche erstellen oder verwalten müssen.
Kontaktauswahl einbinden
Verwenden Sie den Intent ContactsPickerSessionContract.ACTION_PICK_CONTACTS, um die Kontaktauswahl zu integrieren.
Mit diesem Intent wird die Auswahl gestartet und die ausgewählten Kontakte werden an Ihre App zurückgegeben.
Im Gegensatz zum alten ACTION_PICK können Sie mit der Kontaktauswahl mehrere Datenfelder gleichzeitig angeben, die Ihre App benötigt. Dazu verwenden Sie ContactsPickerSessionContract.EXTRA_REQUESTED_DATA_FIELDS und übergeben ein ArrayList<String> von MIME-Typen, die in ContactsContract.CommonDataKinds definiert sind.
Zu den gängigen MIME-Typen gehören:
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPEContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPEContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE
Auswahl starten
Verwenden Sie registerForActivityResult mit dem Vertrag StartActivityForResult, um die Auswahl zu starten. Sie können den Intent so konfigurieren, dass eine oder mehrere Auswahlen möglich sind.
// 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)
}
}
}
Auswahlmodus
Die Benutzeroberfläche der Kontaktauswahl wird an die angeforderten Datenfelder angepasst. Je nach diesen Anforderungen können Nutzer entweder einen gesamten Kontaktdatensatz auswählen, wenn mehrere Felder benötigt werden, oder bestimmte Datenelemente aus den Informationen eines Kontakts auswählen.
Einzelnen Kontakt auswählen
In diesem Beispiel fordert die App nur Telefonnummern an. Die Auswahl filtert die Liste, sodass nur Kontakte mit Telefonnummern angezeigt werden, und ermöglicht es dem Nutzer, eine bestimmte Nummer auszuwählen.
// 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)
Mehrere Kontakte auswählen
Wenn Sie die Mehrfachauswahl aktivieren möchten, fügen Sie das Extra Intent.EXTRA_ALLOW_MULTIPLE hinzu. Optional können Sie die Anzahl der Elemente begrenzen, die ein Nutzer auswählen kann.
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)
Ergebnisse verarbeiten
Wenn der Nutzer die Auswahl abgeschlossen hat, gibt das System eine RESULT_OK und einen Sitzungs-URI zurück. Dieser URI gewährt temporären Lesezugriff auf die ausgewählten Daten.
Sie können diesen URI mit einem Standard-ContentResolver abfragen. Der resultierende Cursor enthält die angeforderten Datenfelder und folgt dem Schema von 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()
}
Abwärtskompatibilität
Bei Apps, die auf Android 17 (API-Level 37) oder höher ausgerichtet sind, wird der vorhandene Intent Intent.ACTION_PICK automatisch aktualisiert, um die neue Kontaktauswahl-Oberfläche zu verwenden.
Wenn in Ihrer App bereits ACTION_PICK verwendet wird, müssen Sie Ihren Code nicht ändern, um die neue Benutzeroberfläche zu erhalten. Wenn Sie jedoch neue Funktionen nutzen möchten, z. B. einen einzelnen Uri zum Abfragen von Kontaktdaten, den Wechsel zwischen privaten und beruflichen Profilen oder mehrere Datenfeldanfragen, müssen Sie Ihre Implementierung aktualisieren, um ContactsPickerSessionContract.ACTION_PICK_CONTACTS oder die neuen Intent-Extras zu verwenden.
Tests mit älteren SDK-Zielversionen
Sie können das neue Verhalten der Auswahl auf Geräten mit Android 17 und höher testen, auch wenn Ihre App auf eine niedrigere SDK-Version ausgerichtet ist. Fügen Sie dazu dem Intent ACTION_PICK den booleschen Extra EXTRA_USE_SYSTEM_CONTACTS_PICKER hinzu.
Best Practices
- Nur das anfordern, was Sie benötigen: Wenn Ihre App nur SMS senden muss, fordern Sie
Phone.CONTENT_ITEM_TYPEan. Die Auswahl filtert automatisch Kontakte ohne Telefonnummern heraus, was zu einer übersichtlicheren Benutzeroberfläche für den Nutzer führt. - Mehrere Dateneinträge pro Kontakt verwalten: Einzelne Kontakte enthalten oft mehrere E‑Mail-Adressen oder Telefonnummern. Damit diese für den Nutzer klar und intuitiv dargestellt werden, empfiehlt es sich, sie mit
ContactsContract.Contacts.LOOKUP_KEYzu gruppieren. Außerdem können Sie für jeden Eintrag bestimmte Labels abrufen (z. B. „Arbeit“ oder „Privat“), um in der Benutzeroberfläche Ihrer App detailliertere Auswahloptionen anzubieten. - Daten sofort persistent speichern: Der Sitzungs-URI gewährt eine temporäre Leseberechtigung. Wenn Sie später auf diese Kontaktdaten zugreifen müssen (nachdem der App-Prozess beendet wurde), muss Ihre App die Kontaktdaten beibehalten.
- Verlassen Sie sich nicht auf Kontodaten: Zum Schutz der Privatsphäre der Nutzer und zur Verhinderung von Fingerprinting werden kontospezifische Metadaten aus den Ergebnissen entfernt.