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

موفِّر جهات الاتصال هو مكوّن قوي ومرن في 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. أعمدة جهات الاتصال الأوّلية المهمة

اسم العمود استخدام ملاحظات
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 وأرسلت رسالة إلكترونية إلى "توماس هيغينز"، ما أدّى إلى إضافته تلقائيًا كجهة اتصال. وهي تتابع أيضًا "colonel_tom" (رقم تعريف "توماس هيغينسون" على Twitter) على Twitter.

ينشئ مقدّم جهات الاتصال ثلاث جهات اتصال أولية نتيجةً لهذا العمل:

  1. جهة اتصال أولية لـ "توماس هيغينزون" مرتبطة بـ emily.dickinson@gmail.com نوع حساب المستخدم هو Google.
  2. جهة اتصال ثانية غير معالجة لـ "توماس هيغينزون" مرتبطة بـ emilyd@gmail.com. ونوع حساب المستخدم هو أيضًا Google. هناك جهة اتصال أساسية ثانية حتى إذا كان الاسم مطابقًا لاسم سابق، لأنّه تمت إضافة الشخص لحساب مستخدم مختلف.
  3. جهة اتصال ثالثة غير معالجة لـ "توماس هيغينزون" مرتبطة بـ "belle_of_amherst" ويكون نوع حساب المستخدم على Twitter.

البيانات

كما ذكرنا سابقًا، يتم تخزين بيانات جهة الاتصال الأوّلية في صف ContactsContract.Data مرتبط بقيمة _ID لجهة الاتصال الأوّلية. ويتيح ذلك لجهة اتصال أولية واحدة الاحتفاظ بمثيلات متعددة من النوع نفسه من البيانات، مثل عناوين البريد الإلكتروني أو أرقام الهواتف. على سبيل المثال، إذا كان "توماس هيغينزون" emilyd@gmail.com (صف جهة الاتصال الأوّلي لـ "توماس هيغينزون" المرتبط بحساب 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 صف البيانات الذي يحتوي على البيانات الأساسية للنوع. على سبيل المثال، إذا الضغط مع الاستمرار على رقم هاتف لأحد جهات الاتصال واختيار ضبط القيمة التلقائية، سيتم ضبط عمود IS_PRIMARY في الصف ContactsContract.Data الذي يحتوي على الرقم على قيمة غير صفرية.

أسماء الأعمدة العامة

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

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

وفقًا للعرف، يتم حجز العمود DATA15 لتخزين بيانات Binary Large Object (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.RawContacts المرتبط بكل صف من صفوف ContactsContract.RawContacts الأوّلية.

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

يوضّح الشكل 3 كيفية ارتباط الجداول الرئيسية الثلاثة ببعضها.

الجداول الرئيسية لمقدّم جهات الاتصال

الشكل 3: علاقات جداول "جهات الاتصال" و"جهات الاتصال الأولية" و"التفاصيل"

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

وفقًا للشروط المذكورة، يُرجى العِلم أنّ النظام يُزيل بصفة دورية أي قيم يتم كتابتها في حقول البيانات التالية:

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

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

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

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

لاسترداد صف جهة الاتصال الذي يحتوي على الملف الشخصي للمستخدم، اتصل بالرقم 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.

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

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

إذن الوصول إلى مقدّم جهات الاتصال

يصف هذا القسم إرشادات الوصول إلى البيانات من "مزوّد جهات الاتصال"، مع التركيز على ما يلي:

  • طلبات البحث عن الكيانات.
  • تعديل مجمّع
  • الاسترجاع والتعديل بأغراض.
  • سلامة البيانات:

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

طلب البحث عن الكيانات

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

الكيان يشبه جدولاً مكونًا من أعمدة محددة من جدول رئيسي وجدوله الفرعي. عند طلب معلومات عن عنصر، عليك تقديم عرض ومعايير بحث استنادًا إلى الأعمدة المتاحة من العنصر. والنتيجة هي 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
    }

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

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

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

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

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

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

الجدول 4: نوايا مقدّم جهات الاتصال

المهمة الإجراء البيانات نوع MIME ملاحظات
اختيار جهة اتصال من قائمة ACTION_PICK أحد الخيارَين التاليَين:
  • Contacts.CONTENT_URI، الذي يعرض قائمة بجهات الاتصال.
  • Phone.CONTENT_URI، الذي يعرض قائمة بأرقام هواتف جهة اتصال غير معالجة.
  • StructuredPostal.CONTENT_URI، الذي يعرض قائمة بالعناوين البريدية لجهة اتصال غير معالجة.
  • Email.CONTENT_URI، الذي يعرض قائمة بعناوين البريد الإلكتروني لجهة اتصال غير معالجة.
لم يتم الاستخدام تعرِض هذه السمة قائمة بجهات الاتصال الأوّلية أو قائمة بالبيانات من جهة اتصال أوّلية، استنادًا إلى نوع معرف الموارد المنتظم للمحتوى الذي تقدّمه.

استدعاء دالة startActivityForResult()، التي تعرض معرّف الموارد المنتظم (URI) للمحتوى في الصف المحدّد تنسيق عنوان URL هو عنوان URL لمحتوى الجدول مع إلحاق LOOKUP_ID بالصف. يفوّض تطبيق جهات الاتصال على الجهاز أذونات القراءة والكتابة إلى عنوان URI للمحتوى طوال مدة نشاطك. اطّلِع على دليل أساسيات مقدّمي المحتوى للحصول على مزيد من التفاصيل.

إدراج جهة اتصال جديدة غير معالجة Insert.ACTION لا ينطبق RawContacts.CONTENT_TYPE، نوع MIME لمجموعة من جهات الاتصال الأولية. يعرض شاشة إضافة جهة اتصال في تطبيق جهات الاتصال على الجهاز. يتم عرض قيم الإضافات التي تضيفها إلى الطلب. في حال الإرسال باستخدام startActivityForResult()، يتمّ تمرير معرّف الموارد المنتظم للمحتوى الخاص بجهة الاتصال الأوّلية التي تمت إضافتها حديثًا إلى onActivityResult() طريقة الاستدعاء في نشاطك ضمن الوسيطة Intent في الحقل "data". للحصول على القيمة، اتّصِل بالرقم 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 و الفئات الفرعية لها للسلطات وعناوين URL للمحتوى ومسارات عناوين URL وأسماء الأعمدة وأنواع 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() لإنشاء مثيل لمحوِّر المزامنة، و قدِّم طريقة "جلب" ثابتة لعرض العنصر الفردي على الأسلوب onBind() لخدمة محوِّر المزامنة.
اختياري: مكوّن Service يستجيب للطلبات الواردة من النظام لمصادقة المستخدم
يبدأ AccountManager هذه الخدمة لبدء عملية مصادقة. تنشئ طريقة onCreate() في الخدمة مثيلًا لعنصر المصادقة. عندما يريد النظام مصادقة حساب مستخدم لمحوّل مزامنة التطبيق، يستدعي طريقة onBind() للخدمة للحصول على IBinder لبرنامج المصادقة. يتيح ذلك للنظام إجراء طلبات متعدّدة المعالجة لأساليب برنامج المصادقة.
اختياري: فئة فرعية محدّدة من AbstractAccountAuthenticator تعالج طلبات المصادقة.
توفّر هذه الفئة طرقًا يستدعيها AccountManager لمصادقة بيانات اعتماد المستخدم مع الخادم. تختلف تفاصيل عملية المصادقة على نطاق واسع، استنادًا إلى تكنولوجيا الخادم المستخدَمة. يجب مراجعة مستندات برنامج الخادم للاطّلاع على مزيد من المعلومات عن المصادقة.
ملفات XML التي تحدد محول المزامنة والمصادق للنظام.
تم تحديد مكونات خدمة محوِّل المزامنة وخدمات المصادقة الموضّحة سابقًا في عناصر <service> في بيان التطبيق. تحتوي هذه العناصر على <meta-data> عناصر فرعية تقدّم بيانات محدّدة للنظام:
  • يشير عنصر <meta-data> لخدمة محوِّل المزامنة إلىملف XML res/xml/syncadapter.xml. ويحدِّد هذا الملف بدوره عنوان URL لخدمة الويب التي ستتم مزامنتها مع مقدّم جهات الاتصال، ونوع حساب لخدمة الويب.
  • اختياري: يشير عنصر <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.ContactsContacts.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.Contacts استخدم.StreamItemscolumn#COMMENTS
اختيارية. تخزِّن معلومات موجزة يمكنك عرضها في بداية عنصر مصدر بيانات.
android.provider.ContactsContract.StreamItemsColumns#TEXT
نص عنصر البث، سواء كان المحتوى الذي نشره مصدر العنصر، أو وصفًا لبعض الإجراءات التي أدّت إلى إنشاء عنصر البث يمكن أن يحتوي هذا العمود على أي تنسيق وصور موارد مضمّنة يمكن عرضها باستخدام fromHtml(). قد يقطع موفّر المحتوى المحتوى الطويل أو يحذف منه أجزاء، ولكن سيحاول تجنُّب تقسيم العلامات.
android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP
سلسلة نصية تحتوي على وقت إدراج عنصر البث أو تعديله، على شكل ملي ثانية منذ بدء حساب الوقت وتكون التطبيقات التي تدرج عناصر ساحة المشاركات أو تعدِّلها مسؤولة عن الحفاظ على هذا العمود، ولا تتم إدارته تلقائيًا من خلال "موفّر جهات الاتصال".

لعرض معلومات تعريفية لعناصر البث، استخدِم ‎#RES_ICON في android.provider.ContactsContract.StreamItemsColumns و‎#RES_LABEL في android.provider.ContactsContract.StreamItemsColumns و‎#RES_PACKAGE في android.provider.ContactsContract.StreamItemsColumns للربط بالموارد في تطبيقك.

يحتوي جدول 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. يتم تخزين مراجع الصور في جدول في الأعمدة التالية:

عمود PHOTO في android.provider.ContactsContract.StreamItemPhotos‏# (وهو ملف BLOB)
عرض ثنائي للصورة، يغيّر مقدّم الخدمة حجمه لتخزينه وعرضه يتوفّر هذا العمود للتوافق مع الإصدارات السابقة من موفِّر جهات الاتصال الذي كان يستخدمه لتخزين الصور. ومع ذلك، في الإصدار الحالي، يجب عدم استخدام هذا العمود لتخزين الصور. وبدلاً من ذلك، استخدِم android.provider.Contacts استخدم.StreamItemPhotosColumns#PHOTO_FILE_ID أو android.provider.ContactsFilters.StreamItemPhotosColumns#PHOTO_URI (المستندان الموضّحان في النقاط التالية) إلى تخزين الصور في أيّ ملف. يحتوي هذا العمود الآن على صورة مصغّرة للصورة المتاحة للقراءة.
android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID
هو معرّف رقمي لصورة جهة اتصال أوّلية. أضِف هذه القيمة إلى الثابت DisplayPhoto.CONTENT_URI للحصول على معرّف موارد منتظم للمحتوى يشير إلى ملف صورة واحد، ثم استخدِم openAssetFileDescriptor() للحصول على معرّف ملف الصورة.
android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI
معرّف موارد منتظم للمحتوى يشير مباشرةً إلى ملف الصورة التي يمثّلها هذا الصف اتصل بـ openAssetFileDescriptor() باستخدام معرّف URI هذا للحصول على معرّف ملف الصورة.

استخدام جداول "البثّ على الشبكات الاجتماعية"

تعمل هذه الجداول بالطريقة نفسها التي تعمل بها الجداول الرئيسية الأخرى في "موفر جهات الاتصال"، باستثناء ما يلي:

  • تتطلّب هذه الجداول أذونات وصول إضافية. للقراءة من هذه المصادر، يجب أن يحصل تطبيقك على الإذن android.Manifest.permission#READ_SOCIAL_STREAM. لتعديلها، يجب أن يحصل تطبيقك على الإذن android.Manifest.permission#WRITE_SOCIAL_STREAM.
  • بالنسبة إلى جدول android.provider.ContactsContract.StreamItems، يكون عدد الصفوف المخزّنة لكل جهة اتصال أولية محدودًا. بعد بلوغ هذا الحد، يُفسح مقدّم جهات الاتصال مساحة لصفوف عناصر البث الجديدة عن طريق حذف الصفوف التي تحتوي على أقدم قيمة لـ android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP. للحصول على الحدّ الأقصى، أدخِل طلب بحث إلى معرّف الموارد المنتظم للمحتوى android.provider.ContactsContract.StreamItems#CONTENT_LIMIT_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 لاستخدامها لاحقًا.
  • بالإضافة إلى المزامنة العادية، يمكنك تفعيل محوِّل المزامنة لاسترداد بيانات إضافية عندما يختار المستخدم جهة اتصال لعرضها. يتيح ذلك لمحوِّل المزامنة retrieving استرداد الصور العالية الدقة وأحدث عناصر البث لجهة الاتصال.
  • من خلال تسجيل إشعار في تطبيق جهات الاتصال على الجهاز وموفِّر جهات الاتصال، يمكنك تلقّي نية عند عرض جهة اتصال، وعند هذه النقطة، يمكنك تعديل حالة جهة الاتصال من خدمتك. قد يكون هذا الأسلوب أسرع ويستخدم معدل نقل بيانات أقل مقارنةً بإجراء مزامنة كاملة باستخدام محوِّل مزامنة.
  • يمكن للمستخدمين إضافة جهة اتصال إلى خدمة الشبكات الاجتماعية أثناء الاطّلاع على جهة الاتصال في تطبيق جهات الاتصال على الجهاز. يمكنك تفعيل هذه الميزة باستخدام ميزة "دعوة جهة اتصال"، والتي يمكنك تفعيلها من خلال نشاط يضيف جهة اتصال حالية إلى الشبكة وملف 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 هو اسم فئة النشاط المؤهَّل بالكامل الذي من المفترض أن يتلقّى النية من تطبيق جهات اتصال الجهاز.

يمكنك الاطّلاع على وصف أكثر تفصيلاً للعنصر <ContactsAccountType> في قسم عنصر<ContactsAccountType>.

يحتوي الإجراء الوارد على معرّف الموارد الموحّد للمحتوى الخاص بالعنصر أو الصورة التي نقر عليها المستخدم. للحصول على أنشطة منفصلة للعناصر النصية والصور، استخدِم السمتَين في الملف نفسه.

التفاعل مع خدمة الشبكات الاجتماعية

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

  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، باستخدام نوع ContactsContract.CommonDataKinds.GroupMembership MIME.

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