ניהול אנשי הקשר

ספק אנשי הקשר הוא רכיב חזק וגמיש של Android שמנהל את המאגר המרכזי של המכשיר לנתונים על אנשים. הספק של אנשי הקשר הוא מקור הנתונים שמוצגים באפליקציית אנשי הקשר של המכשיר. בנוסף, יש לך אפשרות לגשת לנתונים שלו באפליקציה שלך ולהעביר נתונים בין המכשיר לשירותים באינטרנט. הספק מארח מגוון רחב של מקורות נתונים ומנסה לנהל כמה שיותר נתונים לגבי כל אדם, וכתוצאה מכך הארגון שלו מורכב. לכן, ה-API של הספק כולל קבוצה נרחבת של ממשקים וסיווגים של חוזים שמאפשרים גם אחזור וגם שינוי של נתונים.

במדריך הזה נסביר את הנושאים הבאים:

  • המבנה הבסיסי של הספק.
  • איך מאחזרים נתונים מהספק.
  • איך משנים נתונים אצל הספק.
  • איך לכתוב מתאם לסנכרון לצורך סנכרון נתונים מהשרת שלכם לספק אנשי הקשר.

במדריך הזה אנחנו יוצאים מנקודת הנחה שאתם מכירים את העקרונות הבסיסיים של ספקי תוכן ל-Android. למידע נוסף על ספקי תוכן ב-Android, אפשר לעיין במדריך מושגי היסוד בנושא ספקי תוכן.

הארגון של ניהול אנשי הקשר

'ספק אנשי הקשר' הוא רכיב של ספק תוכן ל-Android. מוצגים בו שלושה סוגי נתונים על אדם, וכל אחד מהם תואם לטבלה שמוצעת על ידי הספק, כפי שמוצג באיור 1:

איור 1. מבנה הטבלה של ספק אנשי הקשר.

בדרך כלל מתייחסים לשלושת הטבלאות לפי שמות הכיתות שלהן בחוזה. הכיתות מגדירות קבועים למזהי URI של תוכן, לשמות עמודות ולערכים של עמודות שבהם משתמשות הטבלאות:

טבלה אחת (ContactsContract.Contacts)
שורות שמייצגות אנשים שונים, על סמך צבירת שורות של אנשי קשר גולמיים.
טבלה אחת (ContactsContract.RawContacts)
שורות שמכילות סיכום של הנתונים של משתמש, ספציפיות לחשבון ולסוג.
טבלה אחת (ContactsContract.Data)
שורות שמכילות את הפרטים של איש הקשר הגולמי, כמו כתובות אימייל או מספרי טלפון.

הטבלאות האחרות שמיוצגות על ידי כיתות חוזה ב-ContactsContract הן טבלאות עזר שספק אנשי הקשר משתמש בהן כדי לנהל את הפעולות שלו או לתמוך בפונקציות ספציפיות באנשי הקשר או באפליקציות הטלפוניה של המכשיר.

אנשי קשר גולמיים

איש קשר גולמי מייצג נתונים של משתמש שמגיעים מסוג חשבון יחיד ומשם חשבון יחיד. מאחר ש'ספק אנשי הקשר' מאפשר יותר משירות אונליין אחד כמקור נתונים של אדם, ספק אנשי הקשר מאפשר ליצור כמה אנשי קשר גולמיים עבור אותו אדם. כמה אנשי קשר גולמיים מאפשרים למשתמש גם לשלב את הנתונים של אדם מסוים מכמה חשבונות מאותו סוג.

רוב הנתונים של איש קשר בפורמט גולמי לא מאוחסנים בטבלה ContactsContract.RawContacts. במקום זאת, הוא מאוחסן בשורה אחת או יותר בטבלה ContactsContract.Data. בכל שורת נתונים יש עמודה Data.RAW_CONTACT_ID שמכילה את הערך RawContacts._ID של שורת ההורה ContactsContract.RawContacts שלה.

עמודות חשובות של אנשי קשר בפורמט גולמי

העמודות החשובות בטבלה ContactsContract.RawContacts מפורטות בטבלה 1. יש לקרוא את ההערות שמופיעות אחרי הטבלה:

טבלה 1. עמודות חשובות של אנשי קשר גולמיים.

שם העמודה שימוש הערות
ACCOUNT_NAME שם החשבון של סוג החשבון שממנו הגיע איש הקשר הגולמי הזה. לדוגמה, שם החשבון של חשבון Google הוא אחת מכתובות Gmail של הבעלים של המכשיר. מידע נוסף זמין ברשומה הבאה של ACCOUNT_TYPE. הפורמט של השם הזה ספציפי לסוג החשבון. היא לא חייבת להיות כתובת אימייל.
ACCOUNT_TYPE סוג החשבון שממנו מגיע איש הקשר הגולמי הזה. לדוגמה, סוג החשבון של חשבון Google הוא com.google. חשוב לציין תמיד את סוג החשבון באמצעות מזהה דומיין של דומיין שבבעלותך או בשליטתך. כך תוכלו לוודא שסוג החשבון יהיה ייחודי. לרוב, לסוג חשבון שמציע נתוני אנשי קשר יש מתאם סנכרון משויך שמסנכרן עם ספק אנשי הקשר.
DELETED הדגל 'נמחק' של איש קשר בפורמט גולמי. הדגל הזה מאפשר לספק אנשי הקשר לשמור את השורה באופן פנימי עד שהמתאמים לסנכרון יוכלו למחוק את השורה מהשרתים שלהם, ואז למחוק את השורה מהמאגר.

הערות

הערות חשובות לגבי הטבלה ContactsContract.RawContacts:

  • השם הגולמי של איש הקשר לא נשמר בשורה שלו ב-ContactsContract.RawContacts. במקום זאת, הוא מאוחסן בטבלה ContactsContract.Data, בשורה של ContactsContract.CommonDataKinds.StructuredName. לאיש קשר גולמי יש רק שורה אחת מהסוג הזה בטבלה ContactsContract.Data.
  • זהירות: כדי להשתמש בנתוני החשבון שלכם בשורה של איש קשר גולמי, קודם צריך לרשום אותם ב-AccountManager. כדי לעשות זאת, צריך לבקש מהמשתמשים להוסיף את סוג החשבון ואת שם החשבון שלהם לרשימת החשבונות. אם לא עושים זאת, הספק של אנשי הקשר ימחק באופן אוטומטי את השורה הגולמית של איש הקשר.

    לדוגמה, אם אתם רוצים שהאפליקציה תשמור את נתוני אנשי הקשר של השירות מבוסס-האינטרנט שלכם בדומיין com.example.dataservice, והחשבון של המשתמש בשירות הוא becky.sharp@dataservice.example.com, המשתמש צריך להוסיף קודם את 'סוג' החשבון (com.example.dataservice) ואת 'שם' החשבון (becky.smart@dataservice.example.com) כדי שהאפליקציה תוכל להוסיף שורות של אנשי קשר גולמיים. אפשר להסביר את הדרישה הזו למשתמש במסמכי העזרה, או לבקש מהמשתמש להוסיף את הסוג והשם, או את שניהם. סוגי החשבונות ושמות החשבונות מתוארים בהרחבה בקטע הבא.

מקורות של נתונים גולמיים של אנשי קשר

כדי להבין איך פועלים אנשי הקשר הגולמיים, כדאי לקחת בחשבון את המשתמשת 'אמילי דיקינסון', שמוגדרת במכשיר שלה את שלושת חשבונות המשתמש הבאים:

  • emily.dickinson@gmail.com
  • emilyd@gmail.com
  • חשבון Twitter‏ "belle_of_amherst"

המשתמש הפעיל את האפשרות סנכרון אנשי קשר בכל שלושת החשבונות האלה בהגדרות חשבונות.

נניח שאמילי דיקינסון פותחת חלון דפדפן, מתחברת ל-Gmail בתור emily.dickinson@gmail.com, פותחת את אנשי הקשר ומוסיפה את 'תומס היגינסון'. בהמשך, היא מתחברת ל-Gmail בתור emilyd@gmail.com ושולחת אימייל אל 'Thomas Higginson', וכך הוא מתווסף באופן אוטומטי כאיש קשר. היא גם עוקבת אחרי 'colonel_tom' (מזהה Twitter של Thomas Higginson) ב-Twitter.

כתוצאה מהפעולה הזו, ניהול אנשי הקשר יוצר שלושה אנשי קשר גולמיים:

  1. איש קשר גולמי של "תומס היגינסון" שמשויך אל emily.dickinson@gmail.com. סוג חשבון המשתמש הוא Google.
  2. איש קשר גולמי שני של 'Thomas Higginson' שמשויך ל-emilyd@gmail.com. סוג חשבון המשתמש הוא גם Google. יש איש קשר גולמי שני, למרות שהשם זהה לשם קודם, כי האדם נוסף לחשבון משתמש אחר.
  3. איש קשר גולמי שלישי בשם 'Thomas Higginson' שמשויך לחשבון 'belle_of_amherst'. הסוג של חשבון המשתמש הוא Twitter.

נתונים

כפי שצוין קודם, הנתונים של איש קשר גולמי מאוחסנים בשורה ContactsContract.Data שמקושרת לערך _ID של איש הקשר הגולמי. כך אפשר ליצור כמה מופעים של אותו סוג נתונים, כמו כתובות אימייל או מספרי טלפון, לאותו איש קשר גולמי. לדוגמה, אם ל-'Thomas Higginson' ב-emilyd@gmail.com (שורת איש הקשר הגולמי של Thomas Higginson שמשויכת לחשבון Google emilyd@gmail.com) יש כתובת אימייל ביתית thigg@gmail.com וכתובת אימייל לעבודה thomas.higginson@gmail.com, ספק אנשי הקשר שומר את שתי השורות של כתובות האימייל ומקשר את שתיהן לאיש הקשר הגולמי.

שימו לב שסוגי נתונים שונים מאוחסנים בטבלה היחידה הזו. בטבלה ContactsContract.Data מופיעות כל השורות של השם לתצוגה, מספר הטלפון, כתובת האימייל, הכתובת למשלוח דואר, התמונה ופרטי האתר. כדי לעזור בניהול, בטבלה ContactsContract.Data יש כמה עמודות עם שמות תיאוריים ועמודות אחרות עם שמות כלליים. לתוכן של עמודת שם תיאורי יש אותה משמעות ללא קשר לסוג הנתונים בשורה, בעוד שלתוכן של עמודת שם כללי יש משמעויות שונות בהתאם לסוג הנתונים.

שמות תיאוריים של עמודות

דוגמאות לשמות עמודות תיאוריים:

RAW_CONTACT_ID
הערך של העמודה _ID של איש הקשר הגולמי של הנתונים האלה.
MIMETYPE
סוג הנתונים שמאוחסנים בשורה הזו, כפי שהוא מופיע כסוג MIME מותאם אישית. ספק אנשי הקשר משתמש בסוגי ה-MIME שמוגדרים בתת-הסוגים של ContactsContract.CommonDataKinds. סוגי ה-MIME האלה הם קוד פתוח, וכל אפליקציה או מתאם סנכרון שפועלים עם ספק אנשי הקשר יכולים להשתמש בהם.
IS_PRIMARY
אם שורת הנתונים מהסוג הזה יכולה להופיע יותר מפעם אחת באיש קשר גולמי, עמודת IS_PRIMARY מסמנת את שורת הנתונים שמכילה את הנתונים הראשיים של הסוג. לדוגמה, אם המשתמש לוחץ לחיצה ארוכה על מספר טלפון של איש קשר ובוחר באפשרות Set default (הגדרת ברירת מחדל), בשורה ContactsContract.Data שמכילה את המספר הזה יוגדר ערך שאינו אפס.IS_PRIMARY

שמות עמודות כלליים

יש 15 עמודות גנריות בשם DATA1 עד DATA15 שזמינות לכלל המשתמשים, וארבע עמודות גנריות נוספות SYNC1 עד SYNC4, שמיועדות רק לשימוש של מתאמי סנכרון. הקבועים הכלליים של שמות העמודות תמיד פועלים, ללא קשר לסוג הנתונים שהשורה מכילה.

העמודה DATA1 נוספה לאינדקס. הספק של אנשי הקשר תמיד משתמש בעמודה הזו עבור הנתונים שהספק מצפה שהם יהיו היעד השכיח ביותר של שאילתה. לדוגמה, בשורה של כתובת אימייל, העמודה הזו מכילה את כתובת האימייל בפועל.

לפי הסכמה, העמודה DATA15 מיועדת לאחסון נתוני אובייקט בינארי גדול (BLOB), כמו תמונות ממוזערות.

שמות של עמודות ספציפיות לסוג

כדי להקל על העבודה עם העמודות של סוג מסוים של שורה, ספק אנשי הקשר מספק גם קבועים של שמות עמודות ספציפיים לסוג, שמוגדרים בסוגי משנה של ContactsContract.CommonDataKinds. הקבועים נותנים פשוט שם קבוע שונה לאותו שם עמודה, כדי לעזור לגשת לנתונים בשורה של סוג מסוים.

לדוגמה, המחלקה ContactsContract.CommonDataKinds.Email מגדירה קבועים של שמות עמודות, לפי סוג, לשורה ContactsContract.Data עם סוג MIME Email.CONTENT_ITEM_TYPE. הכיתה מכילה את הקבוע ADDRESS לעמודה של כתובת האימייל. הערך בפועל של ADDRESS הוא 'data1', שהוא זהה לשם הגנרי של העמודה.

זהירות: אל תוסיפו נתונים מותאמים אישית משלכם לטבלה ContactsContract.Data באמצעות שורה עם אחד מסוגי ה-MIME המוגדרים מראש של הספק. אם תעשו זאת, יכול להיות שתאבדו את הנתונים או שתגרמו לבעיות תפקוד אצל הספק. לדוגמה, אסור להוסיף שורה עם סוג MIME‏ Email.CONTENT_ITEM_TYPE שמכילה שם משתמש במקום כתובת אימייל בעמודה DATA1. אם השתמשת בסוג MIME מותאם אישית בשורה, אפשר להגדיר שמות עמודות ספציפיים לכל סוג ולהשתמש בעמודות בכל אופן.

באיור 2 מוצג איך עמודות תיאור ועמודות נתונים מופיעות בשורה ContactsContract.Data, ואיך שמות עמודות ספציפיים לסוג 'חופפים' לשמות העמודות הכלליים.

איך שמות עמודות ספציפיים לסוג ממפים לשמות עמודות כלליים

איור 2. שמות של עמודות מסוגים ספציפיים ושמות כלליים של עמודות.

כיתות של שמות עמודות ספציפיות לסוג

בטבלה 2 מפורטות הכיתות הנפוצות ביותר של שמות עמודות ספציפיות לסוג:

טבלה 2. כיתות של שמות עמודות ספציפיות לסוג

מחלקת מיפוי סוג הנתונים הערות
ContactsContract.CommonDataKinds.StructuredName נתוני השם של איש הקשר הגולמי שמשויך לשורת הנתונים הזו. לאיש קשר בפורמט גולמי יש רק אחת מהשורות האלה.
ContactsContract.CommonDataKinds.Photo התמונה הראשית של איש הקשר הגולמי שמשויך לשורת הנתונים הזו. לאיש קשר בפורמט גולמי יש רק אחת מהשורות האלה.
ContactsContract.CommonDataKinds.Email כתובת אימייל של איש הקשר הגולמי המשויך לשורת הנתונים הזו. לאיש קשר גולמי יכולות להיות מספר כתובות אימייל.
ContactsContract.CommonDataKinds.StructuredPostal כתובת למשלוח דואר של איש הקשר הגולמי המשויך לשורת הנתונים הזו. לאיש קשר בפורמט גולמי יכולות להיות כמה כתובות.
ContactsContract.CommonDataKinds.GroupMembership מזהה שמקשר את איש הקשר הגולמי לאחת הקבוצות בספק אנשי הקשר. קבוצות הן תכונה אופציונלית של סוג חשבון ושם חשבון. הם מתוארים בפירוט רב יותר בקטע קבוצות של אנשי קשר.

אנשי קשר

הספק של אנשי הקשר משליך את שורות אנשי הקשר הגולמיים מכל סוגי החשבונות ומכל שמות החשבונות כדי ליצור איש קשר. כך ניתן להציג ולשנות את כל הנתונים שהמשתמש אסף לגבי המשתמשים. שירות ניהול אנשי הקשר מנהל את היצירה של שורות אנשי קשר חדשות ואת הצבירה של אנשי קשר גולמיים עם שורת איש קשר קיימת. אפליקציות ומתאמי סנכרון לא יכולים להוסיף אנשי קשר, וחלק מהעמודות בשורת איש קשר הן לקריאה בלבד.

הערה: אם תנסו להוסיף איש קשר לספק אנשי הקשר עם insert(), תקבלו חריגה מסוג UnsupportedOperationException. אם תנסו לעדכן עמודה שמופיעה בתור 'לקריאה בלבד', המערכת תתעלם מהעדכון.

הספק של אנשי הקשר יוצר איש קשר חדש בתגובה להוספה של איש קשר גולמי חדש, שלא תואם לאף איש קשר קיים. הספק עושה זאת גם אם הנתונים הגולמיים של איש קשר קיים משתנים באופן שגורם לכך שהם כבר לא תואמים לאיש הקשר שאליו הם היו מצורפים בעבר. אם אפליקציה או מתאם סנכרון יוצרים איש קשר חדש בפורמט גולמי שתואם לאיש קשר קיים, איש הקשר החדש בפורמט גולמי יתווסף לאיש הקשר הקיים.

הספק של אנשי הקשר מקשר שורה של איש קשר לשורות הגולמיות של אנשי הקשר בעזרת העמודה _ID של השורה של איש הקשר בטבלה Contacts. העמודה CONTACT_ID של טבלת אנשי הקשר הגולמית ContactsContract.RawContacts מכילה ערכי _ID בשורה של אנשי הקשר שמשויכת לכל שורה גולמית של אנשי קשר.

בטבלה ContactsContract.Contacts מופיעה גם העמודה LOOKUP_KEY שהיא קישור 'קבוע' לשורה של אנשי הקשר. מכיוון שספק אנשי הקשר שומר על אנשי הקשר באופן אוטומטי, הוא עשוי לשנות את הערך של _ID בשורה של איש הקשר בתגובה לאיסוף או לסנכרון. גם אם זה יקרה, ה-URI של התוכן CONTENT_LOOKUP_URI בשילוב עם LOOKUP_KEY של איש הקשר עדיין יצביע על שורת איש הקשר, כך שתוכלו להשתמש ב-LOOKUP_KEY כדי לשמור על קישורים לאנשי קשר 'מועדפים' וכן הלאה. לעמודה הזו יש פורמט משלה שאינו קשור לפורמט של העמודה _ID.

באיור 3 מוצגת היחסות בין שלוש הטבלאות הראשיות.

הטבלאות הראשיות של ניהול אנשי הקשר

איור 3. היחסים בין טבלאות Contacts,‏ Raw Contacts ו-Details.

זהירות: אם אתם מפרסמים את האפליקציה שלכם בחנות Google Play, או אם האפליקציה שלכם פועלת במכשיר עם Android 10 (רמת API 29) ואילך, חשוב לזכור שקבוצה מוגבלת של שדות ושל שיטות לנתוני אנשי קשר לא רלוונטית יותר.

בתנאים שצוינו, המערכת מוחקת מדי פעם את כל הערכים שנכתבו בשדות הנתונים הבאים:

גם ממשקי ה-API שמשמשים להגדרת שדות הנתונים שלמעלה לא תקפים יותר:

בנוסף, השדות הבאים לא מחזירים יותר אנשי קשר תדירים. חשוב לזכור שחלק מהשדות האלה משפיעים על דירוג אנשי הקשר רק אם אנשי הקשר הם חלק מסוג נתונים ספציפי.

אם האפליקציות שלכם ניגשות לשדות או לממשקי ה-API האלה או מעדכנות אותם, צריך להשתמש בשיטות חלופיות. לדוגמה, אפשר לעמוד בתנאי של תרחישי שימוש מסוימים באמצעות ספקי תוכן פרטיים או נתונים אחרים שמאוחסנים באפליקציה או במערכות הקצה העורפי.

כדי לוודא שהפונקציונליות של האפליקציה לא מושפעת מהשינוי הזה, אפשר לנקות את שדות הנתונים האלה באופן ידני. כדי לעשות זאת, מריצים את הפקודה הבאה של ADB במכשיר עם Android 4.1 (API ברמה 16) ואילך:

adb shell content delete \
--uri content://com.android.contacts/contacts/delete_usage

נתונים ממתאמי סנכרון

המשתמשים מזינים את פרטי אנשי הקשר ישירות במכשיר, אבל הנתונים עוברים גם לספק אנשי הקשר משירותי אינטרנט באמצעות מתאמי סנכרון, שמאפשרים העברה אוטומטית של נתונים בין המכשיר לשירותים. מתאמי הסנכרון פועלים ברקע בשליטת המערכת, והם קוראים לשיטות ContentResolver כדי לנהל את הנתונים.

ב-Android, שירות האינטרנט שאיתו פועל מתאם הסנכרון מזוהה לפי סוג החשבון. כל מתאם סנכרון פועל עם סוג חשבון אחד, אבל הוא יכול לתמוך בכמה שמות של חשבונות מהסוג הזה. סוגי החשבונות ושמות החשבונות מתוארים בקצרה בקטע מקורות הנתונים הגולמיים של אנשי קשר. ההגדרות הבאות מספקות פרטים נוספים ומתארות את הקשר בין סוג החשבון והשם שלו לבין מתאמי הסנכרון והשירותים.

סוג חשבון
מזהה שירות שבו המשתמש שמר נתונים. ברוב המקרים, המשתמש צריך לבצע אימות מול השירות. לדוגמה, 'אנשי קשר מחשבון Google' הוא סוג חשבון שמזוהה באמצעות הקוד google.com. הערך הזה תואם לסוג החשבון שמשמש את AccountManager.
שם החשבון
מזהה חשבון או כניסה מסוימים לסוג חשבון. חשבונות של אנשי קשר ב-Google הם כמו חשבונות Google, שבהם כתובת האימייל משמשת כשם החשבון. שירותים אחרים עשויים להשתמש בשם משתמש בן מילה אחת או במזהה מספרי.

סוגים של חשבונות לא חייבים להיות ייחודיים. משתמש יכול להגדיר כמה חשבונות של אנשי קשר ב-Google ולהוריד את הנתונים שלהם לספק אנשי הקשר. מצב כזה יכול לקרות אם למשתמש יש קבוצה אחת של אנשי קשר אישיים בשם חשבון אישי, וקבוצה אחרת לעבודה. שמות החשבונות הם בדרך כלל ייחודיים. יחד, הם מזהים תעבורת נתונים ספציפית בין ספק אנשי הקשר לבין שירות חיצוני.

אם אתם רוצים להעביר את הנתונים של השירות שלכם לספק אנשי הקשר, תצטרכו לכתוב מתאם סנכרון משלכם. הנושא הזה מוסבר בפירוט בקטע מתאמי סנכרון של ספקי אנשי קשר.

באיור 4 מוצגת ההתאמה של ספק אנשי הקשר לזרימת הנתונים על אנשים. בתיבה 'מתאמי סנכרון', כל מתאם מסומן לפי סוג החשבון שלו.

זרימת נתונים על אנשים

איור 4. זרימת הנתונים של 'ספק אנשי הקשר'.

הרשאות נדרשות

אפליקציות שרוצות לגשת לספק אנשי הקשר צריכות לבקש את ההרשאות הבאות:

הרשאת קריאה לטבלה אחת או יותר
READ_CONTACTS, מצוין ב-AndroidManifest.xml עם הרכיב <uses-permission> בתור <uses-permission android:name="android.permission.READ_CONTACTS">.
הרשאת כתיבה לטבלה אחת או יותר
WRITE_CONTACTS, שצוין ב-AndroidManifest.xml עם רכיב <uses-permission> בתור <uses-permission android:name="android.permission.WRITE_CONTACTS">.

ההרשאות האלה לא חלות על נתוני פרופיל המשתמש. מידע על פרופיל המשתמש וההרשאות הנדרשות שלו מוסבר בקטע הבא, פרופיל המשתמש.

חשוב לזכור שנתוני אנשי הקשר של המשתמש הם אישיים ורגישים. המשתמשים מודאגים לגבי הפרטיות שלהם, ולכן הם לא רוצים שאפליקציות יאספו נתונים עליהם או על אנשי הקשר שלהם. אם לא ברור למה אתם צריכים הרשאה לגשת לנתוני אנשי הקשר שלהם, הם עשויים לתת לאפליקציה שלכם דירוגים נמוכים או פשוט לסרב להתקין אותה.

פרופיל המשתמש

בטבלה ContactsContract.Contacts יש שורה אחת שמכילה את נתוני הפרופיל של משתמש המכשיר. הנתונים האלה מתארים את user של המכשיר ולא את אחד מאנשי הקשר של המשתמש. השורה של אנשי הקשר בפרופיל מקושרת לשורה של אנשי קשר גולמיים בכל מערכת שמשתמשת בפרופיל. כל שורה גולמית של איש קשר של פרופיל יכולה לכלול מספר שורות נתונים. משתנים קבועים לגישה לפרופיל המשתמש זמינים במחלקה ContactsContract.Profile.

כדי לגשת לפרופיל המשתמש נדרשות הרשאות מיוחדות. בנוסף להרשאות READ_CONTACTS ו-WRITE_CONTACTS שנדרשות לקריאה ולכתיבה, גישה לפרופיל המשתמש מחייבת הרשאות android.Manifest.permission#READ_PROFILE ו-android.Manifest.permission#WRITE_PROFILE לצורך גישת קריאה וכתיבה, בהתאמה.

חשוב לזכור: צריך להתייחס לפרופיל המשתמש כרגיש. ההרשאה android.Manifest.permission#READ_PROFILE מאפשרת לכם לגשת לנתונים המזהים אישית של משתמש המכשיר. חשוב לציין בתיאור האפליקציה למה דרושות לכם הרשאות גישה לפרופיל המשתמש.

כדי לאחזר את שורת איש הקשר שמכילה את הפרופיל של המשתמש, צריך להפעיל את הפונקציה ContentResolver.query(). מגדירים את URI התוכן כ-CONTENT_URI ולא מספקים קריטריונים לבחירה. אפשר גם להשתמש ב-URI של התוכן הזה כ-URI הבסיסי לאחזור אנשי קשר או נתונים גולמיים של הפרופיל. לדוגמה, קטע הקוד הזה מאחזר נתונים לפרופיל:

Kotlin

// Sets the columns to retrieve for the user profile
projection = arrayOf(
        ContactsContract.Profile._ID,
        ContactsContract.Profile.DISPLAY_NAME_PRIMARY,
        ContactsContract.Profile.LOOKUP_KEY,
        ContactsContract.Profile.PHOTO_THUMBNAIL_URI
)

// Retrieves the profile from the Contacts Provider
profileCursor = contentResolver.query(
        ContactsContract.Profile.CONTENT_URI,
        projection,
        null,
        null,
        null
)

Java

// Sets the columns to retrieve for the user profile
projection = new String[]
    {
        Profile._ID,
        Profile.DISPLAY_NAME_PRIMARY,
        Profile.LOOKUP_KEY,
        Profile.PHOTO_THUMBNAIL_URI
    };

// Retrieves the profile from the Contacts Provider
profileCursor =
        getContentResolver().query(
                Profile.CONTENT_URI,
                projection ,
                null,
                null,
                null);

הערה: אם מאחזרים כמה שורות של אנשי קשר, ואתם רוצים לקבוע אם אחת מהן היא פרופיל המשתמש, צריך לבדוק את העמודה IS_USER_PROFILE של השורה. העמודה הזו מוגדרת כ-1 אם איש הקשר הוא פרופיל המשתמש.

מטא-נתונים של ניהול אנשי הקשר

שירות ניהול אנשי הקשר מנהל נתונים שמתעדים את המצב של נתוני אנשי הקשר במאגר. המטא-נתונים האלה לגבי המאגר מאוחסנים במקומות שונים, כולל השורות בטבלאות Raw Contacts‏, Data ו-Contacts, בטבלה ContactsContract.Settings ובטבלה ContactsContract.SyncState. בטבלה הבאה מפורטת ההשפעה של כל אחד מהמטא-נתונים האלה:

טבלה 3 מטא-נתונים ב'ניהול אנשי הקשר'

טבלה עמודה ערכים משמעות
ContactsContract.RawContacts DIRTY '0' – לא השתנה מאז הסנכרון האחרון. מסמן אנשי קשר גולמיים שהשתנו במכשיר וצריך לסנכרן אותם בחזרה לשרת. הערך מוגדר באופן אוטומטי על ידי ספק אנשי הקשר כשאפליקציות Android מעדכנות שורה.

מתאמי סנכרון שמבצעים שינויים בטבלאות הנתונים או בפרטי איש הקשר הגולמיים צריכים תמיד לצרף את המחרוזת CALLER_IS_SYNCADAPTER ל-URI של התוכן שבו הם משתמשים. כך הספק לא יוכל לסמן שורות כ'לא נקיות'. אחרת, נראה שהשינויים במתאם הסנכרון הם שינויים מקומיים והם נשלחים לשרת, למרות שהשרת היה מקור השינוי.

'1' – השתנה מאז הסנכרון האחרון, צריך לסנכרן אותו חזרה לשרת.
ContactsContract.RawContacts VERSION מספר הגרסה של השורה הזו. ספק אנשי הקשר מגדיל את הערך הזה באופן אוטומטי בכל פעם שהשורה או הנתונים הקשורים שלה משתנים.
ContactsContract.Data DATA_VERSION מספר הגרסה של השורה הזו. הספק של אנשי הקשר מגדיל את הערך הזה באופן אוטומטי בכל פעם ששורת הנתונים משתנה.
ContactsContract.RawContacts SOURCE_ID ערך מחרוזת שמזהה באופן ייחודי את איש הקשר הגולמי הזה בחשבון שבו הוא נוצר. כשמתאם סנכרון יוצר איש קשר חדש בפורמט גולמי, צריך להגדיר בעמודה הזו את המזהה הייחודי של איש הקשר בפורמט גולמי בשרת. כשאפליקציה ל-Android יוצרת איש קשר גולמי חדש, האפליקציה צריכה להשאיר את העמודה הזו ריקה. הפעולה הזו מאותתת למתאם הסנכרון שהוא צריך ליצור איש קשר חדש בפורמט גולמי בשרת ולקבל ערך בשדה SOURCE_ID.

באופן ספציפי, מזהה המקור צריך להיות ייחודי לכל סוג חשבון, ועקבי במהלך הסנכרון:

  • ייחודי: לכל איש קשר גולמי בחשבון חייב להיות מזהה מקור משלו. אם לא יאכפו את הכלל הזה, יהיו בעיות באפליקציית אנשי הקשר. חשוב לזכור שלשני אנשי קשר גולמיים באותו סוג של חשבון יכול להיות אותו מזהה מקור. לדוגמה, לאיש הקשר הגולמי "תומס היגינסון" בחשבון emily.dickinson@gmail.com יכול להיות אותו מזהה מקור כמו איש הקשר הגולמי "תומס היגינסון" בחשבון emilyd@gmail.com.
  • יציב: מזהי המקור הם חלק קבוע מהנתונים של השירות אונליין לגבי איש הקשר הגולמי. לדוגמה, אם המשתמש מנקה את האחסון של אנשי הקשר בהגדרות האפליקציות ומבצע סנכרון מחדש, לאנשי הקשר הגולמיים המשוחזרים אמורים להיות אותם מזהי מקור כמו קודם. אם לא יאכפו את המדיניות הזו, מקשי הקיצור יפסיקו לפעול.
ContactsContract.Groups GROUP_VISIBLE '0' – אנשי הקשר בקבוצה הזו לא אמורים להיות גלויים בממשקי המשתמש של אפליקציות ל-Android. העמודה הזו מיועדת לתאימות לשרתים שמאפשרים למשתמש להסתיר אנשי קשר בקבוצות מסוימות.
'1' – אנשי הקשר בקבוצה הזו יכולים להיות גלויים בממשקי המשתמש של האפליקציה.
ContactsContract.Settings UNGROUPED_VISIBLE '0' – בחשבון הזה ובסוג החשבון הזה, אנשי קשר שלא שייכים לקבוצה לא גלויים לממשקי המשתמש של אפליקציות ל-Android. כברירת מחדל, אנשי קשר לא גלויים אם אף אחד מאנשי הקשר הגולמיים שלהם לא שייך לקבוצה (השתייכות לקבוצה של איש קשר גולמי מסומן בשורה אחת או יותר של ContactsContract.CommonDataKinds.GroupMembership בטבלה ContactsContract.Data). אם מגדירים את הדגל הזה בשורה ContactsContract.Settings בטבלה של סוג החשבון ושל החשבון, אפשר לאלץ את אנשי הקשר ללא קבוצות להיות גלויים. אחת מהשימושים בדגל הזה היא להציג אנשי קשר משרתי אימייל שלא משתמשים בקבוצות.
'1' – בחשבון הזה ובסוג החשבון הזה, אנשי קשר שלא שייכים לקבוצה גלויים לממשקי המשתמש של האפליקציות.
ContactsContract.SyncState (הכול) משתמשים בטבלה הזו כדי לאחסן מטא-נתונים של מתאם הסנכרון. באמצעות הטבלה הזו ניתן לאחסן באופן קבוע במכשיר את מצב הסנכרון ונתונים אחרים שקשורים לסנכרון.

גישת ספק לאנשי הקשר

בקטע הזה מפורטות הנחיות לגישה לנתונים מספק אנשי הקשר, עם דגש על הנושאים הבאים:

  • שאילתות על ישויות.
  • שינוי בכמות גדולה.
  • אחזור ושינוי של כוונות.
  • תקינות נתונים.

אפשר לקרוא בפירוט רב יותר גם על ביצוע שינויים באמצעות מתאם סנכרון בקטע מתאמי סנכרון של ספק אנשי קשר.

שליחת שאילתות על ישויות

מכיוון שטבלאות הספק של אנשי הקשר מאורגנות בהיררכיה, לרוב כדאי לאחזר שורה ואת כל השורות 'הצאצאיות' שמקושרות אליה. לדוגמה, כדי להציג את כל המידע על משתמש מסוים, יכול להיות שתרצו לאחזר את כל השורות של ContactsContract.RawContacts בשורה אחת של ContactsContract.Contacts, או את כל השורות של ContactsContract.CommonDataKinds.Email בשורה אחת של ContactsContract.RawContacts. כדי לאפשר זאת, ספק אנשי הקשר מציע מבנים של ישויות, שמשמשים כאיחודים של מסדי נתונים בין טבלאות.

ישות היא כמו טבלה המורכבת מעמודות נבחרות מטבלת הורה ומהטבלה הצאצאית שלה. כשמריצים שאילתה על ישות, צריך לספק תחזית וקריטריונים לחיפוש על סמך העמודות הזמינות מהישות. התוצאה היא Cursor שמכיל שורה אחת לכל שורה בטבלת הצאצא שאוחזרה. לדוגמה, אם שולחים שאילתה ל-ContactsContract.Contacts.Entity עם שם של איש קשר וכל השורות של ContactsContract.CommonDataKinds.Email עם כל אנשי הקשר הגולמיים של השם הזה, מקבלים חזרה Cursor שמכיל שורה אחת לכל שורה של ContactsContract.CommonDataKinds.Email.

ישויות מפשטות את השאילתות. באמצעות ישות, אפשר לאחזר בבת אחת את כל נתוני אנשי הקשר של איש קשר או של איש קשר גולמי, במקום להריץ קודם שאילתה בטבלת ההורה כדי לקבל מזהה, ואז להריץ שאילתה בטבלת הצאצא עם המזהה הזה. בנוסף, ספק אנשי הקשר מעבד שאילתה לגבי ישות בעסקה אחת, וכך מוודא שהנתונים שאוחזרו עקביים מבחינה פנימית.

הערה: בדרך כלל ישות לא מכילה את כל העמודות של טבלת ההורה וטבלת הצאצא. אם מנסים לעבוד עם שם עמודה שלא מופיע ברשימת הקבועים של שמות העמודות של הישות, מופיעה הערך Exception.

קטע הקוד הבא מראה איך לאחזר את כל שורות איש הקשר הגולמיות של איש קשר. קטע הקוד הוא חלק מאפליקציה גדולה יותר עם שתי פעילויות, 'main' ו-'detail'. בפעילות הראשית מוצגת רשימה של שורות של אנשי קשר. כשהמשתמש בוחר אחת מהן, הפעילות שולחת את המזהה שלה לפעילות המפורטת. בפעילות הפרטים נעשה שימוש במאפיין ContactsContract.Contacts.Entity כדי להציג את כל שורות הנתונים מכל אנשי הקשר הגולמיים המשויכים לאיש הקשר שנבחר.

קטע הקוד הזה נלקח מהפעילות 'פרטים':

Kotlin

...
    /*
     * Appends the entity path to the URI. In the case of the Contacts Provider, the
     * expected URI is content://com.google.contacts/#/entity (# is the ID value).
     */
    contactUri = Uri.withAppendedPath(
            contactUri,
            ContactsContract.Contacts.Entity.CONTENT_DIRECTORY
    )

    // Initializes the loader identified by LOADER_ID.
    loaderManager.initLoader(
            LOADER_ID,  // The identifier of the loader to initialize
            null,       // Arguments for the loader (in this case, none)
            this        // The context of the activity
    )

    // Creates a new cursor adapter to attach to the list view
    cursorAdapter = SimpleCursorAdapter(
            this,                       // the context of the activity
            R.layout.detail_list_item,  // the view item containing the detail widgets
            mCursor,                    // the backing cursor
            fromColumns,               // the columns in the cursor that provide the data
            toViews,                   // the views in the view item that display the data
            0)                          // flags

    // Sets the ListView's backing adapter.
    rawContactList.adapter = cursorAdapter
...
override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
    /*
     * Sets the columns to retrieve.
     * RAW_CONTACT_ID is included to identify the raw contact associated with the data row.
     * DATA1 contains the first column in the data row (usually the most important one).
     * MIMETYPE indicates the type of data in the data row.
     */
    val projection: Array<String> = arrayOf(
            ContactsContract.Contacts.Entity.RAW_CONTACT_ID,
            ContactsContract.Contacts.Entity.DATA1,
            ContactsContract.Contacts.Entity.MIMETYPE
    )

    /*
     * Sorts the retrieved cursor by raw contact id, to keep all data rows for a single raw
     * contact collated together.
     */
    val sortOrder = "${ContactsContract.Contacts.Entity.RAW_CONTACT_ID} ASC"

    /*
     * Returns a new CursorLoader. The arguments are similar to
     * ContentResolver.query(), except for the Context argument, which supplies the location of
     * the ContentResolver to use.
     */
    return CursorLoader(
            applicationContext, // The activity's context
            contactUri,        // The entity content URI for a single contact
            projection,         // The columns to retrieve
            null,               // Retrieve all the raw contacts and their data rows.
            null,               //
            sortOrder           // Sort by the raw contact ID.
    )
}

Java

...
    /*
     * Appends the entity path to the URI. In the case of the Contacts Provider, the
     * expected URI is content://com.google.contacts/#/entity (# is the ID value).
     */
    contactUri = Uri.withAppendedPath(
            contactUri,
            ContactsContract.Contacts.Entity.CONTENT_DIRECTORY);

    // Initializes the loader identified by LOADER_ID.
    getLoaderManager().initLoader(
            LOADER_ID,  // The identifier of the loader to initialize
            null,       // Arguments for the loader (in this case, none)
            this);      // The context of the activity

    // Creates a new cursor adapter to attach to the list view
    cursorAdapter = new SimpleCursorAdapter(
            this,                        // the context of the activity
            R.layout.detail_list_item,   // the view item containing the detail widgets
            mCursor,                     // the backing cursor
            fromColumns,                // the columns in the cursor that provide the data
            toViews,                    // the views in the view item that display the data
            0);                          // flags

    // Sets the ListView's backing adapter.
    rawContactList.setAdapter(cursorAdapter);
...
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {

    /*
     * Sets the columns to retrieve.
     * RAW_CONTACT_ID is included to identify the raw contact associated with the data row.
     * DATA1 contains the first column in the data row (usually the most important one).
     * MIMETYPE indicates the type of data in the data row.
     */
    String[] projection =
        {
            ContactsContract.Contacts.Entity.RAW_CONTACT_ID,
            ContactsContract.Contacts.Entity.DATA1,
            ContactsContract.Contacts.Entity.MIMETYPE
        };

    /*
     * Sorts the retrieved cursor by raw contact id, to keep all data rows for a single raw
     * contact collated together.
     */
    String sortOrder =
            ContactsContract.Contacts.Entity.RAW_CONTACT_ID +
            " ASC";

    /*
     * Returns a new CursorLoader. The arguments are similar to
     * ContentResolver.query(), except for the Context argument, which supplies the location of
     * the ContentResolver to use.
     */
    return new CursorLoader(
            getApplicationContext(),  // The activity's context
            contactUri,              // The entity content URI for a single contact
            projection,               // The columns to retrieve
            null,                     // Retrieve all the raw contacts and their data rows.
            null,                     //
            sortOrder);               // Sort by the raw contact ID.
}

בסיום הטעינה, LoaderManager מפעילה קריאה חוזרת (callback) ל-onLoadFinished(). אחד מהארגומנטים הנכנסים לשיטה הזו הוא Cursor עם תוצאות השאילתה. באפליקציה שלך אפשר לקבל את הנתונים מה-Cursor כדי להציג אותו או להמשיך איתו.

שינוי בכמות גדולה

כשהדבר אפשרי, מומלץ להוסיף, לעדכן ולמחוק נתונים ב-Contacts Provider ב'מצב באצווה'. לשם כך, יוצרים ArrayList של אובייקטים מסוג ContentProviderOperation ומפעילים את applyBatch(). בגלל שהספק של אנשי הקשר מבצע את כל הפעולות ב-applyBatch() בטרנזקציה אחת, השינויים שלכם אף פעם לא ישאירו את מאגר אנשי הקשר במצב לא עקבי. שינוי בכמות גדולה מאפשר גם להוסיף בו-זמנית איש קשר בסיסי ואת פרטיו המפורטים.

הערה: כדי לשנות איש קשר גולמי יחיד, מומלץ לשלוח כוונה (intent) לאפליקציית אנשי הקשר של המכשיר במקום לטפל בשינוי באפליקציה שלכם. אפשר לקרוא הסבר מפורט יותר בקטע אחזור ושינוי באמצעות כוונות.

נקודות תפוקה

שינוי באצווה שמכיל מספר גדול של פעולות עלול לחסום תהליכים אחרים, וכתוצאה מכך חוויית המשתמש הכוללת תהיה גרועה. כדי לארגן את כל השינויים שרוצים לבצע ברשימות נפרדות ספורות ככל האפשר, ובמקביל למנוע מהם לחסום את המערכת, צריך להגדיר נקודות תשואה לפעולה אחת או יותר. נקודת כניעה היא אובייקט ContentProviderOperation שהערך שלו בשדה isYieldAllowed() מוגדר כ-true. כשספק אנשי הקשר נתקל בנקודת תפוקה, הוא משהה את העבודה שלו כדי לאפשר לתהליכים אחרים להריץ את הטרנזקציה הנוכחית. כשהספק מתחיל שוב, הוא ממשיך בפעולה הבאה ב-ArrayList ומתחיל עסקה חדשה.

נקודות תפוקה מובילות ליותר מעסקה אחת בכל שיחה אל applyBatch(). לכן צריך להגדיר נקודת תפוקה לפעולה האחרונה עבור קבוצה של שורות קשורות. לדוגמה, צריך להגדיר נקודת יעד לפעולה האחרונה בקבוצה שמוסיפה שורות של אנשי קשר גולמיים ושורות הנתונים המשויכות שלהם, או לפעולה האחרונה בקבוצת שורות שקשורות לאיש קשר יחיד.

נקודות עצירה הן גם יחידה של פעולה אטומית. כל הגישה בין שתי נקודות תשואה תצליח או תיכשל כיחידה אחת. אם לא מגדירים נקודות ייצור, הפעולה האטומית הקטנה ביותר היא כל קבוצת הפעולות. אם משתמשים בנקודות ייצור, אפשר למנוע מפעולות לפגוע בביצועי המערכת, ובמקביל לוודא שקבוצת משנה של פעולות היא אטומית.

שינוי הפניות קודמות

כשמוסיפים שורה גולמית חדשה של איש קשר ואת שורות הנתונים שמשויכות אליה, כקבוצה של ContentProviderOperation אובייקטים, צריך לקשר את שורות הנתונים לשורה הגולמית של איש הקשר על ידי הוספת הערך _ID של איש הקשר הגולמי כערך RAW_CONTACT_ID. עם זאת, הערך הזה לא זמין כשיוצרים את הערך ContentProviderOperation של שורת הנתונים, כי עדיין לא החלתם את הערך ContentProviderOperation על השורה הגולמית של איש הקשר. כדי לעקוף את הבעיה, לכיתה ContentProviderOperation.Builder יש את השיטה withValueBackReference(). השיטה הזו מאפשרת להוסיף או לשנות עמודה עם התוצאה של פעולה קודמת.

לשיטה withValueBackReference() יש שני ארגומנטים:

key
המפתח של צמד מפתח/ערך. הערך של הארגומנט הזה צריך להיות שם של עמודה בטבלה שאתם משנים.
previousResult
האינדקס שמתחיל בספרה אפס של ערך במערך של אובייקטים מסוג ContentProviderResult מ-applyBatch(). כשמפעילים את פעולות האצווה, התוצאה של כל פעולה מאוחסנת במערך ביניים של תוצאות. הערך previousResult הוא האינדקס של אחת מהתוצאות האלה, שמאוחזר ונשמר עם הערך key. כך תוכלו להוסיף רשומת איש קשר חדשה ללא עיצוב ולקבל בחזרה את הערך של _ID, ואז ליצור "הפניה לאחור" לערך כשמוסיפים שורה ContactsContract.Data.

מערך התוצאות כולו נוצר בקריאה הראשונה ל-applyBatch(), בגודל שווה לגודל של ArrayList של אובייקטי ContentProviderOperation שסיפקתם. עם זאת, כל הרכיבים במערך התוצאות מוגדרים ל-null, ואם תנסו לבצע הפניה חזרה לתוצאה של פעולה שעדיין לא הוחלה, withValueBackReference() תקפיץ את הערך Exception.

בקטעי הקוד הבאים מוסבר איך להוסיף אנשי קשר ונתונים גולמיים חדשים באצווה. הם כוללים קוד שיוצר נקודת תפוקה ומשתמשת בהפניה חזרה.

קטע הקוד הראשון מאחזר את פרטי הקשר מממשק המשתמש. בשלב הזה, המשתמש כבר בחר את החשבון שאליו צריך להוסיף את איש הקשר הגולמי החדש.

Kotlin

// Creates a contact entry from the current UI values, using the currently-selected account.
private fun createContactEntry() {
    /*
     * Gets values from the UI
     */
    val name = contactNameEditText.text.toString()
    val phone = contactPhoneEditText.text.toString()
    val email = contactEmailEditText.text.toString()

    val phoneType: String = contactPhoneTypes[mContactPhoneTypeSpinner.selectedItemPosition]

    val emailType: String = contactEmailTypes[mContactEmailTypeSpinner.selectedItemPosition]

Java

// Creates a contact entry from the current UI values, using the currently-selected account.
protected void createContactEntry() {
    /*
     * Gets values from the UI
     */
    String name = contactNameEditText.getText().toString();
    String phone = contactPhoneEditText.getText().toString();
    String email = contactEmailEditText.getText().toString();

    int phoneType = contactPhoneTypes.get(
            contactPhoneTypeSpinner.getSelectedItemPosition());

    int emailType = contactEmailTypes.get(
            contactEmailTypeSpinner.getSelectedItemPosition());

קטע הקוד הבא יוצר פעולה להוספת שורת איש הקשר הגולמי לטבלה ContactsContract.RawContacts:

Kotlin

    /*
     * Prepares the batch operation for inserting a new raw contact and its data. Even if
     * the Contacts Provider does not have any data for this person, you can't add a Contact,
     * only a raw contact. The Contacts Provider will then add a Contact automatically.
     */

    // Creates a new array of ContentProviderOperation objects.
    val ops = arrayListOf<ContentProviderOperation>()

    /*
     * Creates a new raw contact with its account type (server type) and account name
     * (user's account). Remember that the display name is not stored in this row, but in a
     * StructuredName data row. No other data is required.
     */
    var op: ContentProviderOperation.Builder =
            ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
                    .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.name)
                    .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.type)

    // Builds the operation and adds it to the array of operations
    ops.add(op.build())

Java

    /*
     * Prepares the batch operation for inserting a new raw contact and its data. Even if
     * the Contacts Provider does not have any data for this person, you can't add a Contact,
     * only a raw contact. The Contacts Provider will then add a Contact automatically.
     */

     // Creates a new array of ContentProviderOperation objects.
    ArrayList<ContentProviderOperation> ops =
            new ArrayList<ContentProviderOperation>();

    /*
     * Creates a new raw contact with its account type (server type) and account name
     * (user's account). Remember that the display name is not stored in this row, but in a
     * StructuredName data row. No other data is required.
     */
    ContentProviderOperation.Builder op =
            ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
            .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.getType())
            .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.getName());

    // Builds the operation and adds it to the array of operations
    ops.add(op.build());

בשלב הבא, הקוד יוצר שורות נתונים לשורות של השם המוצג, הטלפון וכתובת האימייל.

כל אובייקט של בונה הפעולות משתמש ב-withValueBackReference() כדי לקבל את RAW_CONTACT_ID. נקודות העזרה מפנות לאובייקט ContentProviderResult מהפעולה הראשונה, שמוסיפה את שורת איש הקשר הגולמי ומחזירה את הערך החדש של _ID. כתוצאה מכך, כל שורת נתונים מקושרת באופן אוטומטי באמצעות RAW_CONTACT_ID לשורה החדשה ContactsContract.RawContacts שאליה היא שייכת.

האובייקט ContentProviderOperation.Builder שמוסיף את שורת האימייל מסומן ב-withYieldAllowed(), שמגדיר נקודת תשואה:

Kotlin

    // Creates the display name for the new raw contact, as a StructuredName data row.
    op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * withValueBackReference sets the value of the first argument to the value of
             * the ContentProviderResult indexed by the second argument. In this particular
             * call, the raw contact ID column of the StructuredName data row is set to the
             * value of the result returned by the first operation, which is the one that
             * actually adds the raw contact row.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to StructuredName
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)

            // Sets the data row's display name to the name in the UI.
            .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name)

    // Builds the operation and adds it to the array of operations
    ops.add(op.build())

    // Inserts the specified phone number and type as a Phone data row
    op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * Sets the value of the raw contact id column to the new raw contact ID returned
             * by the first operation in the batch.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to Phone
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)

            // Sets the phone number and type
            .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
            .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType)

    // Builds the operation and adds it to the array of operations
    ops.add(op.build())

    // Inserts the specified email and type as a Phone data row
    op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * Sets the value of the raw contact id column to the new raw contact ID returned
             * by the first operation in the batch.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to Email
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)

            // Sets the email address and type
            .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email)
            .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType)

    /*
     * Demonstrates a yield point. At the end of this insert, the batch operation's thread
     * will yield priority to other threads. Use after every set of operations that affect a
     * single contact, to avoid degrading performance.
     */
    op.withYieldAllowed(true)

    // Builds the operation and adds it to the array of operations
    ops.add(op.build())

Java

    // Creates the display name for the new raw contact, as a StructuredName data row.
    op =
            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * withValueBackReference sets the value of the first argument to the value of
             * the ContentProviderResult indexed by the second argument. In this particular
             * call, the raw contact ID column of the StructuredName data row is set to the
             * value of the result returned by the first operation, which is the one that
             * actually adds the raw contact row.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to StructuredName
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)

            // Sets the data row's display name to the name in the UI.
            .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name);

    // Builds the operation and adds it to the array of operations
    ops.add(op.build());

    // Inserts the specified phone number and type as a Phone data row
    op =
            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * Sets the value of the raw contact id column to the new raw contact ID returned
             * by the first operation in the batch.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to Phone
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)

            // Sets the phone number and type
            .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
            .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType);

    // Builds the operation and adds it to the array of operations
    ops.add(op.build());

    // Inserts the specified email and type as a Phone data row
    op =
            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * Sets the value of the raw contact id column to the new raw contact ID returned
             * by the first operation in the batch.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to Email
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)

            // Sets the email address and type
            .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email)
            .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType);

    /*
     * Demonstrates a yield point. At the end of this insert, the batch operation's thread
     * will yield priority to other threads. Use after every set of operations that affect a
     * single contact, to avoid degrading performance.
     */
    op.withYieldAllowed(true);

    // Builds the operation and adds it to the array of operations
    ops.add(op.build());

קטע הקוד האחרון מציג את הקריאה applyBatch(), שמוסיפה את שורות הנתונים ואיש הקשר הגולמי החדש.

Kotlin

    // Ask the Contacts Provider to create a new contact
    Log.d(TAG, "Selected account: ${mSelectedAccount.name} (${mSelectedAccount.type})")
    Log.d(TAG, "Creating contact: $name")

    /*
     * Applies the array of ContentProviderOperation objects in batch. The results are
     * discarded.
     */
    try {
        contentResolver.applyBatch(ContactsContract.AUTHORITY, ops)
    } catch (e: Exception) {
        // Display a warning
        val txt: String = getString(R.string.contactCreationFailure)
        Toast.makeText(applicationContext, txt, Toast.LENGTH_SHORT).show()

        // Log exception
        Log.e(TAG, "Exception encountered while inserting contact: $e")
    }
}

Java

    // Ask the Contacts Provider to create a new contact
    Log.d(TAG,"Selected account: " + selectedAccount.getName() + " (" +
            selectedAccount.getType() + ")");
    Log.d(TAG,"Creating contact: " + name);

    /*
     * Applies the array of ContentProviderOperation objects in batch. The results are
     * discarded.
     */
    try {

            getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
    } catch (Exception e) {

            // Display a warning
            Context ctx = getApplicationContext();

            CharSequence txt = getString(R.string.contactCreationFailure);
            int duration = Toast.LENGTH_SHORT;
            Toast toast = Toast.makeText(ctx, txt, duration);
            toast.show();

            // Log exception
            Log.e(TAG, "Exception encountered while inserting contact: " + e);
    }
}

פעולות באצווה מאפשרות גם להטמיע בקרה אופטימיסטית על בו-זמניות, שיטה להחיל עסקאות שינוי בלי לנעול את המאגר הבסיסי. כדי להשתמש בשיטה הזו, מחילים את העסקה ואז בודקים אם בוצעו בו-זמנית שינויים אחרים. אם הבחנת בשינוי לא עקבי, עליך לבטל את העסקה ולנסות שוב.

בקרה אופטימיסטית על בו-זמניות שימושית במכשיר נייד, שבו יש רק משתמש אחד בכל פעם, והגישות בו-זמניות למאגר נתונים נדירות. מכיוון שאין שימוש בנעילה, לא מתבזבז זמן על הגדרת מנעולים או על המתנה לעסקאות אחרות כדי לבטל את המנעולים שלהן.

כדי להשתמש בבקרת בו-זמניות אופטימית כשמעדכנים שורה אחת של ContactsContract.RawContacts, מבצעים את השלבים הבאים:

  1. מאחזרים את העמודה VERSION של איש הקשר הגולמי יחד עם הנתונים האחרים שאתם מאחזרים.
  2. יוצרים אובייקט ContentProviderOperation.Builder שמתאים לאכיפת אילוץ באמצעות השיטה newAssertQuery(Uri). ב-URI של התוכן, משתמשים ב-RawContacts.CONTENT_URI עם ה-_ID של איש הקשר הגולמי שמצורף אליו.
  3. באובייקט ContentProviderOperation.Builder, צריך להפעיל את הפונקציה withValue() כדי להשוות את העמודה VERSION למספר הגרסה שאתם מקבלים.
  4. לאותו ContentProviderOperation.Builder, צריך להפעיל את withExpectedCount() כדי לוודא שרק שורה אחת נבדקת על ידי ההצהרה הזו.
  5. קוראים ל-build() כדי ליצור את האובייקט ContentProviderOperation, ואז מוסיפים את האובייקט הזה כאובייקט הראשון ב-ArrayList ששולחים ל-applyBatch().
  6. מחילים את העסקה באצווה.

אם השורה הגולמית של איש הקשר מתעדכנת על ידי פעולה אחרת בין מועד קריאת השורה לבין הזמן שבו מנסים לשנות אותה, ה'טענה' ContentProviderOperation תיכשל וכל הפעולות של כל הפעולות יגובו. לאחר מכן תוכלו לנסות שוב את האצווה או לבצע פעולה אחרת.

קטע הקוד הבא מדגים איך ליצור טענת נכונות (assert)‏ ContentProviderOperation אחרי ששולחים שאילתה ליצירת איש קשר גולמי יחיד באמצעות CursorLoader:

Kotlin

/*
 * The application uses CursorLoader to query the raw contacts table. The system calls this method
 * when the load is finished.
 */
override fun onLoadFinished(loader: Loader<Cursor>, cursor: Cursor) {
    // Gets the raw contact's _ID and VERSION values
    rawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID))
    mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION))
}

...

// Sets up a Uri for the assert operation
val rawContactUri: Uri = ContentUris.withAppendedId(
        ContactsContract.RawContacts.CONTENT_URI,
        rawContactID
)

// Creates a builder for the assert operation
val assertOp: ContentProviderOperation.Builder =
        ContentProviderOperation.newAssertQuery(rawContactUri).apply {
            // Adds the assertions to the assert operation: checks the version
            withValue(SyncColumns.VERSION, mVersion)

            // and count of rows tested
            withExpectedCount(1)
        }

// Creates an ArrayList to hold the ContentProviderOperation objects
val ops = arrayListOf<ContentProviderOperation>()

ops.add(assertOp.build())

// You would add the rest of your batch operations to "ops" here

...

// Applies the batch. If the assert fails, an Exception is thrown
try {
    val results: Array<ContentProviderResult> = contentResolver.applyBatch(AUTHORITY, ops)
} catch (e: OperationApplicationException) {
    // Actions you want to take if the assert operation fails go here
}

Java

/*
 * The application uses CursorLoader to query the raw contacts table. The system calls this method
 * when the load is finished.
 */
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {

    // Gets the raw contact's _ID and VERSION values
    rawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID));
    mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION));
}

...

// Sets up a Uri for the assert operation
Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactID);

// Creates a builder for the assert operation
ContentProviderOperation.Builder assertOp = ContentProviderOperation.newAssertQuery(rawContactUri);

// Adds the assertions to the assert operation: checks the version and count of rows tested
assertOp.withValue(SyncColumns.VERSION, mVersion);
assertOp.withExpectedCount(1);

// Creates an ArrayList to hold the ContentProviderOperation objects
ArrayList ops = new ArrayList<ContentProviderOperation>;

ops.add(assertOp.build());

// You would add the rest of your batch operations to "ops" here

...

// Applies the batch. If the assert fails, an Exception is thrown
try
    {
        ContentProviderResult[] results =
                getContentResolver().applyBatch(AUTHORITY, ops);

    } catch (OperationApplicationException e) {

        // Actions you want to take if the assert operation fails go here
    }

אחזור ושינוי באמצעות כוונות

שליחת כוונה לאפליקציית אנשי הקשר של המכשיר מאפשרת לכם לגשת לספק אנשי הקשר באופן עקיף. הכוונה מפעילה את ממשק המשתמש של אפליקציית אנשי הקשר במכשיר, שבו המשתמשים יכולים לבצע פעולות שקשורות לאנשי הקשר. עם סוג הגישה הזה, המשתמשים יכולים:

  • בוחרים איש קשר מהרשימה ומחזירים אותו לאפליקציה לצורך המשך עבודה.
  • עריכת הנתונים של איש קשר קיים.
  • להוסיף איש קשר חדש ללא עיבוד לכל אחד מהחשבונות שלהם.
  • מחיקת נתונים של איש קשר או של אנשי קשר.

אם המשתמש מוסיף או מעדכן נתונים, אפשר לאסוף את הנתונים קודם ולשלוח אותם כחלק מהכוונה.

כשמשתמשים בכוונות (intents) כדי לגשת לספק אנשי הקשר דרך אפליקציית אנשי הקשר של המכשיר, לא צריך לכתוב ממשק משתמש או קוד משלכם כדי לגשת לספק. בנוסף, לא צריך לבקש הרשאה לקריאה או לכתיבה מהספק. אפליקציית 'אנשי קשר' במכשיר יכולה להעניק לכם הרשאות קריאה של איש קשר מסוים. אם מבצעים שינויים בספק דרך אפליקציה אחרת, אין צורך בהרשאות כתיבה.

התהליך הכללי לשליחת כוונה לגשת לספק מתואר בפירוט במדריך יסודות של ספקי תוכן בקטע 'גישה לנתונים באמצעות כוונות'. הפעולה, סוג ה-MIME וערכי הנתונים שבהם משתמשים במשימות הזמינות מסוכמים בטבלה 4. ערכי התוספות שבהם אפשר להשתמש עם putExtra() מופיעים במסמכי העזרה של ContactsContract.Intents.Insert:

טבלה 4. כוונות של ספק אנשי קשר.

משימה פעולה נתונים סוג MIME הערות
בחירת איש קשר מרשימה ACTION_PICK אחת מהאפשרויות הבאות:
  • Contacts.CONTENT_URI, שתוצג רשימת אנשי קשר.
  • Phone.CONTENT_URI,‏ שמציגה רשימה של מספרי טלפון של איש קשר בפורמט גולמי.
  • StructuredPostal.CONTENT_URI,‏ שמציגה רשימה של כתובות למשלוח דואר של איש קשר בפורמט גולמי.
  • Email.CONTENT_URI,‏ שמציגה רשימה של כתובות אימייל של איש קשר בפורמט גולמי.
צולם בלי מבזק הצגת רשימה של אנשי קשר גולמיים או רשימה של נתונים מאיש קשר גולמי, בהתאם לסוג ה-URI של התוכן שסיפקתם.

קוראים לפונקציה startActivityForResult(), שמחזירה את כתובת ה-URI של התוכן של השורה שנבחרה. פורמט ה-URI הוא ה-URI של תוכן הטבלה עם ה-LOOKUP_ID של השורה שמצורף אליו. אפליקציית אנשי הקשר במכשיר מעבירה את הרשאות הקריאה והכתיבה ל-URI של התוכן הזה למשך כל תקופת הפעילות. מידע נוסף זמין במדריך יסודות לספקי תוכן.

הוספת איש קשר גולמי חדש Insert.ACTION לא רלוונטי RawContacts.CONTENT_TYPE, סוג MIME של קבוצת אנשי קשר גולמיים. הצגת המסך הוספת איש קשר של אפליקציית אנשי הקשר של המכשיר. מוצגים הערכים הנוספים שמוסיפים לכוונה. אם שולחים את הבקשה עם startActivityForResult(), ה-URI של התוכן של איש הקשר הגולמי שנוסף מחדש מועבר בחזרה לשיטת ה-callback‏ onActivityResult() של הפעילות, בארגומנט Intent, בשדה data. כדי לקבל את הערך, קוראים לפונקציה getData().
עריכת איש קשר ACTION_EDIT CONTENT_LOOKUP_URI לאיש הקשר. הפעילות של עורך הרשאות תאפשר למשתמש לערוך את כל הנתונים שמשויכים לאיש הקשר הזה. Contacts.CONTENT_ITEM_TYPE, איש קשר יחיד. הצגת המסך 'עריכת איש קשר' באפליקציית אנשי הקשר. מוצגים הערכים הנוספים שמוסיפים לכוונה. כשהמשתמש לוחץ על סיום כדי לשמור את העריכות, הפעילות חוזרת לחזית.
הצגת בורר שאפשר גם להוסיף נתונים ACTION_INSERT_OR_EDIT לא רלוונטי CONTENT_ITEM_TYPE כוונת החיפוש הזו תמיד מציגה את מסך הבחירה של אפליקציית אנשי הקשר. המשתמש יכול לבחור איש קשר לעריכה או להוסיף איש קשר חדש. מסך העריכה או מסך ההוספה יופיעו, בהתאם לבחירה של המשתמש, ונתוני ה-extras שתעבירו בכוונה יוצגו. אם באפליקציה מוצגים פרטים ליצירת קשר כמו כתובת אימייל או מספר טלפון, אפשר להשתמש בכוונה הזו כדי לאפשר למשתמש להוסיף את הפרטים לאיש קשר קיים. איש קשר,

הערה: אין צורך לשלוח ערך של שם בפרטים הנוספים של הכוונה הזו, כי המשתמש תמיד בוחר שם קיים או מוסיף שם חדש. בנוסף, אם תשלחו שם והמשתמש יבחר לערוך אותו, השם שתשלחו יופיע באפליקציית אנשי הקשר ויחליף את הערך הקודם. אם המשתמש לא שם לב לכך ושומר את העריכה, הערך הישן יימחק.

אפליקציית 'אנשי קשר' במכשיר לא מאפשרת למחוק איש קשר גולמי או את הנתונים שלו מתוך כוונה. במקום זאת, כדי למחוק איש קשר בפורמט גולמי, משתמשים ב-ContentResolver.delete() או ב-ContentProviderOperation.newDelete().

קטע הקוד הבא מראה איך ליצור ולשלוח אובייקט Intent שמכניס אנשי קשר ונתונים גולמיים חדשים:

Kotlin

// Gets values from the UI
val name = contactNameEditText.text.toString()
val phone = contactPhoneEditText.text.toString()
val email = contactEmailEditText.text.toString()

val company = companyName.text.toString()
val jobtitle = jobTitle.text.toString()

/*
 * Demonstrates adding data rows as an array list associated with the DATA key
 */

// Defines an array list to contain the ContentValues objects for each row
val contactData = arrayListOf<ContentValues>()

/*
 * Defines the raw contact row
 */

// Sets up the row as a ContentValues object
val rawContactRow = ContentValues().apply {
    // Adds the account type and name to the row
    put(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.type)
    put(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.name)
}

// Adds the row to the array
contactData.add(rawContactRow)

/*
 * Sets up the phone number data row
 */

// Sets up the row as a ContentValues object
val phoneRow = ContentValues().apply {
    // Specifies the MIME type for this data row (all data rows must be marked by their type)
    put(ContactsContract.Data.MIMETYPE,ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)

    // Adds the phone number and its type to the row
    put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
}

// Adds the row to the array
contactData.add(phoneRow)

/*
 * Sets up the email data row
 */

// Sets up the row as a ContentValues object
val emailRow = ContentValues().apply {
    // Specifies the MIME type for this data row (all data rows must be marked by their type)
    put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)

    // Adds the email address and its type to the row
    put(ContactsContract.CommonDataKinds.Email.ADDRESS, email)
}

// Adds the row to the array
contactData.add(emailRow)

// Creates a new intent for sending to the device's contacts application
val insertIntent = Intent(ContactsContract.Intents.Insert.ACTION).apply {
    // Sets the MIME type to the one expected by the insertion activity
    type = ContactsContract.RawContacts.CONTENT_TYPE

    // Sets the new contact name
    putExtra(ContactsContract.Intents.Insert.NAME, name)

    // Sets the new company and job title
    putExtra(ContactsContract.Intents.Insert.COMPANY, company)
    putExtra(ContactsContract.Intents.Insert.JOB_TITLE, jobtitle)

    /*
    * Adds the array to the intent's extras. It must be a parcelable object in order to
    * travel between processes. The device's contacts app expects its key to be
    * Intents.Insert.DATA
    */
    putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData)
}

// Send out the intent to start the device's contacts app in its add contact activity.
startActivity(insertIntent)

Java

// Gets values from the UI
String name = contactNameEditText.getText().toString();
String phone = contactPhoneEditText.getText().toString();
String email = contactEmailEditText.getText().toString();

String company = companyName.getText().toString();
String jobtitle = jobTitle.getText().toString();

// Creates a new intent for sending to the device's contacts application
Intent insertIntent = new Intent(ContactsContract.Intents.Insert.ACTION);

// Sets the MIME type to the one expected by the insertion activity
insertIntent.setType(ContactsContract.RawContacts.CONTENT_TYPE);

// Sets the new contact name
insertIntent.putExtra(ContactsContract.Intents.Insert.NAME, name);

// Sets the new company and job title
insertIntent.putExtra(ContactsContract.Intents.Insert.COMPANY, company);
insertIntent.putExtra(ContactsContract.Intents.Insert.JOB_TITLE, jobtitle);

/*
 * Demonstrates adding data rows as an array list associated with the DATA key
 */

// Defines an array list to contain the ContentValues objects for each row
ArrayList<ContentValues> contactData = new ArrayList<ContentValues>();


/*
 * Defines the raw contact row
 */

// Sets up the row as a ContentValues object
ContentValues rawContactRow = new ContentValues();

// Adds the account type and name to the row
rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.getType());
rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.getName());

// Adds the row to the array
contactData.add(rawContactRow);

/*
 * Sets up the phone number data row
 */

// Sets up the row as a ContentValues object
ContentValues phoneRow = new ContentValues();

// Specifies the MIME type for this data row (all data rows must be marked by their type)
phoneRow.put(
        ContactsContract.Data.MIMETYPE,
        ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
);

// Adds the phone number and its type to the row
phoneRow.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone);

// Adds the row to the array
contactData.add(phoneRow);

/*
 * Sets up the email data row
 */

// Sets up the row as a ContentValues object
ContentValues emailRow = new ContentValues();

// Specifies the MIME type for this data row (all data rows must be marked by their type)
emailRow.put(
        ContactsContract.Data.MIMETYPE,
        ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE
);

// Adds the email address and its type to the row
emailRow.put(ContactsContract.CommonDataKinds.Email.ADDRESS, email);

// Adds the row to the array
contactData.add(emailRow);

/*
 * Adds the array to the intent's extras. It must be a parcelable object in order to
 * travel between processes. The device's contacts app expects its key to be
 * Intents.Insert.DATA
 */
insertIntent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData);

// Send out the intent to start the device's contacts app in its add contact activity.
startActivity(insertIntent);

תקינות נתונים

מאגר אנשי הקשר מכיל מידע חשוב ורגיש שהמשתמשים מצפים שיהיה נכון ועדכני, ולכן לספק אנשי הקשר יש כללים מוגדרים היטב לשמירה על תקינות הנתונים. באחריותכם לפעול בהתאם לכללים האלה כשאתם משנים את נתוני אנשי הקשר. אלה הכללים החשובים:

תמיד מוסיפים שורה של ContactsContract.CommonDataKinds.StructuredName לכל שורה של ContactsContract.RawContacts שמוסיפים.
שורה ContactsContract.RawContacts ללא שורה ContactsContract.CommonDataKinds.StructuredName בטבלה ContactsContract.Data עלולה לגרום לבעיות במהלך צבירה.
תמיד צריך לקשר שורות ContactsContract.Data חדשות לשורת ההורה שלהן, ContactsContract.RawContacts.
שורה של ContactsContract.Data שלא מקושרת אל ContactsContract.RawContacts לא תוצג באפליקציית אנשי הקשר של המכשיר, ויכול להיות שזה יגרום לבעיות עם מתאמי סנכרון.
לשנות את הנתונים רק לגבי אנשי הקשר הגולמיים שבבעלותכם.
חשוב לזכור שהספק של אנשי הקשר בדרך כלל מנהל נתונים מכמה סוגי חשבונות/שירותים אונליין שונים. צריך לוודא שהאפליקציה משנה או מוחקת רק נתונים של שורות ששייכות לך, ושהיא מוסיפה רק נתונים עם סוג חשבון ושם שנמצאים בשליטתך.
תמיד צריך להשתמש בערכי הקבועים שמוגדרים ב-ContactsContract ובתת-הסוגים שלו עבור רשויות, מזהי URI של תוכן, נתיבים של URI, שמות של עמודות, סוגי MIME וערכים של TYPE.
שימוש בקבועים האלה עוזר למנוע שגיאות. תקבלו גם התראות מהמחשב אם אחת מהקבועות הוצאה משימוש.

שורות נתונים בהתאמה אישית

בטבלה ContactsContract.Data, יצירה של סוגי MIME מותאמים אישית ושימוש בהם מאפשרים להוסיף, לערוך, למחוק ולאחזר את שורות הנתונים שלכם. אפשר להשתמש בשמות העמודות שמוגדרים ב-ContactsContract.DataColumns בשורות, אבל אפשר גם למפות שמות עמודות ספציפיים לסוגים לשמות העמודות שמוגדרים כברירת מחדל. נתוני השורות מוצגים באפליקציית אנשי הקשר במכשיר, אבל אי אפשר לערוך או למחוק אותם, והמשתמשים לא יוכלו להוסיף נתונים. כדי לאפשר למשתמשים לשנות את שורות הנתונים בהתאמה אישית, עליכם לספק פעילות עריכה באפליקציה שלכם.

כדי להציג את הנתונים המותאמים אישית, צריך לספק קובץ contacts.xml שמכיל רכיב <ContactsAccountType> ואחד או יותר מרכיבי הצאצא שלו <ContactsDataKind>. המידע הזה מתואר בפירוט בקטע <ContactsDataKind> element.

מידע נוסף על סוגי MIME מותאמים אישית זמין במדריך יצירת ספק תוכן.

מתאמי סנכרון של ספקי אנשי קשר

ספק אנשי הקשר נועד במיוחד לטיפול בסנכרון של נתוני אנשי הקשר בין מכשיר לשירות אונליין. כך המשתמשים יכולים להוריד נתונים קיימים למכשיר חדש ולהעלות נתונים קיימים לחשבון חדש. הסנכרון גם מבטיח שהמשתמשים יקבלו את הנתונים העדכניים ביותר, ללא קשר למקור של התוספות והשינויים. יתרון נוסף של הסנכרון הוא שהוא הופך את נתוני אנשי הקשר לזמינים גם כשהמכשיר לא מחובר לרשת.

אפשר להטמיע סנכרון במגוון דרכים, אבל מערכת Android מספקת מסגרת סנכרון של יישומי פלאגין שמבצעת באופן אוטומטי את המשימות הבאות:

  • בדיקת זמינות הרשת.
  • תזמון וביצוע סנכרון על סמך העדפות המשתמשים.
  • הפעלה מחדש של סנכרון שנעצר.

כדי להשתמש במסגרת הזו, צריך לספק פלאגין של מתאם סנכרון. כל מתאם סנכרון הוא ייחודי לשירות ולספק התוכן, אבל הוא יכול לטפל בכמה שמות של חשבונות באותו שירות. ה-framework מאפשר גם מספר מתאמי סנכרון לאותו שירות ולאותו ספק.

סנכרון של קבצים וכיתות של מתאמים

מטמיעים מתאם סנכרון כסוג משנה של AbstractThreadedSyncAdapter ומתקינים אותו כחלק מאפליקציית Android. המערכת לומדת על מתאם הסנכרון מרכיבים במניפסט של האפליקציה ומקובץ XML מיוחד שמפנים אליו במניפסט. קובץ ה-XML מגדיר את סוג החשבון של השירות אונליין ואת הרשות של ספק התוכן, שמזהים יחד את המתאם באופן ייחודי. מתאם הסנכרון לא פעיל עד שהמשתמש מוסיף חשבון לסוג החשבון של מתאם הסנכרון ומפעיל את הסנכרון עם ספק התוכן שאליו מתבצע הסנכרון. בשלב הזה המערכת מתחילה לנהל את המתאם, ומפעילה אותו לפי הצורך כדי לסנכרן בין ספק התוכן לבין השרת.

הערה: שימוש בסוג חשבון כחלק מהזיהוי של מתאם הסנכרון מאפשר למערכת לזהות ולקבץ יחד מתאמי סנכרון שניגשים לשירותים שונים מאותו ארגון. לדוגמה, לכל מתאמי הסנכרון של שירותי Google אונליין יש את אותו סוג חשבון com.google. כשמשתמשים מוסיפים חשבון Google למכשירים שלהם, כל מתאמי הסנכרון המותקנים לשירותי Google מופיעים יחד. כל מתאם סנכרון שמופיע מסתנכרן עם ספק תוכן אחר במכשיר.

מאחר שרוב השירותים דורשים מהמשתמשים לאמת את הזהות שלהם לפני שהם יכולים לגשת לנתונים, מערכת Android מציעה מסגרת אימות שדומה למסגרת של מתאם הסנכרון, ולעיתים קרובות משתמשים בה בשילוב עם המסגרת הזו. מסגרת האימות משתמשת במאמתי תוספים שהם מחלקות משנה של AbstractAccountAuthenticator. אימות הזהות של המשתמש מתבצע בשלבים הבאים:

  1. אוספת את השם, הסיסמה או מידע דומה של המשתמש (פרטי הכניסה של המשתמש).
  2. שליחת פרטי הכניסה לשירות
  3. בודק את התשובה של השירות.

אם השירות מקבל את פרטי הכניסה, מאמת החשבונות יכול לאחסן אותם לשימוש מאוחר יותר. בגלל מסגרת האימות של הפלאגין, ה-AccountManager יכול לספק גישה לכל אסימון אימות שתומך בו האימות ובוחר לחשוף, כמו אסימוני אימות של OAuth2.

למרות שלא נדרש אימות, רוב שירותי אנשי הקשר משתמשים בו. עם זאת, אין צורך להשתמש במסגרת האימות של Android כדי לבצע אימות.

הטמעת מתאם סנכרון

כדי להטמיע מתאם סנכרון לספק אנשי הקשר, צריך קודם ליצור אפליקציה ל-Android שמכילה את הרכיבים הבאים:

רכיב Service שמגיב לבקשות מהמערכת לקישור למתאם הסנכרון.
כשהמערכת רוצה להריץ סנכרון, היא קוראת ל-method‏ onBind() של השירות כדי לקבל IBinder למתאם הסנכרון. כך המערכת יכולה לבצע קריאות בין תהליכים ל-methods של המתאם.
מתאם הסנכרון בפועל, שמיושם כסוג משנה קונקרטי של AbstractThreadedSyncAdapter.
הכיתה הזו מבצעת את הורדת הנתונים מהשרת, העלאת הנתונים מהמכשיר ופתרון של קונפליקטים. העבודה העיקרית של המתאם מתבצעת ב-method‏ onPerformSync(). צריך ליצור מופע של המחלקה הזו כ-singleton.
מחלקה משנית של Application.
הכיתה הזו משמשת כמפעל ליחידה היחידה של מתאם הסנכרון. משתמשים ב-method onCreate() כדי ליצור את מתאם הסנכרון, ולספק שיטת 'getter' סטטית כדי להחזיר את הסינגלטון לשיטה onBind() של השירות של מתאם הסנכרון.
אופציונלי: רכיב Service שמגיב לבקשות מהמערכת לאימות משתמשים.
AccountManager מפעיל את השירות הזה כדי להתחיל את תהליך האימות. שיטת onCreate() של השירות יוצרת אובייקט של מאמת. כשהמערכת רוצה לאמת חשבון משתמש למתאמת הסנכרון של האפליקציה, היא קוראת לשיטה onBind() של השירות כדי לקבל IBinder לאימות. כך המערכת תוכל לבצע קריאות בין תהליכים ל-methods של מאמת החשבונות.
אופציונלי: תת-סוג קונקרטי של AbstractAccountAuthenticator שמטפל בבקשות לאימות.
הסיווג הזה מספק methods שה-AccountManager מפעיל כדי לאמת את פרטי הכניסה של המשתמש מול השרת. הפרטים של תהליך האימות משתנים במידה רבה, בהתאם לטכנולוגיית השרת שבה נעשה שימוש. למידע נוסף על אימות, כדאי לעיין במסמכי העזרה של תוכנת השרת.
קובצי XML שמגדירים את מתאם הסנכרון ואת מאמת החשבון למערכת.
רכיבי השירות של מתאם הסנכרון ומאמת הזהות שתוארו למעלה מוגדרים ברכיבי <service> במניפסט של האפליקציה. הרכיבים האלה מכילים <meta-data> רכיבי צאצא שמספקים למערכת נתונים ספציפיים:
  • הרכיב <meta-data> של השירות של מתאם הסנכרון מפנה לקובץ ה-XML מסוג res/xml/syncadapter.xml. קובץ זה מציין URI של שירות האינטרנט שיתוזמן עם ספק אנשי הקשר, וגם סוג חשבון לשירות האינטרנט.
  • אופציונלי: הרכיב <meta-data> של המאמת מפנה לקובץ ה-XML res/xml/authenticator.xml. הקובץ הזה מציין את סוג החשבון שתומך בו מאמת החשבונות, וגם משאבי ממשק משתמש שמופיעים במהלך תהליך האימות. סוג החשבון שצוין ברכיב הזה חייב להיות זהה לסוג החשבון שצוין למתאם הסנכרון.

נתונים של מקור נתונים ברשתות חברתיות

הטבלאות android.provider.ContactsContract.StreamItems ו-android.provider.ContactsContract.StreamItemPhotos מנהלים נתונים נכנסים מרשתות חברתיות. אפשר לכתוב מתאם סנכרון שמוסיף לטבלאות האלה נתוני סטרימינג מהרשת שלכם, או לקרוא נתוני סטרימינג מהטבלאות האלה ולהציג אותם באפליקציה שלכם, או גם וגם. בעזרת התכונות האלה, תוכלו לשלב את האפליקציות והשירותים של הרשתות החברתיות בחוויית השימוש של Android ברשתות החברתיות.

טקסט של 'רשתות חברתיות'

פריטים בסטרימינג תמיד משויכים לאיש קשר גולמי. הערך android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID מקשר לערך _ID של איש הקשר הגולמי. סוג החשבון ושם החשבון של איש הקשר הגולמי נשמרים גם בשורה של פריט הסטרימינג.

שומרים את הנתונים מהמקור בעמודות הבאות:

android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_TYPE
חובה. סוג החשבון של המשתמש עבור איש הקשר הגולמי שמשויך לפריט מקור הנתונים הזה. חשוב לזכור להגדיר את הערך הזה כשמוסיפים פריט בסטרימינג.
android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_NAME
חובה. שם החשבון של המשתמש לאיש הקשר הגולמי שמשויך לפריט הסטרימינג הזה. חשוב לזכור להגדיר את הערך הזה כשמוסיפים פריט בסטרימינג.
עמודות מזהה
חובה. כשאתם מוסיפים פריט בסטרימינג, עליכם להוסיף את העמודות הבאות של המזהים:
  • android.provider.ContactsContract.StreamItemsColumns#CONTACT_ID: הערך של android.provider.BaseColumns#_ID של איש הקשר שאליו משויך פריט המקור הזה.
  • android.provider.ContactsContract.StreamItemsColumns#CONTACT_LOOKUP_KEY: הערך של android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY של איש הקשר שאליו משויך פריט הסטרימינג הזה.
  • android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID: הערך של android.provider.BaseColumns#_ID של איש הקשר הגולמי שאליו משויך פריט המקור הזה.
android.provider.ContactsContract.StreamItemsColumns#COMMENTS
אופציונלי. אחסון פרטי סיכום שאפשר להציג בתחילת הפריט בפיד.
android.provider.ContactsContract.StreamItemsColumns#TEXT
הטקסט של הפריט שבשידור – התוכן שפורסם על ידי מקור הפריט או תיאור של פעולה כלשהי שיצרה את הפריט בסטרימינג. העמודה הזו יכולה להכיל כל עיצוב ותמונות מוטמעות של משאבים שאפשר להציג באמצעות fromHtml(). יכול להיות שהספק יקצר תוכן ארוך או ידגיש אותו בקו אליפטי, אבל הוא ינסה להימנע מהפרדת תגים.
android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP
מחרוזת טקסט שמכילה את הזמן שבו פריט הסטרימינג הוכנס או עודכן, בפורמט של מיליאקונדות מאז תחילת הזמן. אפליקציות שמוסיפות או מעדכנות פריטים בסטרימינג אחראיות על ניהול העמודה הזו. היא לא מנוהלת באופן אוטומטי על ידי ספק אנשי הקשר.

כדי להציג פרטים מזהים של הפריטים בסטרימינג, צריך להשתמש ב-android.provider.ContactsContract.StreamItemsColumns#RES_ICON, android.provider.ContactsContract.StreamItemsColumns#RES_LABEL ו-android.provider.ContactsContract.StreamItemsColumns#RES_כדי לקשר למשאבים באפליקציה.

הטבלה android.provider.ContactsContract.StreamItems מכילה גם את העמודות android.provider.ContactsContract.StreamItemsColumns#SYNC1 עד android.provider.ContactsContract.StreamItemsColumns#SYNC4 לשימוש בלעדי של מתאמי סנכרון.

תמונות מזרם הפעילות ברשתות החברתיות

בטבלה android.provider.ContactsContract.StreamItemPhotos מאוחסנות תמונות שמשויכות לפריט במסך הבית. העמודה android.provider.ContactsContract.StreamItemPhotosColumns#STREAM_ITEM_ID בטבלה מקשרת לערכים בעמודה _ID בטבלה android.provider.ContactsContract.StreamItems. הפניות לתמונות מאוחסנות בטבלה בעמודות הבאות:

העמודה android.provider.ContactsContract.StreamItemPhotos#PHOTO (BLOB).
ייצוג בינארי של התמונה, שהספק שינה את הגודל שלה לצורכי אחסון ותצוגה. העמודה הזו זמינה לצורך תאימות לאחור לגרסאות קודמות של ספק אנשי הקשר שהשתמשה בה לאחסון תמונות. עם זאת, בגרסה הנוכחית לא מומלץ להשתמש בעמודה הזו לאחסון תמונות. במקום זאת, צריך להשתמש ב-android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID או ב-android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI (שניהם מתוארים בהמשך) כדי לאחסן תמונות בקובץ. העמודה הזו מכילה עכשיו תמונה ממוזערת של התמונה, שזמינה לקריאה.
android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID
מזהה מספרי של תמונה של איש קשר בפורמט גולמי. מוסיפים את הערך הזה לערך הקבוע DisplayPhoto.CONTENT_URI כדי לקבל URI של תוכן שמפנה לקובץ תמונה אחד, ואז קוראים לפונקציה openAssetFileDescriptor() כדי לקבל נקודת אחיזה לקובץ התמונה.
android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI
URI של תוכן שמפנה ישירות לקובץ התמונה של התמונה שמיוצגת על ידי השורה הזו. צריך להתקשר אל openAssetFileDescriptor() עם ה-URI הזה כדי לקבל כינוי לקובץ התמונה.

שימוש בטבלאות של מקור הנתונים 'פעילות ברשתות החברתיות'

הטבלאות האלה פועלות כמו הטבלאות הראשיות האחרות בספק אנשי הקשר, חוץ מהעובדות הבאות:

  • בטבלאות האלה נדרשות הרשאות גישה נוספות. כדי לקרוא מהם, לאפליקציה צריכה להיות ההרשאה android.Manifest.permission#READ_SOCIAL_STREAM. כדי לשנות אותם, לאפליקציה צריכה להיות ההרשאה android.Manifest.permission#WRITE_SOCIAL_STREAM.
  • בטבלה android.provider.ContactsContract.StreamItems, מספר השורות שנשמרות לכל איש קשר גולמי מוגבל. לאחר ההגעה למגבלה, הספק של אנשי הקשר מפנה מקום לשורות חדשות של פריטים בסטרימינג על ידי מחיקה אוטומטית של השורות שמכילות את android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP הישן ביותר. כדי לקבל את המגבלה, שולחים שאילתה ל-URI של התוכן android.provider.ContactsContract.StreamItems#CONTENT_LIMIT_URI. אפשר להשאיר את ההגדרה null עבור כל הארגומנטים מלבד ה-URI של התוכן. השאילתה מחזירה Cursor שמכיל שורה אחת עם העמודה היחידה android.provider.ContactsContract.StreamItems#MAX_ITEMS.

הכיתה android.provider.ContactsContract.StreamItems.StreamItemPhotos מגדירה טבלת משנה של android.provider.ContactsContract.StreamItemPhotos שמכילה את שורות התמונות של פריט אחד במקור החדשות.

אינטראקציות עם שידור חי ברשתות חברתיות

הנתונים של עדכוני התוכן מהרשתות החברתיות שמנוהלים על ידי ספק אנשי הקשר, בשילוב עם אפליקציית אנשי הקשר במכשיר, מציעים דרך מתקדמת לחיבור מערכת הרשתות החברתיות לאנשי קשר קיימים. התכונות הבאות זמינות:

  • סנכרון של שירות הרשת החברתית עם ספק אנשי הקשר באמצעות מתאם סנכרון מאפשר לאחזר את הפעילות האחרונה של אנשי הקשר של המשתמש ולאחסן אותה בטבלאות android.provider.ContactsContract.StreamItems ו-android.provider.ContactsContract.StreamItemPhotos לשימוש מאוחר יותר.
  • בנוסף לסנכרון הרגיל, אפשר להפעיל את מתאם הסנכרון כדי לאחזר נתונים נוספים כשהמשתמש בוחר איש קשר להצגה. כך מתאם הסנכרון יוכל לאחזר תמונות ברזולוציה גבוהה ואת הפריטים האחרונים שפורסמו בסטורי של איש הקשר.
  • אם תירשמו התראה באפליקציית אנשי הקשר של המכשיר ובספק אנשי הקשר, תוכלו לקבל כוונה כשאיש קשר מוצג, ובשלב הזה לעדכן את הסטטוס של איש הקשר מהשירות שלכם. הגישה הזו עשויה להיות מהירה יותר ולצרוך פחות רוחב פס מאשר סנכרון מלא באמצעות מתאם סנכרון.
  • המשתמשים יכולים להוסיף איש קשר לשירות הרשת החברתית שלכם כשהם צופים באיש הקשר באפליקציית אנשי הקשר של המכשיר. כדי להפעיל את התכונה הזו, צריך להשתמש בתכונה 'הזמנת איש קשר', שמפעילים באמצעות שילוב של פעילות שמוסיפה איש קשר קיים לרשת, וקובץ XML שמספק לאפליקציית אנשי הקשר של המכשיר ולספק אנשי הקשר את פרטי האפליקציה.

סנכרון קבוע של פריטי הסטרימינג עם ספק אנשי הקשר זהה לסנכרון עם ספקים אחרים. מידע נוסף על סנכרון זמין בקטע מתאמי סנכרון של ספקי אנשי קשר. בשני הסעיפים הבאים מתוארים שני הסעיפים הבאים לגבי רישום התראות והזמנת אנשי קשר.

רישום לטיפול בבקשות לצפיות ברשתות חברתיות

כדי לרשום את מתאם הסנכרון לקבלת התראות כשהמשתמש מציג איש קשר שמנוהל על ידי מתאם הסנכרון:

  1. יוצרים קובץ בשם contacts.xml בתיקייה res/xml/ של הפרויקט. אם כבר יש לכם את הקובץ הזה, אתם יכולים לדלג על השלב הזה.
  2. בקובץ הזה, מוסיפים את הרכיב <ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">. אם הרכיב הזה כבר קיים, אפשר לדלג על השלב הזה.
  3. כדי לרשום שירות שיקבל התראה כשהמשתמש יפתח את דף הפרטים של איש קשר באפליקציית אנשי הקשר של המכשיר, מוסיפים את המאפיין viewContactNotifyService="serviceclass" לאלמנט, כאשר serviceclass הוא שם הכיתה המלא של השירות שצריך לקבל את הכוונה מאפליקציית אנשי הקשר של המכשיר. בשירות ההתראות, משתמשים בכיתה שמרחיבה את IntentService כדי לאפשר לשירות לקבל כוונות. הנתונים בכוונה הנכנסת מכילים את ה-URI של התוכן של איש הקשר הגולמי שהמשתמש לחץ עליו. משירות שירות ההתראות, אפשר לבצע קישור למתאם הסנכרון ולאחר מכן להפעיל אותו כדי לעדכן את הנתונים של איש הקשר הגולמי.

כדי לרשום פעילות שתופעל כשהמשתמש לוחץ על פריט בסטרימינג או על תמונה, או על שניהם:

  1. יוצרים קובץ בשם contacts.xml בתיקייה res/xml/ של הפרויקט. אם הקובץ הזה כבר נמצא ברשותך, אפשר לדלג על השלב הזה.
  2. לקובץ הזה, מוסיפים את הרכיב <ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">. אם הרכיב הזה כבר קיים, אפשר לדלג על השלב הזה.
  3. כדי לרשום אחת מהפעילויות לטיפול במשתמש שלוחצים על פריט באפליקציית אנשי הקשר של המכשיר, יש להוסיף את המאפיין viewStreamItemActivity="activityclass" לרכיב. activityclass הוא השם המוגדר במלואו של הפעילות, שאמורה לקבל את הכוונה מאפליקציית אנשי הקשר של המכשיר.
  4. כדי לרשום אחת מהפעילויות שלכם לטפל במקרה שבו המשתמש לוחץ על תמונה בסטרים באפליקציית אנשי הקשר של המכשיר, מוסיפים את המאפיין viewStreamItemPhotoActivity="activityclass" לאלמנט, כאשר activityclass הוא שם הכיתה המלא של הפעילות שאמורה לקבל את ה-Intent מאפליקציית אנשי הקשר של המכשיר.

תיאור מפורט יותר של הרכיב <ContactsAccountType> זמין בקטע הרכיב<ContactsAccountType>.

הכוונה הנכנסת מכילה את ה-URI של התוכן של הפריט או התמונה שהמשתמש לחץ עליהם. כדי ליצור פעילויות נפרדות לפריטי טקסט ולתמונות, צריך להשתמש בשני המאפיינים באותו קובץ.

אינטראקציה עם שירות הרשת החברתית

המשתמשים לא צריכים לצאת מאפליקציית אנשי הקשר של המכשיר כדי להזמין איש קשר לאתר של הרשת החברתית. במקום זאת, אפשר להגדיר שאפליקציית אנשי הקשר במכשיר תשלח כוונה להזמנת איש הקשר לאחת מהפעילויות שלכם. כדי להגדיר זאת:

  1. יוצרים קובץ בשם contacts.xml בתיקייה res/xml/ של הפרויקט. אם הקובץ הזה כבר נמצא ברשותך, אפשר לדלג על השלב הזה.
  2. בקובץ הזה, מוסיפים את הרכיב <ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">. אם הרכיב הזה כבר קיים, אפשר לדלג על השלב הזה.
  3. מוסיפים את המאפיינים הבאים:
    • inviteContactActivity="activityclass"
    • inviteContactActionLabel="@string/invite_action_label"
    הערך activityclass הוא שם הסיווג המוגדר במלואו של הפעילות שאמורה לקבל את Intent. הערך של invite_action_label הוא מחרוזת טקסט שמוצגת בתפריט Add Connection באפליקציית אנשי הקשר של המכשיר.

הערה: ContactsSource הוא שם תג שהוצא משימוש עבור ContactsAccountType.

מסמך עזר בנושא contacts.xml

הקובץ contacts.xml מכיל רכיבי XML ששולטים באינטראקציה של מתאם הסנכרון והאפליקציה עם אפליקציית אנשי הקשר ועם ספק אנשי הקשר. הרכיבים האלה מתוארים בקטעים הבאים.

רכיב <ContactsAccountType>

האלמנט <ContactsAccountType> קובע את האינטראקציה של האפליקציה שלכם עם אפליקציית אנשי הקשר. התחביר שלו הוא:

<ContactsAccountType
        xmlns:android="http://schemas.android.com/apk/res/android"
        inviteContactActivity="activity_name"
        inviteContactActionLabel="invite_command_text"
        viewContactNotifyService="view_notify_service"
        viewGroupActivity="group_view_activity"
        viewGroupActionLabel="group_action_text"
        viewStreamItemActivity="viewstream_activity_name"
        viewStreamItemPhotoActivity="viewphotostream_activity_name">

נכלל ב:

res/xml/contacts.xml

יכולים להכיל:

<ContactsDataKind>

תיאור:

מצהירה על רכיבי Android ותוויות ממשק משתמש שמאפשרות למשתמשים להזמין אחד מאנשי הקשר שלהם לרשת חברתית, להודיע למשתמשים כאשר מתעדכן אחד מהרשתות החברתיות שלהם וכן הלאה.

שימו לב שקידומת המאפיין android: לא נדרשת למאפיינים של <ContactsAccountType>.

מאפיינים:

inviteContactActivity
שם הכיתה המלא של הפעילות באפליקציה שרוצים להפעיל כשהמשתמש בוחר באפשרות Add connection באפליקציית אנשי הקשר של המכשיר.
inviteContactActionLabel
מחרוזת טקסט שמוצגת לפעילות שצוינה ב-inviteContactActivity, בתפריט Add connection (הוספת חיבור). לדוגמה, אפשר להשתמש במחרוזת 'לעקוב ברשת שלי'. אפשר להשתמש במזהה משאב מחרוזת בשביל התווית הזו.
viewContactNotifyService
שם הכיתה המלא של שירות באפליקציה שאמור לקבל התראות כשהמשתמש מציג איש קשר. ההתראה הזו נשלחת על ידי אפליקציית אנשי הקשר במכשיר, והיא מאפשרת לאפליקציה לדחות פעולות שמתבצעות באמצעות כמות גדולה של נתונים עד שהן נדרשות. לדוגמה, האפליקציה יכולה להגיב להתראה הזו על ידי קריאה והצגה של התמונה באיכות גבוהה של איש הקשר ושל הפריטים האחרונים בסטטוס שלו ברשתות החברתיות. התכונה הזו מתוארת בפירוט בקטע אינטראקציות של רשתות חברתיות.
viewGroupActivity
שם הכיתה שמוגדר במלואו של פעילות באפליקציה, שיכול להציג את פרטי הקבוצה. כשהמשתמש לוחץ על תווית הקבוצה באפליקציית אנשי הקשר במכשיר, מוצג ממשק המשתמש של הפעילות הזו.
viewGroupActionLabel
התווית שמופיעה באפליקציית 'אנשי קשר' לפקד של ממשק המשתמש שמאפשרת למשתמש לעיין בקבוצות באפליקציה.

אפשר להשתמש במזהה משאב של מחרוזת במאפיין הזה.

viewStreamItemActivity
שם הכיתה המלא של פעילות באפליקציה, שאפליקציית אנשי הקשר במכשיר מפעילה כשהמשתמש לוחץ על פריט בסטרימינג של איש קשר גולמי.
viewStreamItemPhotoActivity
שם הסיווג המוגדר במלואו של פעילות באפליקציה שלך, שמופעל באפליקציית אנשי הקשר במכשיר כשהמשתמש לוחץ על תמונה בפריט שבסטרימינג עבור איש קשר גולמי.

האלמנט <ContactsDataKind>

הרכיב <ContactsDataKind> קובע את הצגת שורות הנתונים בהתאמה אישית של האפליקציה בממשק המשתמש של אפליקציית אנשי הקשר. התחביר שלו הוא:

<ContactsDataKind
        android:mimeType="MIMEtype"
        android:icon="icon_resources"
        android:summaryColumn="column_name"
        android:detailColumn="column_name">

נכלל ב:

<ContactsAccountType>

תיאור:

צריך להשתמש ברכיב הזה כדי שאפליקציית 'אנשי קשר' תציג את התוכן של שורת נתונים בהתאמה אישית כחלק מהפרטים של איש קשר גולמי. כל רכיב צאצא של <ContactsDataKind> ב-<ContactsAccountType> מייצג סוג של שורת נתונים בהתאמה אישית שהמתאם לסנכרון מוסיף לטבלה ContactsContract.Data. מוסיפים רכיב <ContactsDataKind> אחד לכל סוג MIME מותאם אישית שבו אתם משתמשים. אין צורך להוסיף את הרכיב אם יש לכם שורת נתונים מותאמת אישית שאתם לא רוצים להציג בה נתונים.

מאפיינים:

android:mimeType
סוג ה-MIME המותאם אישית שהגדרתם לאחד מסוגי השורות של הנתונים בהתאמה אישית בטבלה ContactsContract.Data. לדוגמה, הערך vnd.android.cursor.item/vnd.example.locationstatus יכול להיות סוג MIME בהתאמה אישית של שורת נתונים שמתעדת את המיקום האחרון הידוע של איש הקשר.
android:icon
משאב drawable ב-Android, שמוצג באפליקציית אנשי הקשר לצד הנתונים שלכם. אפשר להשתמש במאפיין הזה כדי לציין למשתמש שהנתונים מגיעים מהשירות שלכם.
android:summaryColumn
שם העמודה של הערך הראשון מבין שני הערכים שאוחזרו משורת הנתונים. הערך מוצג בשורה הראשונה של הרשומה בשורת הנתונים הזו. השורה הראשונה מיועדת לסיכום הנתונים, אבל היא אופציונלית. אפשר לעיין גם במאמר android:detailColumn.
android:detailColumn
שם העמודה של הערך השני מתוך שני ערכים שאוחזרו משורת הנתונים. הערך מוצג כשורה השנייה של הרשומה בשורת הנתונים הזו. למידע נוסף, ראו גם android:summaryColumn.

תכונות נוספות של 'ניהול אנשי הקשר'

בנוסף לתכונות הראשיות המתוארות בקטעים הקודמים, ספק אנשי הקשר מציע את התכונות השימושיות הבאות לעבודה עם נתוני אנשי הקשר:

  • קבוצות של אנשי קשר
  • תכונות שקשורות לתמונות

קבוצות של אנשי קשר

ספק אנשי הקשר יכול לתייג אוספים של אנשי קשר קשורים באמצעות נתוני קבוצה. אם השרת שמשויך לחשבון משתמש רוצה לשמור על קבוצות, מתאם הסנכרון של סוג החשבון צריך להעביר את נתוני הקבוצות בין ספק אנשי הקשר לבין השרת. כשמשתמשים מוסיפים איש קשר חדש לשרת ולאחר מכן מוסיפים את איש הקשר הזה לקבוצה חדשה, מתאם הסנכרון חייב להוסיף את הקבוצה החדשה לטבלה ContactsContract.Groups. הקבוצה או הקבוצות שאליהן שייך איש הקשר הגולמי מאוחסנות בטבלה ContactsContract.Data, באמצעות סוג ה-MIME ContactsContract.CommonDataKinds.GroupMembership.

אם אתם מתכננים מתאם סנכרון שיוסיף נתוני אנשי קשר גולמיים מהשרת לספק אנשי הקשר, ואתם לא משתמשים בקבוצות, תצטרכו להורות לספק להפוך את הנתונים שלכם לגלויים. בקוד שמופעל כשמשתמש מוסיף חשבון למכשיר, מעדכנים את השורה ContactsContract.Settings שספק אנשי הקשר מוסיף לחשבון. בשורה הזו, מגדירים את הערך של עמודת Settings.UNGROUPED_VISIBLE כ-1. כשעושים זאת, נתוני אנשי הקשר שלכם תמיד יהיו גלויים לספק אנשי הקשר, גם אם אתם לא משתמשים בקבוצות.

התמונות של אנשי הקשר

בטבלה ContactsContract.Data תמונות מאוחסנות בשורות עם סוג MIME‏ Photo.CONTENT_ITEM_TYPE. העמודה CONTACT_ID של השורה מקושרת לעמודה _ID של איש הקשר הגולמי שאליו היא שייכת. הכיתה ContactsContract.Contacts.Photo מגדירה טבלת משנה של ContactsContract.Contacts שמכילה את פרטי התמונה של תמונת הפרופיל הראשית של איש הקשר, שהיא תמונת הפרופיל הראשית של איש הקשר הגולמי הראשי. באופן דומה, הכיתה ContactsContract.RawContacts.DisplayPhoto מגדירה טבלת משנה של ContactsContract.RawContacts שמכילה את פרטי התמונה של התמונה הראשית של איש הקשר הגולמי.

במסמכי העזרה של ContactsContract.Contacts.Photo ושל ContactsContract.RawContacts.DisplayPhoto יש דוגמאות לאחזור פרטי תמונה. אין סיווג נוחות לאחזור התמונה הממוזערת הראשית של איש קשר, אבל אפשר לשלוח שאילתה לטבלה ContactsContract.Data על ידי בחירה בעמודה _ID של איש הקשר הגולמי, Photo.CONTENT_ITEM_TYPE ו-IS_PRIMARY כדי למצוא את שורת התמונה הראשית של איש הקשר.

נתונים של סטרימינג מרשתות חברתיות של אדם עשויים לכלול גם תמונות. התמונות האלה מאוחסנות בטבלה android.provider.ContactsContract.StreamItemPhotos, שמתוארת בפירוט בקטע תמונות מהעדכונים של החברים ברשתות החברתיות.