הכלי לבחירת אנשי קשר ב-Android הוא ממשק סטנדרטי שניתן לדפדף בו, שמאפשר למשתמשים לשתף אנשי קשר עם האפליקציה. הכלי זמין במכשירים עם Android בגרסה 17 (רמת API 37) ומעלה, והוא מאפשר לשמור על הפרטיות במקום להשתמש בהרשאה הרחבה READ_CONTACTS. במקום לבקש גישה לכל פנקס הכתובות של המשתמש, האפליקציה מציינת את שדות הנתונים שהיא צריכה, כמו מספרי טלפון או כתובות אימייל, והמשתמש בוחר אנשי קשר ספציפיים לשיתוף. כך האפליקציה מקבלת גישת קריאה רק לנתונים שנבחרו, מה שמבטיח שליטה פרטנית תוך מתן חוויית משתמש עקבית עם יכולות מובנות של חיפוש, החלפת פרופילים ובחירה מרובה, בלי צורך לבנות או לתחזק את ממשק המשתמש.
שילוב הכלי לבחירת אנשי קשר
כדי לשלב את הכלי לבחירת אנשי קשר, משתמשים ב-Intent ContactsPickerSessionContract.ACTION_PICK_CONTACTS.
ה-intent הזה מפעיל את בורר אנשי הקשר ומחזיר את אנשי הקשר שנבחרו לאפליקציה.
בניגוד ל-ACTION_PICK הקודם, הכלי לבחירת אנשי קשר מאפשר לציין כמה שדות נתונים שהאפליקציה צריכה בו-זמנית. כדי לעשות את זה, משתמשים ב-ContactsPickerSessionContract.EXTRA_PICK_CONTACTS_REQUESTED_DATA_FIELDS ומעבירים ArrayList<String> של סוגי MIME שמוגדרים ב-ContactsContract.CommonDataKinds.
סוגי MIME נפוצים כוללים:
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPEContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPEContactsContract.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)
}
}
}
מצב בחירה
ממשק המשתמש של הכלי לבחירת אנשי קשר משתנה בהתאם לשדות הנתונים המבוקשים. בהתאם לדרישות האלה, המשתמשים יכולים לבחור רשומת איש קשר שלמה כשצריך כמה שדות, או לבחור פריטי נתונים ספציפיים מתוך הפרטים של איש הקשר.
בחירת איש קשר אחד
בדוגמה הזו, האפליקציה מבקשת רק מספרי טלפון. הכלי לבחירת אנשי קשר יסנן את הרשימה כך שיוצגו רק אנשי קשר עם מספרי טלפון, ויאפשר למשתמש לבחור מספר ספציפי.
// 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)
בחירה של כמה אנשי קשר
כדי להפעיל בחירה מרובה, צריך להוסיף את ה-extra 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, אתם לא צריכים לשנות את הקוד כדי לקבל את ממשק המשתמש החדש. עם זאת, כדי ליהנות מתכונות חדשות, כמו קבלת Uri יחיד לשליחת שאילתות על נתוני אנשי קשר, מעבר בין פרופילים אישיים ופרופילים של עבודה או בקשות מרובות של שדות נתונים, צריך לעדכן את ההטמעה כך שתשתמש ב-ContactsPickerSessionContract.ACTION_PICK_CONTACTS או בתוספות החדשות של Intent.
בדיקה בערכות SDK ישנות יותר
אתם יכולים לבדוק את ההתנהגות החדשה של הכלי לבחירת קבצים במכשירים עם Android בגרסה 17 ואילך, גם אם האפליקציה שלכם מטרגטת גרסת SDK נמוכה יותר. כדי לעשות את זה, מוסיפים את הערך הבוליאני הנוסף EXTRA_USE_SYSTEM_CONTACTS_PICKER ל-Intent של ACTION_PICK.
שיטות מומלצות
- בקשו רק את מה שאתם צריכים: אם האפליקציה שלכם צריכה רק לשלוח SMS, בקשו את ההרשאה
Phone.CONTENT_ITEM_TYPE. הכלי לבחירת אנשי קשר יסנן באופן אוטומטי אנשי קשר שאין להם מספרי טלפון, וכך ממשק המשתמש יהיה נקי יותר. - ניהול כמה רשומות נתונים לכל איש קשר: אנשי קשר בודדים מכילים לעיתים קרובות כמה כתובות אימייל או מספרי טלפון. כדי לוודא שהם מוצגים למשתמש בצורה ברורה ואינטואיטיבית, מומלץ לקבץ אותם באמצעות
ContactsContract.Contacts.LOOKUP_KEY. בנוסף, אתם יכולים לאחזר תוויות ספציפיות לכל רשומה (כמו עבודה או אישי) כדי להציע אפשרויות בחירה מפורטות יותר בממשק של האפליקציה. - שמירת נתונים באופן מיידי: ה-URI של הסשן מעניק הרשאת קריאה זמנית. אם אתם צריכים לגשת לפרטים האלה ליצירת קשר בשלב מאוחר יותר (אחרי שהתהליך של האפליקציה מסתיים), האפליקציה צריכה לשמור את הנתונים של איש הקשר.
- אל תסתמכו על נתוני החשבון: כדי להגן על פרטיות המשתמשים ולמנוע זיהוי מכשירים, מטא-נתונים ספציפיים לחשבון מוסרים מהתוצאות.