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

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

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

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

يفترض هذا الدليل أنّك تعرف أساسيات موفّري المحتوى في Android. لمزيد من المعلومات حول موفّري المحتوى على Android، يُرجى الاطّلاع على دليل أساسيات موفّر المحتوى.

مؤسسة مقدّم جهات الاتصال

مقدّم جهات الاتصال هو أحد مكونات موفّر المحتوى في Android. تحتفظ هذه الخدمة بثلاثة أنواع من البيانات حول شخص معيّن، ويتوافق كل نوع مع جدول يوفّره مقدّم الخدمة، كما هو موضّح في الشكل 1:

الشكل 1. بنية جدول "مقدّم جهات الاتصال"

يُشار عادةً إلى الجداول الثلاثة بأسماء فئات العقود. تحدّد الفئات ثوابت لمعرّفات الموارد الموحّدة الخاصة بالمحتوى وأسماء الأعمدة وقيم الأعمدة المستخدَمة في الجداول:

جدول 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. جهة اتصال أولية باسم "Thomas Higginson" مرتبطة بـ emily.dickinson@gmail.com نوع حساب المستخدم هو Google.
  2. جهة اتصال ثانية غير معالَجة باسم "Thomas Higginson" مرتبطة بـ emilyd@gmail.com نوع حساب المستخدم هو أيضًا Google. هناك جهة اتصال ثانية غير معالجة على الرغم من أنّ الاسم مطابق لاسم سابق، لأنّه تمت إضافة الشخص إلى حساب مستخدم مختلف.
  3. جهة اتصال ثالثة غير معالَجة باسم "Thomas Higginson" مرتبطة بـ "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) مثل الصور المصغّرة.

أسماء الأعمدة الخاصة بنوع معيّن

لتسهيل العمل مع الأعمدة لنوع معيّن من الصفوف، يوفّر Contacts Provider أيضًا ثوابت لأسماء الأعمدة خاصة بالنوع، ويتم تحديدها في الفئات الفرعية من ContactsContract.CommonDataKinds. تمنح الثوابت ببساطة اسمًا ثابتًا مختلفًا لاسم العمود نفسه، ما يساعدك في الوصول إلى البيانات في صف من نوع معيّن.

على سبيل المثال، يحدّد الصف ContactsContract.CommonDataKinds.Email ثوابت أسماء الأعمدة الخاصة بنوع معين لصف ContactsContract.Data يتضمّن نوع MIME Email.CONTENT_ITEM_TYPE. يحتوي الصف على الثابت ADDRESS لعمود عنوان البريد الإلكتروني. القيمة الفعلية لـ ADDRESS هي "data1"، وهي نفس الاسم العامود.

تنبيه: لا تُضِف بياناتك المخصّصة إلى جدول ContactsContract.Data باستخدام صف يتضمّن أحد أنواع MIME المحدّدة مسبقًا من الموفّر. وفي حال عدم الالتزام بذلك، قد تفقد البيانات أو يتوقف مقدّم الخدمة عن العمل. على سبيل المثال، يجب عدم إضافة صف بنوع MIME Email.CONTENT_ITEM_TYPE يحتوي على اسم مستخدم بدلاً من عنوان بريد إلكتروني في العمود DATA1. إذا كنت تستخدم نوع MIME مخصّصًا للصف، يمكنك تحديد أسماء الأعمدة الخاصة بالنوع واستخدام الأعمدة بالطريقة التي تريدها.

يوضّح الشكل 2 كيف تظهر الأعمدة الوصفية وأعمدة البيانات في صف ContactsContract.Data، وكيف "تتراكب" أسماء الأعمدة الخاصة بالنوع على أسماء الأعمدة العامة.

كيفية ربط أسماء الأعمدة الخاصة بنوع معيّن بأسماء الأعمدة العامة

الشكل 2. أسماء الأعمدة الخاصة بنوع معيّن وأسماء الأعمدة العامة

فئات أسماء الأعمدة الخاصة بالنوع

يسرد الجدول 2 فئات أسماء الأعمدة الأكثر استخدامًا والمحدّدة النوع:

الجدول 2. فئات أسماء الأعمدة الخاصة بالنوع

فئة التعيين نوع البيانات الملاحظات
ContactsContract.CommonDataKinds.StructuredName تمثّل هذه السمة بيانات الاسم لجهة الاتصال الأولية المرتبطة بصف البيانات هذه. لا يحتوي جهة الاتصال الأولية إلا على أحد هذه الصفوف.
ContactsContract.CommonDataKinds.Photo الصورة الرئيسية لجهة الاتصال الأولية المرتبطة بصف البيانات هذه لا يحتوي جهة الاتصال الأولية إلا على أحد هذه الصفوف.
ContactsContract.CommonDataKinds.Email عنوان البريد الإلكتروني لجهة الاتصال الأولية المرتبطة بصف البيانات هذه. يمكن أن تتضمّن جهة الاتصال الأولية عناوين بريد إلكتروني متعددة.
ContactsContract.CommonDataKinds.StructuredPostal عنوان بريدي لجهة الاتصال الأولية المرتبطة بصف البيانات هذه يمكن أن تتضمّن جهة الاتصال الأولية عناوين بريدية متعددة.
ContactsContract.CommonDataKinds.GroupMembership معرّف يربط جهة الاتصال الأولية بإحدى المجموعات في "موفّر جهات الاتصال". المجموعات هي ميزة اختيارية لنوع الحساب واسمه. يمكنك الاطّلاع على وصف أكثر تفصيلاً لها في القسم مجموعات جهات الاتصال.

جهات الاتصال

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

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

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

يربط "موفّر جهات الاتصال" صف جهة اتصال بصفوف جهات الاتصال الأولية الخاصة به باستخدام العمود _ID في صف جهة الاتصال ضمن جدول Contacts. يحتوي العمود CONTACT_ID في جدول جهات الاتصال الأولية ContactsContract.RawContacts على قيم _ID لصف جهات الاتصال المرتبط بكل صف من صفوف جهات الاتصال الأولية.

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

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

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

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

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

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

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

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

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

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

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

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

يدير &quot;مقدّم جهات الاتصال&quot; البيانات التي تتتبّع حالة بيانات جهات الاتصال في المستودع. يتم تخزين هذه البيانات الوصفية الخاصة بالمستودع في أماكن مختلفة، بما في ذلك صفوف جداول Raw Contacts وData وContacts، وجدول ContactsContract.Settings، وجدول ContactsContract.SyncState. يوضّح الجدول التالي تأثير كل جزء من بيانات التعريف هذه:

الجدول 3. البيانات الوصفية في "مقدّم جهات الاتصال"

جدول العمود القيم المعنى
ContactsContract.RawContacts DIRTY "0" - لم يتم تغييرها منذ آخر مزامنة. تضع علامات على جهات الاتصال الأولية التي تم تغييرها على الجهاز ويجب إعادة مزامنتها مع الخادم. يتم ضبط القيمة تلقائيًا بواسطة "موفّر جهات الاتصال" عندما تعدّل تطبيقات Android صفًا.

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

‫"1": تم تغييره منذ آخر مزامنة، ويجب مزامنته مرة أخرى مع الخادم.
ContactsContract.RawContacts VERSION رقم إصدار هذا الصف. يزيد &quot;موفّر جهات الاتصال&quot; هذه القيمة تلقائيًا كلما تغيّر الصف أو البيانات ذات الصلة به.
ContactsContract.Data DATA_VERSION رقم إصدار هذا الصف. يزيد &quot;موفّر جهات الاتصال&quot; هذه القيمة تلقائيًا كلما تم تغيير صف البيانات.
ContactsContract.RawContacts SOURCE_ID قيمة سلسلة تحدّد بشكل فريد جهة الاتصال الأولية هذه للحساب الذي تم إنشاؤها فيه. عندما ينشئ برنامج المزامنة جهة اتصال أولية جديدة، يجب ضبط هذا العمود على المعرّف الفريد الخاص بالخادم لجهة الاتصال الأولية. عندما ينشئ تطبيق Android جهة اتصال أولية جديدة، يجب أن يترك التطبيق هذا العمود فارغًا. يشير ذلك إلى محوّل المزامنة بأنّه يجب إنشاء جهة اتصال أولية جديدة على الخادم والحصول على قيمة SOURCE_ID.

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

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

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

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

يجب إدراج البيانات وتعديلها وحذفها في Contacts Provider في "وضع الدُفعات" كلما أمكن ذلك، وذلك من خلال إنشاء ArrayList من عناصر ContentProviderOperation واستدعاء applyBatch(). بما أنّ Contacts Provider ينفّذ جميع العمليات في 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 الجديدة. نتيجةً لذلك، يتم تلقائيًا ربط كل صف بيانات بصف ContactsContract.RawContacts الجديد الذي ينتمي إليه من خلال RAW_CONTACT_ID.

يتم وضع علامة 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);
    }
}

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

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

لاستخدام ميزة &quot;التحكّم المتزامن المتفائل&quot; أثناء تعديل صف واحد، اتّبِع الخطوات التالية: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
    }

استرداد البيانات وتعديلها باستخدام النوايا

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

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

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

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

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

الجدول 4. أهداف مقدّم جهات الاتصال

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

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

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

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

سلامة البيانات

بما أنّ مستودع جهات الاتصال يحتوي على بيانات مهمة وحسّاسة يتوقّع المستخدمون أن تكون صحيحة وحديثة، يفرض &quot;مزوّد جهات الاتصال&quot; قواعد محددة جيدًا لضمان سلامة البيانات. وتقع على عاتقك مسؤولية الالتزام بهذه القواعد عند تعديل بيانات جهات الاتصال. في ما يلي القواعد المهمة:

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

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

استخدام جداول "خلاصة الوسائط الاجتماعية"

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

  • تتطلّب هذه الجداول أذونات وصول إضافية. ولقراءة البيانات من هذه المصادر، يجب أن يحصل تطبيقك على الإذن android.Manifest.permission#READ_SOCIAL_STREAM. لتعديلها، يجب أن يحصل تطبيقك على الإذن android.Manifest.permission#WRITE_SOCIAL_STREAM.
  • بالنسبة إلى جدول android.provider.ContactsContract.StreamItems، يكون عدد الصفوف المخزَّنة لكل جهة اتصال أولية محدودًا. عند الوصول إلى هذا الحد، يوفّر &quot;مقدّم جهات الاتصال&quot; مساحة لصفوف عناصر البث الجديدة من خلال حذف الصفوف التي تتضمّن أقدم android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP تلقائيًا. للحصول على الحد الأقصى، أرسِل طلب بحث إلى معرّف الموارد المنتظم الخاص بالمحتوى android.provider.ContactsContract.StreamItems#CONTENT_LIMIT_URI. يمكنك ترك جميع الوسيطات الأخرى غير معرّف الموارد المنتظم (URI) للمحتوى مضبوطًا على null. يعرض طلب البحث Cursor يحتوي على صف واحد، مع العمود الفردي android.provider.ContactsContract.StreamItems#MAX_ITEMS.

يحدّد الصف android.provider.ContactsContract.StreamItems.StreamItemPhotos جدولاً فرعيًا من android.provider.ContactsContract.StreamItemPhotos يحتوي على صفوف الصور لعنصر واحد من عناصر ساحة المشاركات.

التفاعلات مع خلاصة الوسائط الاجتماعية

توفّر بيانات خلاصة الشبكات الاجتماعية التي يديرها &quot;مزوّد جهات الاتصال&quot;، بالإضافة إلى تطبيق جهات الاتصال على الجهاز، طريقة فعّالة لربط نظام الشبكات الاجتماعية بجهات الاتصال الحالية. تتوفّر الميزات التالية:

  • من خلال مزامنة خدمة الشبكات الاجتماعية مع &quot;موفّر جهات الاتصال&quot; باستخدام أداة مزامنة، يمكنك استرداد النشاط الأخير لجهات اتصال المستخدم وتخزينه في جدولَي android.provider.ContactsContract.StreamItems وandroid.provider.ContactsContract.StreamItemPhotos لاستخدامه لاحقًا.
  • بالإضافة إلى المزامنة العادية، يمكنك تفعيل أداة المزامنة لاسترداد بيانات إضافية عندما يختار المستخدم جهة اتصال لعرضها. يتيح ذلك لمحوّل المزامنة استرداد صور عالية الدقة وأحدث عناصر البث لجهة الاتصال.
  • من خلال تسجيل إشعار في تطبيق جهات الاتصال على الجهاز و&quot;مزوّد جهات الاتصال&quot;، يمكنك تلقّي هدف عندما يتم عرض جهة اتصال، وعندئذٍ يمكنك تعديل حالة جهة الاتصال من خدمتك. قد يكون هذا الأسلوب أسرع ويستخدم معدل نقل بيانات أقل من إجراء مزامنة كاملة باستخدام أداة مزامنة.
  • يمكن للمستخدمين إضافة جهة اتصال إلى خدمة الشبكات الاجتماعية أثناء الاطّلاع على جهة الاتصال في تطبيق جهات الاتصال على الجهاز. يمكنك تفعيل هذه الميزة من خلال ميزة "دعوة جهة اتصال"، والتي يتم تفعيلها من خلال الجمع بين نشاط يضيف جهة اتصال حالية إلى شبكتك، وملف XML يوفّر لتطبيق جهات الاتصال على الجهاز و"مزوّد جهات الاتصال" تفاصيل تطبيقك.

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

تسجيل معالج طرق عرض الشبكات الاجتماعية

لتسجيل أداة المزامنة لتلقّي إشعارات عندما يعرض المستخدم جهة اتصال تتم إدارتها من خلال أداة المزامنة، اتّبِع الخطوات التالية:

  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 تتحكّم في تفاعل محوّل المزامنة والتطبيق مع تطبيق &quot;جهات الاتصال&quot; و&quot;موفّر جهات الاتصال&quot;. يتم وصف هذه العناصر في الأقسام التالية.

العنصر <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> في عرض صفوف البيانات المخصّصة لتطبيقك في واجهة مستخدم تطبيق &quot;جهات الاتصال&quot;. تتضمّن هذه الدالة بنية الجملة التالية:

<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.

ميزات إضافية لمقدّم جهات الاتصال

بالإضافة إلى الميزات الرئيسية الموضّحة في الأقسام السابقة، يوفّر &quot;مقدّم جهات الاتصال&quot; الميزات المفيدة التالية للتعامل مع بيانات جهات الاتصال:

  • مجموعات جهات الاتصال
  • ميزات الصور

مجموعات جهات الاتصال

يمكن لموفّر جهات الاتصال اختياريًا تصنيف مجموعات جهات الاتصال ذات الصلة باستخدام بيانات المجموعة. إذا كان الخادم المرتبط بحساب مستخدم يريد الاحتفاظ بالمجموعات، يجب أن ينقل محوّل المزامنة لنوع حساب الحساب بيانات المجموعات بين &quot;مزوّد جهات الاتصال&quot; والخادم. عندما يضيف المستخدمون جهة اتصال جديدة إلى الخادم ثم يضعون جهة الاتصال هذه في مجموعة جديدة، يجب أن يضيف محوّل المزامنة المجموعة الجديدة إلى جدول ContactsContract.Groups. يتم تخزين المجموعة أو المجموعات التي تنتمي إليها جهة اتصال أولية في جدول ContactsContract.Data باستخدام نوع MIME ContactsContract.CommonDataKinds.GroupMembership.

إذا كنت تصمّم أداة مزامنة ستضيف بيانات جهات الاتصال الأولية من الخادم إلى &quot;مزوّد جهات الاتصال&quot;، ولم تكن تستخدم المجموعات، عليك إخبار &quot;المزوّد&quot; بجعل بياناتك مرئية. في الرمز البرمجي الذي يتم تنفيذه عندما يضيف مستخدم حسابًا إلى الجهاز، عدِّل الصف ContactsContract.Settings الذي يضيفه "مقدّم خدمة جهات الاتصال" للحساب. في هذا الصف، اضبط قيمة العمود Settings.UNGROUPED_VISIBLE على 1. عند إجراء ذلك، سيجعل &quot;موفّر جهات الاتصال&quot; بيانات جهات الاتصال مرئية دائمًا، حتى إذا لم تستخدم المجموعات.

صور جهات الاتصال

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