Pemilih Kontak Android adalah antarmuka standar yang dapat dijelajahi bagi pengguna untuk membagikan kontak ke aplikasi Anda. Tersedia di perangkat yang menjalankan Android 17 (level API 37) atau yang lebih tinggi, pemilih ini menawarkan alternatif yang menjaga privasi untuk izin READ_CONTACTS yang luas. Daripada meminta akses ke seluruh buku alamat pengguna, aplikasi Anda menentukan kolom data yang diperlukan, seperti nomor telepon atau alamat email, dan pengguna memilih kontak tertentu untuk dibagikan. Hal ini memberi aplikasi Anda akses baca hanya ke data yang dipilih, sehingga memastikan kontrol terperinci sekaligus memberikan pengalaman pengguna yang konsisten dengan kemampuan penelusuran, pengalihan profil, dan pemilihan ganda bawaan tanpa harus membuat atau mempertahankan UI.
Mengintegrasikan Pemilih Kontak
Untuk mengintegrasikan Pemilih Kontak, gunakan intent ContactsPickerSessionContract.ACTION_PICK_CONTACTS.
Intent ini meluncurkan pemilih dan menampilkan kontak yang dipilih ke aplikasi Anda.
Tidak seperti ACTION_PICK lama, Pemilih Kontak memungkinkan Anda menentukan beberapa kolom data yang diperlukan aplikasi secara bersamaan. Anda melakukannya menggunakan
ContactsPickerSessionContract.EXTRA_REQUESTED_DATA_FIELDS, dengan meneruskan an
ArrayList<String> jenis MIME yang ditentukan dalam ContactsContract.CommonDataKinds.
Jenis MIME umum mencakup:
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPEContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPEContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE
Meluncurkan pemilih
Gunakan registerForActivityResult dengan kontrak StartActivityForResult untuk meluncurkan pemilih. Anda dapat mengonfigurasi intent untuk mengizinkan satu atau beberapa pilihan.
// 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)
}
}
}
Mode pilihan
UI Pemilih Kontak akan disesuaikan dengan kolom data yang diminta. Bergantung pada persyaratan ini, pengguna dapat memilih seluruh rekaman kontak jika beberapa kolom diperlukan, atau memilih item data tertentu dari dalam informasi kontak.
Memilih satu kontak
Dalam contoh ini, aplikasi hanya meminta nomor telepon. Pemilih akan memfilter daftar untuk hanya menampilkan kontak dengan nomor telepon dan memungkinkan pengguna memilih nomor tertentu.
// 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)
Memilih beberapa kontak
Untuk mengaktifkan pemilihan ganda, tambahkan tambahan Intent.EXTRA_ALLOW_MULTIPLE. Anda dapat membatasi jumlah item yang dapat dipilih pengguna.
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)
Menangani hasil
Saat pengguna menyelesaikan pemilihan, sistem akan menampilkan RESULT_OK dan URI Sesi. URI ini memberikan akses baca sementara ke data yang dipilih.
Anda dapat membuat kueri URI ini menggunakan ContentResolver standar. Cursor yang dihasilkan berisi kolom data yang diminta dan mengikuti skema 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()
}
Kompatibilitas Mundur
Untuk aplikasi yang menargetkan Android 17 (level API 37) dan yang lebih tinggi, sistem secara otomatis mengupgrade intent Intent.ACTION_PICK yang ada untuk menggunakan antarmuka Pemilih Kontak yang baru.
Jika aplikasi Anda sudah menggunakan ACTION_PICK, Anda tidak perlu mengubah kode untuk menerima UI baru. Namun, untuk memanfaatkan fitur baru, seperti menerima satu Uri untuk membuat kueri data kontak, beralih antara profil pribadi &kerja atau beberapa permintaan kolom data, Anda harus mengupdate implementasi untuk menggunakan ContactsPickerSessionContract.ACTION_PICK_CONTACTS atau tambahan intent baru.
Pengujian pada SDK target yang lebih lama
Anda dapat menguji perilaku pemilih baru di perangkat yang menjalankan Android 17 dan yang lebih tinggi meskipun aplikasi Anda menargetkan versi SDK yang lebih rendah dengan menambahkan tambahan boolean EXTRA_USE_SYSTEM_CONTACTS_PICKER ke intent ACTION_PICK.
Praktik terbaik
- Hanya minta yang Anda butuhkan: Jika aplikasi Anda hanya perlu mengirim SMS,
minta
Phone.CONTENT_ITEM_TYPE. Pemilih akan otomatis memfilter kontak yang tidak memiliki nomor telepon, sehingga menghasilkan UI yang lebih bersih bagi pengguna. - Mengelola beberapa entri data per kontak: Setiap kontak sering kali
berisi berbagai alamat email atau nomor telepon. Untuk membantu memastikan data ini ditampilkan dengan jelas dan intuitif bagi pengguna, sebaiknya kelompokkan menggunakan
ContactsContract.Contacts.LOOKUP_KEY. Selain itu, Anda dapat mengambil label tertentu untuk setiap entri (seperti kerja atau pribadi) untuk menawarkan opsi pemilihan yang lebih terperinci dalam antarmuka aplikasi. - Mempertahankan data segera: URI Sesi memberikan izin baca sementara Jika Anda perlu mengakses informasi kontak ini nanti (setelah proses aplikasi dihentikan), aplikasi Anda harus mempertahankan data kontak.
- Jangan mengandalkan Data Akun: Untuk melindungi privasi pengguna dan mencegah sidik jari, metadata khusus akun akan dihapus dari hasil.