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