Kişi seçici

Android Kişi Seçici, kullanıcıların uygulamanızla kişi paylaşması için standartlaştırılmış, göz atılabilir bir arayüzdür. Android 17 (API düzeyi 37) veya sonraki sürümleri çalıştıran cihazlarda kullanılabilen seçici, geniş kapsamlı READ_CONTACTS iznine gizliliği korumaya yönelik bir alternatif sunar. Uygulamanız, kullanıcının tüm adres defterine erişim istemek yerine telefon numaraları veya e-posta adresleri gibi ihtiyaç duyduğu veri alanlarını belirtir ve kullanıcı, paylaşılacak belirli kişileri seçer. Bu sayede uygulamanız yalnızca seçilen verilere okuma erişimi elde eder. Böylece, kullanıcı arayüzü oluşturmak veya yönetmek zorunda kalmadan yerleşik arama, profil değiştirme ve çoklu seçim özellikleriyle tutarlı bir kullanıcı deneyimi sunarken ayrıntılı kontrol sağlayabilirsiniz.

Kişi Seçici'yi entegre etme

Kişi Seçici'yi entegre etmek için ContactsPickerSessionContract.ACTION_PICK_CONTACTS intent'ini kullanın. Bu intent, seçiciyi başlatır ve seçilen kişileri uygulamanıza döndürür.

Eski ACTION_PICK seçicinin aksine Kişi Seçici, uygulamanızın aynı anda ihtiyaç duyduğu birden fazla veri alanını belirtmenize olanak tanır. Bunu yapmak için ContactsPickerSessionContract.EXTRA_PICK_CONTACTS_REQUESTED_DATA_FIELDS yöntemini kullanır ve ContactsContract.CommonDataKinds içinde tanımlanan MIME türlerinin ArrayList<String> değerini iletirsiniz.

Yaygın MIME türleri şunlardır:

  • ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
  • ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE
  • ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE

Seçiciyi başlatma

Seçiciyi başlatmak için StartActivityForResult sözleşmesiyle registerForActivityResult kullanın. Intent'i tek veya birden fazla seçime izin verecek şekilde yapılandırabilirsiniz.

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

Seçim modu

Kişi Seçici'nin kullanıcı arayüzü, istenen veri alanlarına göre ayarlanır. Bu gereksinimlere bağlı olarak kullanıcılar, birden fazla alan gerektiğinde tüm kişi kaydını seçebilir veya bir kişinin bilgilerinden belirli veri öğelerini seçebilir.

Kişi Seçici'nin farklı kullanıcı arayüzü modları
Şekil 1. Kişi Seçici arayüzü, istenen veri alanlarına (tek kişi, birden fazla kişi ve birden fazla telefon numarası seçimi) uyum sağlar.

Tek bir kişi seçme

Bu örnekte, uygulama yalnızca telefon numaralarını ister. Seçici, listeyi yalnızca telefon numarası olan kişileri gösterecek şekilde filtreler ve kullanıcının belirli bir numara seçmesine olanak tanır.

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

Birden fazla kişi seçme

Çoklu seçimi etkinleştirmek için Intent.EXTRA_ALLOW_MULTIPLE ekstrasını ekleyin. İsterseniz kullanıcının seçebileceği öğe sayısını sınırlayabilirsiniz.

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)

Sonuçları işleme

Kullanıcı seçimi tamamladığında sistem bir RESULT_OK ve oturum URI'si döndürür. Bu URI, seçilen verilere geçici okuma erişimi verir.

Bu URI'yi standart bir ContentResolver kullanarak sorgulayabilirsiniz. Sonuçta elde edilen Cursor, istenen veri alanlarını içerir ve ContactsContract.Data şemasını kullanır.

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

Geriye Dönük Uyumluluk

Android 17 (API düzeyi 37) ve sonraki sürümleri hedefleyen uygulamalarda sistem, yeni Kişi Seçici arayüzünü kullanmak için mevcut Intent.ACTION_PICK intent'ini otomatik olarak günceller.

Uygulamanızda ACTION_PICK kullanılıyorsa yeni kullanıcı arayüzünü almak için kodunuzu değiştirmeniz gerekmez. Ancak kişi verilerini sorgulamak için tek bir Uri alma, kişisel ve iş profilleri arasında geçiş yapma veya birden fazla veri alanı isteği gibi yeni özelliklerden yararlanmak için uygulamanızı ContactsPickerSessionContract.ACTION_PICK_CONTACTS veya yeni intent ekstralarını kullanacak şekilde güncellemeniz gerekir.

Eski hedef SDK'larda test etme

Uygulamanız daha düşük bir SDK sürümünü hedeflese bile ACTION_PICK intent'inize EXTRA_USE_SYSTEM_CONTACTS_PICKER boole ek değerini ekleyerek yeni seçici davranışını Android 17 ve sonraki sürümleri çalıştıran cihazlarda test edebilirsiniz.

En iyi uygulamalar

  • Yalnızca ihtiyacınız olan izinleri isteyin: Uygulamanızın yalnızca SMS göndermesi gerekiyorsa Phone.CONTENT_ITEM_TYPE iznini isteyin. Seçici, telefon numarası olmayan kişileri otomatik olarak filtreleyerek kullanıcıya daha temiz bir kullanıcı arayüzü sunar.
  • Kişi başına birden fazla veri girişini yönetme: Kişiler genellikle çeşitli e-posta adresleri veya telefon numaraları içerir. Bunların kullanıcıya net ve sezgisel bir şekilde sunulması için ContactsContract.Contacts.LOOKUP_KEY kullanarak gruplandırmanız önerilir. Ayrıca, uygulamanızın arayüzünde daha ayrıntılı seçim seçenekleri sunmak için her girişle ilgili belirli etiketleri (ör. iş veya kişisel) alabilirsiniz.
  • Verileri hemen kalıcı hale getirin: Oturum URI'si geçici okuma izni verir. Bu iletişim bilgilerine daha sonra (uygulama işleminiz sonlandırıldıktan sonra) erişmeniz gerekiyorsa uygulamanızın iletişim verilerini saklaması gerekir.
  • Hesap verilerine güvenmeyin: Kullanıcı gizliliğini korumak ve dijital parmak izi oluşturmayı önlemek için hesaba özgü meta veriler sonuçlardan çıkarılır.