انتخابگر مخاطب

«انتخابگر مخاطب Android» یک میانای استاندارد و مرورپذیر برای کاربران است تا مخاطبین را با برنامه شما هم‌رسانی کنند. این انتخابگر که در دستگاه‌های دارای Android 17 (سطح میانای برنامه‌سازی کاربردی 37) یا بالاتر دردسترس است، جایگزینی برای اجازه گسترده READ_CONTACTS ارائه می‌دهد که حریم خصوصی را حفظ می‌کند. به‌جای درخواست دسترسی به کل دفترچه نشانی کاربر، برنامه شما فیلدهای داده‌ای را که نیاز دارد (مثل شماره تلفن یا نشانی ایمیل) مشخص می‌کند و کاربر مخاطبین خاصی را برای هم‌رسانی انتخاب می‌کند. این کار به برنامه شما اجازه می‌دهد فقط به داده‌های انتخاب‌شده دسترسی خواندن داشته باشد و کنترل جزئی را تضمین می‌کند و درعین‌حال تجربه کاربری یکپارچه‌ای با قابلیت‌های جستجوی داخلی، تعویض نمایه، و انتخاب چندگانه بدون نیاز به ساختن یا نگهداری میانای کاربری ارائه می‌دهد.

یکپارچه‌سازی «انتخاب‌گر مخاطب»

برای ادغام «انتخابگر مخاطب»، از ContactsPickerSessionContract.ACTION_PICK_CONTACTS هدف استفاده کنید. این هدف انتخابگر را راه‌اندازی می‌کند و مخاطبین انتخاب‌شده را به برنامه شما برمی‌گرداند.

برخلاف 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 برای راه‌اندازی انتخابگر استفاده کنید. می‌توانید هدف را پیکربندی کنید تا انتخاب‌های تکی یا چندگانه را مجاز کند.

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

حالت انتخاب

واسط کاربر «انتخابگر مخاطب» براساس فیلدهای داده درخواستی تنظیم می‌شود. بسته به این الزامات، کاربران می‌توانند یا کل سابقه مخاطب را انتخاب کنند (وقتی به چند فیلد نیاز باشد) یا موارد داده خاصی را از اطلاعات مخاطب انتخاب کنند.

حالت‌های مختلف رابط کاربری «انتخاب‌گر مخاطب»
شکل ۱. میانای «انتخابگر مخاطب» با فیلدهای داده درخواستی (انتخاب یک مخاطب، چند مخاطب، و چند شماره تلفن) سازگار می‌شود.

انتخاب یک مخاطب

در این مثال، برنامه فقط شماره تلفن درخواست می‌کند. انتخابگر فهرست را فیلتر می‌کند تا فقط مخاطبین دارای شماره تلفن را نشان دهد و به کاربر اجازه می‌دهد شماره‌ای خاص را انتخاب کند.

// 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 و نشانی وب جلسه را برمی‌گرداند. این نشانی وب به داده‌های انتخاب‌شده دسترسی خواندن موقت می‌دهد.

می‌توانید این نشانی وب را بااستفاده از 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 (سطح میانای برنامه‌سازی کاربردی ۳۷) و بالاتر را هدف‌یابی می‌کنند، سیستم به‌طور خودکار قصد Intent.ACTION_PICK موجود را برای استفاده از میانای جدید «انتخاب‌گر مخاطب» ارتقا می‌دهد.

اگر برنامه‌تان ازقبل از ACTION_PICK استفاده می‌کند، برای دریافت کردن واسط کاربر جدید نیازی نیست کدتان را تغییر دهید. بااین‌حال، برای بهره‌مندی از ویژگی‌های جدید، مثل دریافت Uri واحد برای پُرسمان داده‌های مخاطب، جابه‌جایی بین نمایه شخصی و کاری، یا درخواست‌های چند فیلد داده، باید پیاده‌سازی‌تان را به‌روز کنید تا از ContactsPickerSessionContract.ACTION_PICK_CONTACTS یا افزوده‌های هدف جدید استفاده کنید.

آزمایش در کیت‌های توسعه نرم‌افزار هدف قدیمی‌تر

حتی اگر برنامه شما نسخه پایین‌تری از کیت توسعه نرم‌افزار را هدف‌یابی کند، می‌توانید با افزودن مقدار اضافی بولی EXTRA_USE_SYSTEM_CONTACTS_PICKER به هدف ACTION_PICK، عملکرد انتخابگر جدید را در دستگاه‌های دارای Android 17 و بالاتر آزمایش کنید.

روال‌های مطلوب

  • فقط آنچه را نیاز دارید درخواست کنید: اگر برنامه شما فقط نیاز به ارسال پیامک دارد، Phone.CONTENT_ITEM_TYPE را درخواست کنید. انتخابگر به‌طور خودکار مخاطبینی را که شماره تلفن ندارند فیلتر می‌کند و درنتیجه رابط کاربری تمیزتری برای کاربر ایجاد می‌شود.
  • مدیریت چندین ورودی داده برای هر مخاطب: مخاطبین فردی اغلب حاوی نشانی‌های ایمیل یا شماره تلفن‌های مختلف هستند. برای کمک به اینکه این موارد به‌صورت واضح و شهودی به کاربر ارائه شود، توصیه می‌شود آن‌ها را بااستفاده از ContactsContract.Contacts.LOOKUP_KEY گروه‌بندی کنید. علاوه‌براین، می‌توانید برچسب‌های خاصی را برای هر ورودی (مثل کاری یا شخصی) بازیابی کنید تا گزینه‌های انتخاب دقیق‌تری را در رابط برنامه‌تان ارائه دهید.
  • ماندگار کردن داده‌ها بلافاصله: «نشانی وب جلسه» اجازه خواندن موقت اعطا می‌کند. اگر بعداً (پس‌از بسته شدن فرایند برنامه) نیاز به دسترسی به این اطلاعات تماس داشتید، برنامه شما باید داده‌های تماس را حفظ کند.
  • به «داده‌های حساب» تکیه نکنید: برای محافظت از حریم خصوصی کاربر و جلوگیری از اثر انگشت، فراداده‌های مختص حساب از نتایج حذف می‌شود.