পরিচিতি বাছাইকারী

Android Contact Picker হল একটি স্ট্যান্ডার্ড, ব্রাউজ করা যায় এমন ইন্টারফেস যার মাধ্যমে ব্যবহারকারীরা আপনার অ্যাপের সাথে পরিচিতি শেয়ার করতে পারেন। এটি Android 17 (API লেভেল 37) বা তার পরের যেকোনও ভার্সন চালানো ডিভাইসে উপলভ্য। এই পিকার, ব্যাপক READ_CONTACTS অনুমতির পরিবর্তে গোপনীয়তা রক্ষা করে এমন বিকল্প অফার করে। ব্যবহারকারীর সম্পূর্ণ অ্যাড্রেস বুক অ্যাক্সেস করার অনুরোধ করার পরিবর্তে, আপনার অ্যাপটি ফোন নম্বর বা ইমেল আইডির মতো প্রয়োজনীয় ডেটা ফিল্ড নির্দিষ্ট করে এবং ব্যবহারকারী শেয়ার করার জন্য নির্দিষ্ট পরিচিতি বেছে নেন। এটি আপনার অ্যাপকে শুধুমাত্র বেছে নেওয়া ডেটা পড়ার অ্যাক্সেস দেয়, এর ফলে বিল্ট-ইন সার্চ, প্রোফাইল পরিবর্তন ও একাধিক বেছে নেওয়ার মতো ফিচার সহ একই ধরনের ব্যবহারকারীর অভিজ্ঞতা প্রদান করার সময় গ্র্যানুলার কন্ট্রোল নিশ্চিত করা যায়। এর জন্য UI তৈরি বা ম্যানেজ করতে হয় না।

পরিচিতি বাছাইকারী ইন্টিগ্রেট করা

পরিচিতি বাছাইকারীকে ইন্টিগ্রেট করতে, ContactsPickerSessionContract.ACTION_PICK_CONTACTS ইনটেন্ট ব্যবহার করুন। এই ইনটেন্ট পিকার লঞ্চ করে এবং বেছে নেওয়া পরিচিতি আপনার অ্যাপে ফেরত পাঠায়।

পুরনো ACTION_PICK-এর মতো নয়, Contact Picker আপনাকে একই সাথে আপনার অ্যাপের প্রয়োজনীয় একাধিক ডেটা ফিল্ড নির্দিষ্ট করতে দেয়। আপনি এটি ContactsPickerSessionContract.EXTRA_REQUESTED_DATA_FIELDS ব্যবহার করে করেন, ContactsContract.CommonDataKinds-এ সংজ্ঞায়িত MIME ধরনের ArrayList<String> পাস করে।

সাধারণ MIME-এর ধরনের মধ্যে এগুলি অন্তর্ভুক্ত:

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

পিকার লঞ্চ করা

পিকার লঞ্চ করতে StartActivityForResult চুক্তির সাথে registerForActivityResult ব্যবহার করুন। একক বা একাধিক বেছে নেওয়ার অনুমতি দিতে আপনি ইনটেন্ট কনফিগার করতে পারবেন।

// 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 মোড
ছবি ১. পরিচিতি বাছাইকারী ইন্টারফেস, অনুরোধ করা ডেটা ফিল্ডের (সিঙ্গেল পরিচিতি, একাধিক পরিচিতি ও একাধিক ফোন নম্বর বেছে নেওয়া) সাথে মানিয়ে নেয়।

একটি পরিচিতি বেছে নিন

এই উদাহরণে, অ্যাপটি শুধুমাত্র ফোন নম্বর অ্যাক্সেস করার অনুরোধ করে। পিকার, তালিকা ফিল্টার করে শুধুমাত্র ফোন নম্বর সহ পরিচিতি দেখাবে এবং ব্যবহারকারীকে একটি নির্দিষ্ট নম্বর বেছে নিতে দেবে।

// 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 extra যোগ করুন। আপনি ঐচ্ছিকভাবে ব্যবহারকারীর বেছে নেওয়া আইটেমের সংখ্যা সীমিত করতে পারেন।

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 বেছে নেওয়া ডেটা সাময়িকভাবে পড়ার অ্যাক্সেস দেয়।

আপনি একটি স্ট্যান্ডার্ড ContentResolver ব্যবহার করে এই URI কোয়েরি করতে পারবেন। ফলাফলে 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.ACTION_PICKইন্টেন্ট অটোমেটিক আপগ্রেড করে।

আপনার অ্যাপে আগে থেকেই ACTION_PICK ব্যবহার করা হলে, নতুন UI পেতে আপনাকে কোড পরিবর্তন করতে হবে না। তবে, পরিচিতির ডেটা কোয়েরি করার জন্য একটি Uri পাওয়া, ব্যক্তিগত ও অফিসের প্রোফাইলের মধ্যে পরিবর্তন করা অথবা একাধিক ডেটা ফিল্ডের অনুরোধের মতো নতুন ফিচারের সুবিধা নিতে, আপনাকে ContactsPickerSessionContract.ACTION_PICK_CONTACTS অথবা নতুন ইনটেন্ট এক্সট্রা ব্যবহার করার জন্য আপনার ইমপ্লিমেন্টেশন আপডেট করতে হবে।

পুরনো টার্গেট SDK-তে পরীক্ষা করা

Android 17 ও এর পরের যেকোনও ভার্সনে চলা ডিভাইসে আপনি নতুন পিকারের আচরণ পরীক্ষা করে দেখতে পারেন। এমনকি, আপনার অ্যাপ যদি এর আগের কোনও SDK ভার্সনকে টার্গেট করে থাকে, তাহলেও আপনি ACTION_PICK ইনটেন্টে EXTRA_USE_SYSTEM_CONTACTS_PICKER boolean এক্সট্রা যোগ করে এটি করতে পারেন।

পেশাদার পদ্ধতি

  • যা প্রয়োজন শুধু সেই অনুমতিই চান: আপনার অ্যাপকে যদি শুধু এসএমএস পাঠাতে হয়, তাহলে Phone.CONTENT_ITEM_TYPE-এর জন্য অনুরোধ করুন। যেসব পরিচিতিতে ফোন নম্বর নেই, পিকার সেগুলি অটোমেটিক ফিল্টার করে দেবে। এর ফলে ব্যবহারকারী আরও পরিষ্কার UI দেখতে পাবেন।
  • প্রতিটি পরিচিতির জন্য একাধিক ডেটা এন্ট্রি ম্যানেজ করা: ব্যক্তিগত পরিচিতিতে প্রায়ই বিভিন্ন ইমেল আইডি বা ফোন নম্বর থাকে। এগুলি যাতে ব্যবহারকারীর কাছে স্পষ্ট ও সহজবোধ্যভাবে উপস্থাপন করা যায়, সেই জন্য এগুলিকে ContactsContract.Contacts.LOOKUP_KEY ব্যবহার করে গ্রুপে ভাগ করার সাজেশন দেওয়া হয়। এছাড়াও, আপনার অ্যাপের ইন্টারফেসের মধ্যে আরও গ্র্যানুলার বেছে নেওয়ার বিকল্প অফার করতে, আপনি প্রতিটি এন্ট্রির জন্য নির্দিষ্ট লেবেল (যেমন, অফিস বা ব্যক্তিগত) রিট্রিভ করতে পারবেন।
  • অবিলম্বে ডেটা পারসিস্ট করা: সেশন URI সাময়িক রিড পার্মিশন দেয়। পরে (আপনার অ্যাপ প্রসেস বন্ধ হয়ে যাওয়ার পরে) এই পরিচিতির তথ্য অ্যাক্সেস করার প্রয়োজন হলে, আপনার অ্যাপকে পরিচিতির ডেটা সেভ করে রাখতে হবে।
  • অ্যাকাউন্ট ডেটার উপর নির্ভর করবেন না: ব্যবহারকারীর গোপনীয়তা রক্ষা করতে এবং ফিঙ্গারপ্রিন্টিং প্রতিরোধ করতে, ফলাফল থেকে অ্যাকাউন্ট-নির্দিষ্ট মেটাডেটা সরিয়ে দেওয়া হয়।