مقدِّم جهات الاتصال

"مقدِّم جهات الاتصال" هو أحد مكونات Android الفعّالة والمرنة التي تدير مستودع الجهاز المركزي للبيانات حول الأشخاص. "مقدِّم جهات الاتصال" هو مصدر البيانات التي تظهر لك في تطبيق جهات الاتصال على الجهاز، ويمكنك أيضًا الوصول إلى بياناته في تطبيقك ونقل البيانات بين الجهاز والخدمات على الإنترنت. ويستوعب مقدّم الخدمة مجموعة كبيرة من مصادر البيانات ويحاول إدارة أكبر قدر ممكن من البيانات لكل شخص، ما يؤدّي إلى تعقيد المؤسسة. ولهذا السبب، تتضمن واجهة برمجة التطبيقات لدى الموفّر مجموعة كبيرة من فئات العقود والواجهات التي تسهّل استرجاع البيانات وتعديلها.

يوضِّح هذا الدليل ما يلي:

  • البنية الأساسية لمزود الخدمة.
  • كيفية استرداد البيانات من مقدّم الخدمة
  • كيفية تعديل البيانات في مقدّم الخدمة
  • طريقة كتابة محوّل مزامنة لمزامنة البيانات من الخادم مع "مزوّد جهات الاتصال".

يفترض هذا الدليل أنّك على دراية بأساسيات موفّري محتوى 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. أعمدة جهات الاتصال الأولية المهمة.

اسم العمود استخدام Notes
ACCOUNT_NAME اسم الحساب لنوع الحساب الذي يشكّل مصدر جهة الاتصال الأولية هذه. على سبيل المثال، اسم حساب Google هو أحد عناوين Gmail لمالك الجهاز. راجِع الإدخال التالي الخاص بسمة ACCOUNT_TYPE للاطّلاع على مزيد من المعلومات. ويكون تنسيق هذا الاسم خاصًا بنوع حسابه. وليس بالضرورة عنوان بريد إلكتروني.
ACCOUNT_TYPE نوع الحساب الذي يشكّل مصدر جهة الاتصال الأولية هذه. على سبيل المثال، نوع الحساب الخاص بحساب Google هو com.google. احرص دائمًا على تأهيل نوع حسابك باستخدام معرّف نطاق لنطاق تملكه أو تتحكّم فيه. سيضمن ذلك أن يكون نوع حسابك فريدًا. عادةً ما يكون لنوع الحساب الذي يقدم بيانات جهات الاتصال محوّل مزامنة مقترن يتزامن مع "مقدِّم جهات الاتصال".
DELETED علامة "محذوفة" لجهة اتصال أولية. تسمح هذه العلامة لموفّر جهات الاتصال بالاحتفاظ بالصف داخليًا إلى أن تتمكن محوّلات المزامنة من حذف الصف من الخوادم ثم حذف الصف من المستودع في النهاية.

Notes

في ما يلي ملاحظات مهمة حول جدول "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" التي تم تحديد حسابات المستخدمين الثلاثة التالية على جهازها:

  • emily.dickinson@gmail.com
  • emilyd@gmail.com
  • حساب Twitter "belle_of_amherst"

فعَّل هذا المستخدم مزامنة جهات الاتصال لهذه الحسابات الثلاثة في إعدادات الحسابات.

لنفترض أنّ إميلي ديكنسون فتحت نافذة متصفّح، وتسجّل الدخول إلى Gmail باسم emily.dickinson@gmail.com، وتفتح "جهات الاتصال"، وأضافت "توماس هيغنسون". وفي وقت لاحق، سجّلت الدخول إلى Gmail باسم emilyd@gmail.com وأرسلت رسالة إلكترونية إلى "توماس هيغينسون" التي تضيفه تلقائيًا كجهة اتصال. وتتابع أيضًا "colonel_tom" (رقم تعريف Thomas Higginson على Twitter) على Twitter.

يقوم موفر جهات الاتصال بإنشاء ثلاث جهات اتصال أولية نتيجة لهذا العمل:

  1. جهة اتصال أولية لـ "Thomas Higginson" المرتبطة بـ 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 إلى صف البيانات الذي يحتوي على البيانات الأساسية لهذا النوع. على سبيل المثال، إذا ضغط المستخدم مع الاستمرار على رقم هاتف لجهة اتصال واخترت ضبط كتلقائي، سيتم ضبط العمود 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. فئات أسماء الأعمدة الخاصة بالنوع

صف رسم الخرائط نوع البيانات Notes
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. علاقات جدول "جهات الاتصال" و"جهات الاتصال الأولية" و"التفاصيل"

تنبيه: إذا نشرت تطبيقك على "متجر Google Play" أو كان تطبيقك على جهاز يعمل بإصدار Android 10 (المستوى 29 من واجهة برمجة التطبيقات) أو إصدار أحدث، يُرجى العِلم أنّ هناك مجموعة محدودة من حقول بيانات جهات الاتصال وطرقها قديمة.

في إطار الشروط المذكورة، يمحو النظام بشكل دوري أي قيم مكتوبة في حقول البيانات التالية:

واجهات برمجة التطبيقات المستخدمة لإعداد حقول البيانات أعلاه هي أيضًا قديمة:

وبالإضافة إلى ذلك، لم تعد الحقول التالية تعرض جهات اتصال متكررة. يُرجى العلم بأنّ بعض هذه الحقول لا تؤثر في ترتيبات جهات الاتصال إلا عندما تكون جهات الاتصال جزءًا من نوع بيانات معيّن.

في حال وصول تطبيقاتك إلى هذه الحقول أو واجهات برمجة التطبيقات أو تعديلها، يمكنك استخدام طرق بديلة. على سبيل المثال، يمكنك تنفيذ حالات استخدام معيّنة باستخدام موفّري المحتوى الخاص أو البيانات الأخرى المخزَّنة داخل تطبيقك أو أنظمة الخلفية.

للتأكّد من أنّ وظائف تطبيقك لا تتأثر بهذا التغيير، يمكنك محو حقول البيانات هذه يدويًا. ولإجراء ذلك، شغِّل أمر ADB التالي على جهاز يعمل بنظام التشغيل Android 4.1 (المستوى 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" إذا كانت جهة الاتصال هي الملف الشخصي للمستخدم.

البيانات الوصفية لمزوِّد جهات الاتصال

يدير "مقدِّم جهات الاتصال" البيانات التي تتتبّع حالة بيانات جهات الاتصال في المستودع. يتم تخزين هذه البيانات الوصفية حول المستودع في أماكن مختلفة، بما في ذلك صفوف جدول "جهات الاتصال الأولية" و"البيانات" و"جهات الاتصال" والجدول "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.

وعلى وجه الخصوص، يجب أن يكون رقم تعريف المصدر فريدًا لكل نوع حساب، كما يجب أن يكون ثابتًا في جميع عمليات المزامنة:

  • فريد: يجب أن يكون لكل جهة اتصال أولية في الحساب رقم تعريف مصدر خاص بها. إذا لم تفرض هذا الإجراء، سيؤدي ذلك إلى حدوث مشاكل في تطبيق جهات الاتصال. لاحظ أن جهتي اتصال أوليتين من نوع الحساب نفسه قد يكون لهما معرّف المصدر نفسه. على سبيل المثال، يُسمح لجهة الاتصال الأولية "Thomas Higginson" للحساب emily.dickinson@gmail.com باستخدام رقم تعريف المصدر نفسه الخاص بجهة الاتصال الأولية "Thomas Higginson" للحساب 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 واحد. لتسهيل ذلك، يوفر موفِّر جهات الاتصال بُنى entity، والتي تعمل كعمليات ضم قاعدة بيانات بين الجداول.

يشبه الكيان الجدول المكون من أعمدة محددة من جدول رئيسي وجدوله الفرعي. عند طلب البحث عن كيان، يمكنك تقديم توقّع ومعايير بحث استنادًا إلى الأعمدة المتاحة من الكيان. والنتيجة هي Cursor يحتوي على صف واحد لكل صف جدول فرعي تم استرداده. على سبيل المثال، إذا أجريت طلب بحث عن ContactsContract.Contacts.Entity لاسم جهة اتصال وجميع صفوف ContactsContract.CommonDataKinds.Email لجميع جهات الاتصال الأولية المرتبطة بهذا الاسم، ستحصل على حرف Cursor يحتوي على صف واحد لكل صف ContactsContract.CommonDataKinds.Email.

تعمل الكيانات على تبسيط طلبات البحث. باستخدام أحد الكيانات، يمكنك استرداد جميع بيانات جهات الاتصال لجهة اتصال أو جهة اتصال أولية في آنٍ واحد، بدلاً من الحاجة إلى إجراء طلب بحث في الجدول الرئيسي أولاً للحصول على رقم تعريف، ثم إجراء طلب بحث في الجدول الفرعي باستخدام رقم التعريف هذا. بالإضافة إلى ذلك، يعالج "مقدِّم جهات الاتصال" طلب بحث في أحد الكيانات في معاملة واحدة، ما يضمن أن تكون البيانات التي تم استردادها متسقة داخليًا.

ملاحظة: لا يحتوي الكيان عادةً على جميع أعمدة الجدول الرئيسي والثانوي. إذا كنت تحاول العمل باستخدام اسم عمود غير مُدرَج في قائمة ثوابت اسم العمود الخاصة بالكيان، ستحصل على Exception.

يعرض المقتطف التالي كيفية استرداد جميع صفوف جهات الاتصال الأولية لجهة اتصال. والمقتطف هو جزء من تطبيق أكبر يشتمل على نشاطين، هما "الرئيسي" و "التفاصيل". ويعرض النشاط الرئيسي قائمة بصفوف جهات الاتصال. عندما يختار المستخدم إحدى الصفوف، يرسل النشاط رقم تعريفه إلى النشاط التفصيلي. يستخدم نشاط التفاصيل 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 معاودة الاتصال إلى onLoadFinished(). تمثّل إحدى الوسيطات الواردة إلى هذه الطريقة Cursor التي تحتوي على نتائج طلب البحث. وفي تطبيقك الخاص، يمكنك الحصول على البيانات من جهاز "Cursor" هذا لعرضها أو العمل معه بشكل أكبر.

التعديل المجمّع

كلما أمكن، عليك إدراج البيانات في "مقدِّم جهات الاتصال" وتعديلها وحذفها في "وضع الدفعة"، من خلال إنشاء ArrayList من كائنات ContentProviderOperation واستدعاء applyBatch(). وبما أنّ "مقدِّم جهات الاتصال" ينفِّذ جميع العمليات في applyBatch() في عملية واحدة، لن تترك التعديلات التي تُجريها أبدًا مستودع جهات الاتصال في حالة غير متّسقة. يسهّل التعديل المجمّع أيضًا إدراج جهة اتصال أولية وبيانات تفاصيلها في الوقت نفسه.

ملاحظة: لتعديل جهة اتصال أولية واحدة، يمكنك إرسال هدف إلى تطبيق جهات الاتصال على الجهاز بدلاً من معالجة التعديل في تطبيقك. يمكنك الاطّلاع على مزيد من التفاصيل بخصوص إجراء ذلك في قسم الاسترجاع والتعديل باستخدام الأهداف.

النقاط المحقّقة

يمكن أن يؤدي التعديل المجمّع الذي يحتوي على عدد كبير من العمليات إلى حظر العمليات الأخرى، ما يؤدي إلى ترك انطباع سيئ لدى المستخدم بشكل عام. لتنظيم جميع التعديلات التي تريد إجراؤها في أقل عدد ممكن من القوائم المنفصلة، وفي الوقت نفسه منعها من حظر النظام، عليك تحديد نقاط النتيجة لعملية واحدة أو أكثر. نقطة العائد هي عنصر ContentProviderOperation تم ضبط قيمته isYieldAllowed() على true. عندما يواجه "مقدِّم جهات الاتصال" نقطة تسليم، يتوقف عمله مؤقتًا للسماح بتشغيل العمليات الأخرى ويتم إغلاق المعاملة الحالية. عندما يبدأ مقدّم الخدمة عملية الشراء مجددًا، يبدأ بالعملية التالية في ArrayList ويبدأ معاملة جديدة.

تؤدي النقاط المحقَّقة إلى إجراء أكثر من معاملة واحدة لكل اتصال بـ "applyBatch()". لهذا السبب، يجب ضبط نقطة نتيجة للعملية الأخيرة لمجموعة من الصفوف ذات الصلة. على سبيل المثال، يجب تحديد نقطة نتيجة للعملية الأخيرة في مجموعة تضيف صفوف جهات الاتصال الأولية وصفوف البيانات المرتبطة بها، أو قيمة العملية الأخيرة لمجموعة من الصفوف ذات الصلة بجهة اتصال واحدة.

تعتبر نقاط الجرعة أيضًا وحدة من العمليات الذرية. وستنجح جميع عمليات الوصول بين نقطتين من نقاط العائدات أو ستفشل كوحدة واحدة. في حال عدم تحديد أي نقاط حصيلة، ستكون أصغر عملية ذرية هي مجموعة العمليات بالكامل. وإذا كنت تستخدم نقاطًا، ستمنع العمليات من التأثير سلبًا في أداء النظام، مع ضمان أنّ مجموعة فرعية من العمليات شاملة.

مراجع الرجوع إلى التعديلات

عند إدراج صف جهة اتصال أولي جديد وصفوف البيانات المرتبطة به كمجموعة من عناصر ContentProviderOperation، عليك ربط صفوف البيانات بصف جهة الاتصال الأوليّة عن طريق إدراج قيمة _ID لجهة الاتصال الأولية على أنّها قيمة RAW_CONTACT_ID. ولا تتوفّر هذه القيمة عند إنشاء ContentProviderOperation لصف البيانات، لأنّك لم تطبّق بعد العنصر ContentProviderOperation لصف جهة الاتصال الأولية. ولحلّ هذه المشكلة، تستخدم الصف ContentProviderOperation.Builder الطريقة withValueBackReference(). تتيح لك هذه الطريقة إدراج أو تعديل عمود كنتيجة لعملية سابقة.

تتضمّن الطريقة withValueBackReference() وسيطتَين هما:

key
المفتاح لزوج المفتاح/القيمة. يجب أن تكون قيمة هذه الوسيطة اسم العمود في الجدول الذي تعدله.
previousResult
هو الفهرس المستند إلى 0 لقيمة في مصفوفة عناصر 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 الجديد الذي ينتمي إليه.

يتم وضع علامة withYieldAllowed() على العنصر ContentProviderOperation.Builder الذي يضيف صف البريد الإلكتروني، ما يحدِّد نقطة تسليم:

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 "تأكيد" وسيتم الاحتفاظ بنسخة احتياطية من مجموعة العمليات بأكملها. يمكنك بعد ذلك اختيار إعادة محاولة الدفعة أو اتخاذ بعض الإجراءات الأخرى.

يوضّح المقتطف التالي كيفية إنشاء 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
    }

الاسترجاع والتعديل باستخدام الأغراض

إنّ إرسال غرض إلى تطبيق جهات الاتصال في الجهاز يتيح لك الوصول إلى "مقدِّم جهات الاتصال" بشكل غير مباشر. يبدأ الغرض من واجهة مستخدم تطبيق "جهات الاتصال" على الجهاز، والتي يمكن للمستخدمين من خلالها تنفيذ العمل المرتبط بجهات الاتصال. من خلال هذا النوع من الوصول، يمكن للمستخدمين:

  • اختر جهة اتصال من القائمة وأعدها إلى تطبيقك لإجراء المزيد من العمل.
  • تعديل بيانات جهة اتصال حالية
  • إدراج جهة اتصال أولية جديدة لأي من حساباتها.
  • حذف جهة اتصال أو بيانات جهات الاتصال

إذا كان المستخدم يدرج بيانات أو يعدّلها، يمكنك جمع البيانات أولاً وإرسالها كجزء من الغرض.

عند استخدام أهداف للوصول إلى "مقدِّم جهات الاتصال" من خلال تطبيق جهات الاتصال على الجهاز، لن تحتاج إلى كتابة واجهة المستخدم أو الرمز الخاص بك للوصول إلى مقدّم الخدمة. ولن تضطر أيضًا إلى طلب إذن للقراءة أو الكتابة إلى مقدّم الخدمة. قد يفوّض تطبيق جهات الاتصال على الجهاز إذن القراءة لجهة الاتصال إليك، ولأنّك تجري تعديلات على مقدّم الخدمة من خلال تطبيق آخر، لن تحتاج إلى أذونات الكتابة.

يتم وصف العملية العامة لإرسال هدف الوصول إلى مقدّم الخدمة بالتفصيل في دليل أساسيات موفِّر المحتوى ضمن القسم "الوصول إلى البيانات عبر الأغراض". في الجدول 4، يتم تلخيص الإجراء ونوع MIME وقيم البيانات التي تستخدمها للمهام المتاحة، بينما يتم تلخيص القيم الإضافية التي يمكنك استخدامها مع putExtra() في المستندات المرجعية لـ ContactsContract.Intents.Insert:

الجدول 4. أغراض مقدِّمي خدمة جهات الاتصال

المهمة الإجراء البيانات نوع MIME Notes
اختيار جهة اتصال من قائمة 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()، يتم إرسال معرّف الموارد المنتظم للمحتوى لجهة الاتصال الأولية التي تمت إضافتها حديثًا إلى طريقة معاودة الاتصال onActivityResult() الخاصة بنشاطك في الوسيطة Intent ضمن حقل "البيانات". لمعرفة القيمة، يمكنك طلب "getData()".
تعديل جهة اتصال ACTION_EDIT CONTENT_LOOKUP_URI لجهة الاتصال. سيسمح نشاط المحرّر للمستخدم بتعديل أي من البيانات المرتبطة بجهة الاتصال هذه. Contacts.CONTENT_ITEM_TYPE، جهة اتصال واحدة يعرض شاشة "تعديل جهة الاتصال" في تطبيق جهات الاتصال. ويتم عرض القيم الإضافية التي تضيفها إلى الغرض. عندما ينقر المستخدم على تم لحفظ التعديلات، يعود نشاطك إلى المقدّمة.
عرض أداة اختيار يمكنها إضافة بيانات أيضًا ACTION_INSERT_OR_EDIT لا ينطبق CONTENT_ITEM_TYPE يعرض هذا الغرض دائمًا شاشة أداة اختيار تطبيق جهات الاتصال. ويمكن للمستخدم اختيار جهة اتصال لتعديلها أو إضافة جهة اتصال جديدة. ستظهر إما شاشة التعديل أو شاشة الإضافة، حسب اختيار المستخدم، وسيتم عرض البيانات الإضافية التي تنقلها في الغرض. إذا كان تطبيقك يعرض بيانات الاتصال، مثل عنوان البريد الإلكتروني أو رقم الهاتف، استخدِم هذا الغرض للسماح للمستخدم بإضافة البيانات إلى جهة اتصال حالية. جهة اتصال

ملاحظة: ليس من الضروري إرسال قيمة للاسم في العناصر الإضافية لهذا الغرض، لأنّ المستخدم دائمًا يختار اسمًا حاليًا أو يضيف اسمًا جديدًا. إضافةً إلى ذلك، إذا أرسلت اسمًا واختار المستخدم إجراء تعديل، سيعرض تطبيق "جهات الاتصال" الاسم الذي ترسله، وستحلّ محلّ القيمة السابقة. إذا لم يلاحظ المستخدم ذلك وحفظ التعديل، ستفقد القيمة القديمة.

لا يسمح لك تطبيق جهات الاتصال على الجهاز بحذف جهة اتصال أولية أو أي من بياناتها بغرض. بدلاً من ذلك، يمكنك استخدام ContentResolver.delete() أو ContentProviderOperation.newDelete() لحذف جهة اتصال أولية.

يوضّح المقتطف التالي كيفية إنشاء وإرسال غرض يدرج جهة اتصال أو بيانات أولية جديدة:

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.
يساعدك استخدام هذه الثوابت في تجنُّب الأخطاء. وسيتم إعلامك أيضًا بتحذيرات برامج التحويل البرمجي في حال إيقاف أي من الثوابت.

صفوف البيانات المخصّصة

من خلال إنشاء أنواع MIME مخصّصة خاصة بك واستخدامها، يمكنك إدراج صفوف بياناتك وتعديلها وحذفها واستردادها في الجدول ContactsContract.Data. تقتصر صفوفك على استخدام العمود المحدّد في ContactsContract.DataColumns، ولكن يمكنك ربط أسماء الأعمدة الخاصة بنوع محدّد مع أسماء الأعمدة التلقائية. في تطبيق "جهات الاتصال" على الجهاز، يتم عرض بيانات الصفوف، ولكن لا يمكن تعديلها أو حذفها، ولا يمكن للمستخدمين إضافة بيانات إضافية. للسماح للمستخدمين بتعديل صفوف البيانات المخصّصة، يجب توفير نشاط تعديل في تطبيقك الخاص.

لعرض بياناتك المخصّصة، قدِّم ملف contacts.xml يحتوي على العنصر <ContactsAccountType> وعنصر واحد أو أكثر من عناصر <ContactsDataKind> الثانوية الخاصة به. يمكنك الاطّلاع على مزيد من التفاصيل في القسم <ContactsDataKind> element.

للمزيد من المعلومات حول أنواع MIME المخصّصة، يُرجى قراءة دليل إنشاء موفِّر محتوى.

محوّلات مزامنة مزوّد جهات الاتصال

تم تصميم "مقدِّم جهات الاتصال" خصيصًا لمعالجة مزامنة بيانات جهات الاتصال بين جهاز وخدمة على الإنترنت. يتيح ذلك للمستخدمين تنزيل البيانات الحالية على جهاز جديد وتحميل البيانات الحالية إلى حساب جديد. تضمن المزامنة أيضًا توفير أحدث البيانات للمستخدمين، بغض النظر عن مصدر الإضافات والتغييرات. وهناك ميزة أخرى للمزامنة وهي أنها تجعل بيانات جهات الاتصال متاحة حتى عندما لا يكون الجهاز متصلاً بالشبكة.

على الرغم من أنّه يمكنك تنفيذ المزامنة بعدة طرق، يوفّر نظام Android إطار عمل مزامنة مكوّن إضافي يؤدي إلى تشغيل المهام التالية تلقائيًا:

  • جارٍ التحقّق من توفّر الشبكة.
  • جدولة عملية المزامنة وتنفيذها استنادًا إلى إعدادات المستخدم المفضَّلة.
  • إعادة تشغيل المزامنات التي توقفت

لاستخدام إطار العمل هذا، يجب توفير مكوّن إضافي لمحوّل المزامنة. يكون كل محوّل مزامنة فريدًا لموفّر الخدمة والمحتوى، ولكن يمكنه التعامل مع أسماء حسابات متعددة للخدمة نفسها. ويسمح إطار العمل أيضًا باستخدام محوّلات متعددة للمزامنة مع الخدمة ومقدّم الخدمة نفسهما.

فئات وملفات محوِّل المزامنة

عليك تنفيذ محوّل مزامنة كفئة فرعية من AbstractThreadedSyncAdapter وتثبيتها كجزء من تطبيق Android. يتعرّف النظام على محوّل المزامنة من العناصر في بيان التطبيق، ومن ملف XML خاص يُشار إليه في البيان. ويحدّد ملف XML نوع حساب الخدمة على الإنترنت والهيئة الخاصة بموفِّر المحتوى، واللذين يحدّدان معًا المحوّل بشكل فريد. لا يصبح محوّل المزامنة نشطًا حتى يضيف المستخدم حسابًا لنوع حساب محوّل المزامنة ويمكّن المزامنة لموفّر المحتوى الذي تتم مزامنة محوّل المزامنة معه. عندها، يبدأ النظام في إدارة المحوّل، ويطلب منه المزامنة بين موفِّر المحتوى والخادم.

ملاحظة: إنّ استخدام نوع حساب كجزء من تعريف محوّل المزامنة يتيح للنظام اكتشاف محوّلات المزامنة التي يمكنها الوصول إلى خدمات مختلفة من المؤسسة نفسها وتجميعها معًا. على سبيل المثال، تستخدم محوّلات المزامنة لخدمات Google على الإنترنت نوع الحساب com.google نفسه. عندما يضيف المستخدمون حساب Google إلى أجهزتهم، يتم إدراج جميع محوّلات المزامنة المثبَّتة لخدمات Google معًا، وتتم مزامنة كل محوّل مزامنة مع موفّر محتوى مختلف على الجهاز.

ولأن معظم الخدمات تتطلب من المستخدمين إثبات هويتهم قبل الوصول إلى البيانات، يوفّر نظام Android إطار عمل مصادقة يشبه إطار عمل محوّل المزامنة ويتم استخدامه غالبًا مع ذلك. يستخدم إطار عمل المصادقة برامج مصادقة للمكوّنات الإضافية وهي فئات فرعية من AbstractAccountAuthenticator. يتحقّق برنامج المصادقة من هوية المستخدم باتّباع الخطوات التالية:

  1. جمع اسم المستخدم أو كلمة المرور أو معلومات مشابهة (بيانات اعتماد المستخدم).
  2. يرسل بيانات الاعتماد إلى الخدمة.
  3. يفحص ردّ الخدمة.

إذا قبلت الخدمة بيانات الاعتماد، يمكن لبرنامج المصادقة تخزين بيانات الاعتماد لاستخدامها لاحقًا. وبسبب إطار عمل برنامج المصادقة، يمكن لـ AccountManager توفير إمكانية الوصول إلى أي رموز مصادقة تتوافق مع برنامج المصادقة ويختار عرضها، مثل رموز المصادقة OAuth2.

على الرغم من أنّ المصادقة غير مطلوبة، تستخدمها معظم خدمات جهات الاتصال. ومع ذلك، لا يلزمك استخدام إطار عمل مصادقة Android لإجراء المصادقة.

تنفيذ محوّل المزامنة

لتنفيذ محوّل مزامنة لـ "مقدِّم جهات الاتصال"، عليك البدء بإنشاء تطبيق Android يحتوي على ما يلي:

هو مكوّن Service يستجيب لطلبات النظام للربط بمحوّل المزامنة.
عندما يريد النظام تشغيل مزامنة، يستدعي طريقة onBind() للخدمة للحصول على IBinder لمحوّل المزامنة. ويسمح ذلك للنظام بإجراء طلبات متعدّدة العمليات لطرق المحوّل.
محوِّل المزامنة الفعلي، الذي تم تنفيذه كفئة فرعية ملموسة من AbstractThreadedSyncAdapter.
تؤدي هذه الفئة وظيفة تنزيل البيانات من الخادم وتحميل البيانات من الجهاز وحلّ التعارضات. ويتم تنفيذ العمل الرئيسي للمحوّل باستخدام الطريقة onPerformSync(). يجب إنشاء مثيل لهذه الفئة على أنّها فردية.
فئة فرعية من Application.
تعمل هذه الفئة كمصنِّع لمحوّل المزامنة سينغلتون. استخدِم الإجراء onCreate() لإنشاء مثيل لمحوّل المزامنة، ووفِّر طريقة "getter" ثابتة لإعادة التوجيه المفرد إلى طريقة onBind() لخدمة محوِّل المزامنة.
اختياري: مكوِّن Service يستجيب للطلبات الواردة من النظام لمصادقة المستخدم.
يبدأ AccountManager هذه الخدمة لبدء عملية المصادقة. تُنشئ طريقة onCreate() الخاصة بالخدمة مثيلاً لكائن برنامج المصادقة. عندما يريد النظام مصادقة حساب مستخدم لمحوّل مزامنة التطبيق، فإنّه يطلب طريقة onBind() في الخدمة للحصول على IBinder لجهاز المصادقة. ويسمح هذا الإجراء للنظام بإجراء طلبات متعدّدة العمليات لأساليب برنامج المصادقة.
اختياري: فئة فرعية ملموسة من AbstractAccountAuthenticator تعالج طلبات المصادقة.
توفر هذه الفئة الطرق التي يستدعيها 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_PACKAGE للربط بالموارد في تطبيقك.

يحتوي الجدول 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. ويمكنك ترك جميع الوسيطات باستثناء معرّف الموارد المنتظم (URI) للمحتوى على null. يعرض طلب البحث مؤشرًا يحتوي على صف واحد يحتوي على العمود الواحد 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، للسماح للخدمة بتلقّي رسائل intent. تحتوي بيانات الغرض الوارد على معرّف الموارد المنتظم (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 هو اسم الفئة المؤهّل بالكامل للنشاط الذي يجب أن يتلقى الغرض من تطبيق جهات الاتصال على الجهاز.

يمكن الاطّلاع على مزيد من التفاصيل حول العنصر <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 هي اسم الفئة المؤهّل بالكامل للنشاط الذي يجب أن يحصل على الغرض. القيمة invite_action_label هي سلسلة نصية يتم عرضها في قائمة إضافة اتصال في تطبيق جهات الاتصال على الجهاز.

ملاحظة: 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
اسم الفئة المؤهّل بالكامل للنشاط في تطبيقك والذي تريد تفعيله عندما يختار المستخدم إضافة اتصال من تطبيق جهات الاتصال على الجهاز.
inviteContactActionLabel
سلسلة نصية يتم عرضها للنشاط المحدّد في inviteContactActivity، في قائمة إضافة اتصال. على سبيل المثال، يمكنك استخدام السلسلة "متابعة في شبكتي". يمكنك استخدام معرِّف لمورد السلسلة لهذا التصنيف.
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
مورد قابل للرسم على جهاز 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 الموضح بمزيد من التفصيل في قسم صور ساحة المشاركات على الشبكات الاجتماعية.