Kişi seçici

Android Kişi Seçici, kullanıcıların kişilerle ilgili bilgileri uygulamanızla paylaşması için standartlaştırılmış ve gözatılabilir bir arayüzdür. Android 17 (API düzeyi 37) veya sonraki sürümlerin yüklü olduğu 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 isteğinde bulunmak yerine ihtiyaç duyduğu veri alanlarını (ör. telefon numaraları veya e-posta adresleri) belirtir ve kullanıcı, paylaşılacak belirli kişileri seçer. Bu sayede uygulamanız yalnızca seçilen verilere okuma erişimi kazanır. Böylece, kullanıcı arayüzünü oluşturmak veya bakımını yapmak zorunda kalmadan yerleşik arama, profil değiştirme ve çoklu seçim özellikleri sayesinde tutarlı bir kullanıcı deneyimi sunarken ayrıntılı kontrol de sağlanır.

Kişi seçiciyi entegre etme

Kişi seçiciyi entegre etmek için Intent.ACTION_PICK_CONTACTS amacını kullanın. Bu amaç, seçiciyi başlatır ve seçilen kişileri uygulamanıza döndürür.

Eski ACTION_PICK'dan farklı olarak, Kişi Seçici, uygulamanızın gerektirdiği birden fazla veri alanını aynı anda belirtmenize olanak tanır. Bunu, Intent.EXTRA_REQUESTED_DATA_FIELDS kullanarak yaparsınız. ContactsContract.CommonDataKinds içinde tanımlanan MIME türlerinin ArrayList<String>'sini 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 registerForActivityResult tuşunu StartActivityForResult sözleşmesiyle birlikte kullanın. Amacı, tek veya birden çok 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çicinin kullanıcı arayüzü, istenen veri alanlarına göre ayarlanır. Bu şartlara bağlı olarak, birden fazla alan gerektiğinde kullanıcılar tüm kişi kaydını seçebilir veya bir kişinin bilgilerindeki belirli veri öğelerini seçebilir.

Kişi seçicinin 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 numarası istiyor. Seçici, listede yalnızca telefon numarası olan kişileri gösterecek şekilde filtreleme yapar ve kullanıcının belirli bir numarayı 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 {
    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 ekleyin. İsteğe bağlı olarak, 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 {
    // 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 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ı izler.

// 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, mevcut Intent.ACTION_PICK amacını otomatik olarak yeni Kişi Seçici arayüzünü kullanacak şekilde yükseltir.

Uygulamanızda zaten ACTION_PICK kullanılıyorsa yeni kullanıcı arayüzünü almak için kodunuzu değiştirmeniz gerekmez. Ancak, tek bir Uri ile iletişim verilerini sorgulama, 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ı Intent.ACTION_PICK_CONTACTS veya yeni amaç 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 EXTRA_USE_SYSTEM_CONTACTS_PICKER boolean ekstrasını ACTION_PICK amacınıza ekleyerek Android 17 ve sonraki sürümlerin yüklü olduğu cihazlarda yeni seçici davranışını test edebilirsiniz.

En iyi uygulamalar

  • Yalnızca ihtiyacınız olanı 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ı için daha temiz bir kullanıcı arayüzü sağlar.
  • 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ını sağlamak 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 getirme: Oturum URI'si geçici okuma izni verir. Bu iletişim bilgilerine daha sonra (uygulama işleminiz sonlandırıldıktan sonra) erişmeniz gerekirse uygulamanızın iletişim verilerini kalıcı olarak saklaması gerekir.
  • Hesap verilerine güvenmeyin: Kullanıcı gizliliğini korumak ve parmak izi oluşturmayı önlemek için sonuçlardan hesaba özel meta veriler kaldırılır.