เครื่องมือเลือกรายชื่อติดต่อ

เครื่องมือเลือกรายชื่อติดต่อ Android เป็นอินเทอร์เฟซที่ได้มาตรฐานและเรียกดูได้สำหรับผู้ใช้ในการแชร์รายชื่อติดต่อกับแอปของคุณ เครื่องมือเลือกนี้พร้อมใช้งานในอุปกรณ์ที่ใช้ Android 17 (API ระดับ 37) ขึ้นไป โดยเป็นทางเลือกที่รักษาความเป็นส่วนตัวแทนสิทธิ์ READ_CONTACTS แบบกว้าง แทนที่จะขอสิทธิ์เข้าถึงสมุดที่อยู่ทั้งหมดของผู้ใช้ แอปของคุณจะระบุฟิลด์ข้อมูลที่ต้องการ เช่น หมายเลขโทรศัพท์หรืออีเมล และผู้ใช้จะเลือกรายชื่อติดต่อที่ต้องการแชร์ ซึ่งจะให้สิทธิ์แอปของคุณเข้าถึงแบบอ่านเฉพาะข้อมูลที่เลือกเท่านั้น เพื่อให้ควบคุมได้อย่างละเอียด พร้อมทั้งมอบประสบการณ์ของผู้ใช้ที่สอดคล้องกันด้วยความสามารถในการค้นหา การสลับโปรไฟล์ Search และการเลือกหลายรายการในตัว โดยไม่ต้องสร้างหรือดูแลรักษา UI

ผสานรวมเครื่องมือเลือกรายชื่อติดต่อ

หากต้องการผสานรวมเครื่องมือเลือกรายชื่อติดต่อ ให้ใช้ Intent ContactsPickerSessionContract.ACTION_PICK_CONTACTS โดย Intent นี้จะเปิดเครื่องมือเลือกและส่งคืนรายชื่อติดต่อที่เลือกไปยังแอปของคุณ

ซึ่งต่างจาก ACTION_PICK แบบเดิมตรงที่เครื่องมือเลือกรายชื่อติดต่อช่วยให้คุณระบุฟิลด์ข้อมูลหลายรายการที่แอปต้องการได้พร้อมกัน โดยใช้ ContactsPickerSessionContract.EXTRA_REQUESTED_DATA_FIELDS และส่ง ArrayList<String> ของประเภท MIME ที่กำหนดไว้ใน ContactsContract.CommonDataKinds

ประเภท MIME ทั่วไป ได้แก่

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

เปิดเครื่องมือเลือก

ใช้ registerForActivityResult กับสัญญา StartActivityForResult เพื่อเปิดเครื่องมือเลือก คุณสามารถกำหนดค่า Intent เพื่ออนุญาตให้เลือกรายการเดียวหรือหลายรายการได้

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

โหมดการเลือก

UI ของเครื่องมือเลือกรายชื่อติดต่อจะปรับตามฟิลด์ข้อมูลที่ขอ ผู้ใช้สามารถเลือกบันทึกรายชื่อติดต่อทั้งหมดเมื่อต้องการหลายช่อง หรือเลือกรายการข้อมูลที่เฉพาะเจาะจงจากข้อมูลของรายชื่อติดต่อก็ได้ ทั้งนี้ขึ้นอยู่กับข้อกำหนดเหล่านี้

โหมด UI ต่างๆ ของเครื่องมือเลือกรายชื่อติดต่อ
รูปที่ 1 อินเทอร์เฟซของเครื่องมือเลือกรายชื่อติดต่อจะปรับตามฟิลด์ข้อมูลที่ขอ (รายชื่อติดต่อเดียว รายชื่อติดต่อหลายรายการ และการเลือกหมายเลขโทรศัพท์หลายหมายเลข)

เลือกรายชื่อติดต่อเดียว

ในตัวอย่างนี้ แอปจะขอเฉพาะหมายเลขโทรศัพท์ เครื่องมือเลือกจะกรองรายการเพื่อแสดงเฉพาะรายชื่อติดต่อที่มีหมายเลขโทรศัพท์และให้ผู้ใช้เลือกหมายเลขที่ต้องการ

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

เลือกรายชื่อติดต่อหลายรายการ

หากต้องการเปิดใช้การเลือกหลายรายการ ให้เพิ่ม Intent.EXTRA_ALLOW_MULTIPLE พิเศษ คุณสามารถจำกัดจำนวนรายการที่ผู้ใช้เลือกได้ (ไม่บังคับ)

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)

จัดการผลลัพธ์

เมื่อผู้ใช้เลือกเสร็จแล้ว ระบบจะแสดงผล RESULT_OK และ URI ของเซสชัน URI นี้ให้สิทธิ์อ่านข้อมูลที่เลือกชั่วคราว

คุณค้นหา URI นี้ได้โดยใช้ ContentResolver มาตรฐาน Cursor ที่ได้จะมีฟิลด์ข้อมูลที่ขอและเป็นไปตามสคีมาของ 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()
}

ความเข้ากันได้แบบย้อนหลัง

สำหรับแอปที่กำหนดเป้าหมายเป็น Android 17 (API ระดับ 37) ขึ้นไป ระบบจะอัปเกรด Intent Intent.ACTION_PICK ที่มีอยู่โดยอัตโนมัติเพื่อใช้อินเทอร์เฟซเครื่องมือเลือกรายชื่อติดต่อใหม่

หากแอปใช้ ACTION_PICK อยู่แล้ว คุณไม่จำเป็นต้องเปลี่ยนโค้ดเพื่อรับ UI ใหม่ อย่างไรก็ตาม หากต้องการใช้ประโยชน์จากฟีเจอร์ใหม่ๆ เช่น การรับ Uri รายการเดียวเพื่อค้นหาข้อมูลรายชื่อติดต่อ การสลับระหว่างโปรไฟล์ส่วนตัวและโปรไฟล์งาน หรือคำขอฟิลด์ข้อมูลหลายรายการ คุณต้องอัปเดตการติดตั้งใช้งานเพื่อใช้ ContactsPickerSessionContract.ACTION_PICK_CONTACTS หรือ Intent Extra ใหม่

การทดสอบใน SDK เป้าหมายที่เก่ากว่า

คุณสามารถทดสอบลักษณะการทำงานใหม่ของเครื่องมือเลือกในอุปกรณ์ที่ใช้ Android 17 ขึ้นไปได้ แม้ว่าแอปจะกำหนดเป้าหมายเป็น SDK เวอร์ชันต่ำกว่าก็ตาม โดยเพิ่มบูลีน EXTRA_USE_SYSTEM_CONTACTS_PICKER ลงใน Intent ACTION_PICK

แนวทางปฏิบัติแนะนำ

  • ขอเฉพาะสิ่งที่คุณต้องการ: หากแอปของคุณต้องการส่ง SMS เท่านั้น ให้ขอ Phone.CONTENT_ITEM_TYPE เครื่องมือเลือกจะกรองรายชื่อติดต่อที่ไม่มีหมายเลขโทรศัพท์ออกโดยอัตโนมัติ ส่งผลให้ UI สะอาดตายิ่งขึ้นสำหรับผู้ใช้
  • การจัดการข้อมูลหลายรายการต่อรายชื่อติดต่อ: รายชื่อติดต่อแต่ละรายมักมีอีเมลหรือหมายเลขโทรศัพท์หลายรายการ ขอแนะนำให้จัดกลุ่มโดยใช้ ContactsContract.Contacts.LOOKUP_KEY เพื่อช่วยให้มั่นใจว่าข้อมูลเหล่านี้จะแสดงอย่างชัดเจนและเข้าใจง่ายสำหรับผู้ใช้ นอกจากนี้ คุณยังสามารถดึงข้อมูลป้ายกำกับที่เฉพาะเจาะจงสำหรับแต่ละรายการ (เช่น งานหรือส่วนตัว) เพื่อเสนอตัวเลือกที่ละเอียดยิ่งขึ้นภายในอินเทอร์เฟซของแอปได้
  • เก็บข้อมูลทันที: URI ของเซสชันจะให้สิทธิ์อ่านชั่วคราว หากต้องการเข้าถึงข้อมูลติดต่อนี้ในภายหลัง (หลังจากที่กระบวนการของแอปถูกปิด) แอปของคุณต้องเก็บข้อมูลรายชื่อติดต่อไว้
  • อย่าพึ่งพาข้อมูลบัญชี: ระบบจะตัดข้อมูลเมตาเฉพาะบัญชีออกจากผลลัพธ์เพื่อปกป้องความเป็นส่วนตัวของผู้ใช้และป้องกันการเก็บลายนิ้วมือ