Produktneuigkeiten

Kontaktauswahl: Datenschutzorientiertes Teilen von Kontakten

Lesezeit: 4 Minuten
Profil von Roxanna Aliabadi Walker ansehen
Roxanna Aliabadi Walker Product Manager, Android

Datenschutz und Nutzerkontrolle sind weiterhin ein zentraler Bestandteil von Android. So wie die Bildauswahl das Teilen von Medien sicher und einfach gemacht hat, bringen wir jetzt dasselbe Maß an Datenschutz, Einfachheit und Nutzerfreundlichkeit in die Kontaktauswahl.

Ein neuer Standard für den Datenschutz bei Kontakten

Bisher waren Anwendungen, die Zugriff auf die Kontakte eines bestimmten Nutzers benötigten, auf die umfassende Berechtigung READ_CONTACTS angewiesen. Dieser Ansatz war zwar funktional, gewährte Apps jedoch oft mehr Daten als nötig. Die neue Android-Kontaktauswahl, die in Android 17 eingeführt wurde, ändert diese Dynamik, indem sie eine standardisierte, sichere und durchsuchbare Oberfläche für die Kontaktauswahl bietet.

Mit dieser Funktion können Nutzer Apps nur Zugriff auf die von ihnen ausgewählten Kontakte gewähren. Das entspricht dem Engagement von Android für Datentransparenz und minimierte Berechtigungen.

picker.png
selection.png

Funktionsweise

Entwickler können die Kontaktauswahl mit dem Intent Intent.ACTION_PICK_CONTACTS einbinden. Diese aktualisierte API bietet mehrere leistungsstarke Funktionen:

  • Anfragen für detaillierte Daten:Apps können genau angeben, welche Felder sie benötigen, z. B. Telefonnummern oder E‑Mail-Adressen, anstatt den gesamten Kontaktdatensatz zu erhalten.
  • Unterstützung für Mehrfachauswahl: Die Auswahl unterstützt sowohl die Auswahl einzelner als auch mehrerer Kontakte, was Entwicklern mehr Flexibilität für Funktionen wie Gruppeneinladungen bietet.
  • Auswahllimits:Entwickler können benutzerdefinierte Limits für die Anzahl der Kontakte festlegen, die ein Nutzer gleichzeitig auswählen kann.
  • Temporärer Zugriff: Nach der Auswahl gibt das System einen Sitzungs-URI zurück, der temporären Lesezugriff auf die angeforderten Daten gewährt. So wird sichergestellt, dass der Zugriff nicht länger als nötig besteht.
  • Zugriff auf andere Profile : Wenn dieser neue Intent verwendet wird, können Nutzer über die Benutzeroberfläche Inhalte aus anderen Nutzerprofilen auswählen, z. B. aus einem Arbeitsprofil, einem geklonten Profil oder einem vertraulichen Profil.
  • Optimierte Leistung : Die Kontaktauswahl gibt einen einzelnen URI zurück, der die gemeinsame Abfrage von Ergebnissen ermöglicht. Es ist also nicht mehr erforderlich, einzelne Kontakt-URIs separat abzufragen, wie es bei ACTION_PICK der Fall ist. Diese Effizienz reduziert den Systemaufwand weiter, da eine einzige Binder-Transaktion verwendet wird.

Abwärtskompatibilität und Implementierung

Auf Geräten mit Android 17 oder höher werden ältere ACTION_PICK-Intents, die Kontaktdatentypen angeben, automatisch auf die neue, sicherere Oberfläche aktualisiert. Um erweiterte Funktionen wie die Mehrfachauswahl optimal nutzen zu können, sollten Entwickler jedoch ihren Implementierungscode aktualisieren und den zurückgegebenen Sitzungs-URI mit dem ContentResolver abfragen.


Kontaktauswahl integrierenEntwickler verwenden den Intent ACTION_PICK_CONTACTS, um die Kontaktauswahl zu integrieren. Im folgenden Codebeispiel wird gezeigt, wie Sie die Auswahl starten und bestimmte Datenfelder wie E-Mail-Adressen und Telefonnummern anfordern.

// 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)

Nachdem der Nutzer eine Auswahl getroffen hat, verarbeitet die App das Ergebnis, indem sie den zurückgegebenen Sitzungs-URI abfragt, um die angeforderten Kontaktdaten zu extrahieren.

// 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
}

Hier finden Sie die vollständige Dokumentation.

Best Practices für Entwickler

Damit Sie die bestmögliche Nutzererfahrung bieten und hohe Sicherheitsstandards einhalten können, empfehlen wir Folgendes:

  • Datenminimierung:Fordern Sie nur die spezifischen Datenfelder (z.B. E‑Mail-Adresse) an, die Ihre App benötigt.
  • Sofortige Persistenz: Ausgewählte Daten werden sofort persistent gespeichert, da der Zugriff auf den Sitzungs-URI nur vorübergehend ist.
Weiterlesen